/src/wireshark/wsutil/file_compressed.c
Line | Count | Source |
1 | | /* file_compressed.c |
2 | | * Code for writing compressed files. |
3 | | * |
4 | | * Wireshark - Network traffic analyzer |
5 | | * By Gerald Combs <gerald@wireshark.org> |
6 | | * Copyright 1998 Gerald Combs |
7 | | * |
8 | | * Derived from code in the Wiretap Library |
9 | | * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu> |
10 | | * |
11 | | * SPDX-License-Identifier: GPL-2.0-or-later |
12 | | */ |
13 | | |
14 | | #include <config.h> |
15 | | |
16 | | #include <wireshark.h> |
17 | | |
18 | | #include <errno.h> |
19 | | |
20 | | #include <wsutil/file_util.h> |
21 | | #include <wsutil/zlib_compat.h> |
22 | | |
23 | | #ifdef HAVE_LZ4FRAME_H |
24 | | #include <lz4frame.h> |
25 | | #endif /* HAVE_LZ4FRAME_H */ |
26 | | |
27 | | #include "file_compressed.h" |
28 | | |
29 | | /* |
30 | | * List of compression types supported. |
31 | | * This includes compression types that can only be read, not written. |
32 | | */ |
33 | | static const struct compression_type { |
34 | | ws_compression_type type; |
35 | | const char *extension; |
36 | | const char *description; |
37 | | const char *name; |
38 | | const bool can_write_compressed; |
39 | | } compression_types[] = { |
40 | | #ifdef USE_ZLIB_OR_ZLIBNG |
41 | | { WS_FILE_GZIP_COMPRESSED, "gz", "gzip compressed", "gzip", true }, |
42 | | #endif /* USE_ZLIB_OR_ZLIBNG */ |
43 | | #ifdef HAVE_ZSTD |
44 | | { WS_FILE_ZSTD_COMPRESSED, "zst", "zstd compressed", "zstd", false }, |
45 | | #endif /* HAVE_ZSTD */ |
46 | | #ifdef HAVE_LZ4FRAME_H |
47 | | { WS_FILE_LZ4_COMPRESSED, "lz4", "lz4 compressed", "lz4", true }, |
48 | | #endif /* HAVE_LZ4FRAME_H */ |
49 | | { WS_FILE_UNCOMPRESSED, NULL, NULL, "none", true }, |
50 | | { WS_FILE_UNKNOWN_COMPRESSION, NULL, NULL, NULL, false }, |
51 | | }; |
52 | | |
53 | | ws_compression_type |
54 | | ws_name_to_compression_type(const char *name) |
55 | 0 | { |
56 | 0 | for (const struct compression_type *p = compression_types; |
57 | 0 | p->type != WS_FILE_UNKNOWN_COMPRESSION; p++) { |
58 | 0 | if (!g_strcmp0(name, p->name)) |
59 | 0 | return p->type; |
60 | 0 | } |
61 | 0 | return WS_FILE_UNKNOWN_COMPRESSION; |
62 | 0 | } |
63 | | |
64 | | ws_compression_type |
65 | | ws_extension_to_compression_type(const char *ext) |
66 | 0 | { |
67 | 0 | for (const struct compression_type *p = compression_types; |
68 | 0 | p->type != WS_FILE_UNKNOWN_COMPRESSION; p++) { |
69 | 0 | if (g_strcmp0(ext, p->extension) == 0) |
70 | 0 | return p->type; |
71 | 0 | } |
72 | 0 | return WS_FILE_UNKNOWN_COMPRESSION; |
73 | 0 | } |
74 | | |
75 | | bool |
76 | | ws_can_write_compression_type(ws_compression_type compression_type) |
77 | 0 | { |
78 | 0 | for (const struct compression_type *p = compression_types; |
79 | 0 | p->type != WS_FILE_UNKNOWN_COMPRESSION; p++) { |
80 | 0 | if (compression_type == p->type) |
81 | 0 | return p->can_write_compressed; |
82 | 0 | } |
83 | | |
84 | 0 | return false; |
85 | 0 | } |
86 | | |
87 | | const char * |
88 | | ws_compression_type_description(ws_compression_type compression_type) |
89 | 0 | { |
90 | 0 | for (const struct compression_type *p = compression_types; |
91 | 0 | p->type != WS_FILE_UNCOMPRESSED; p++) { |
92 | 0 | if (p->type == compression_type) |
93 | 0 | return p->description; |
94 | 0 | } |
95 | 0 | return NULL; |
96 | 0 | } |
97 | | |
98 | | const char * |
99 | | ws_compression_type_extension(ws_compression_type compression_type) |
100 | 0 | { |
101 | 0 | for (const struct compression_type *p = compression_types; |
102 | 0 | p->type != WS_FILE_UNCOMPRESSED; p++) { |
103 | 0 | if (p->type == compression_type) |
104 | 0 | return p->extension; |
105 | 0 | } |
106 | 0 | return NULL; |
107 | 0 | } |
108 | | |
109 | | const char * |
110 | | ws_compression_type_name(ws_compression_type compression_type) |
111 | 0 | { |
112 | 0 | for (const struct compression_type *p = compression_types; |
113 | 0 | p->type != WS_FILE_UNCOMPRESSED; p++) { |
114 | 0 | if (p->type == compression_type) |
115 | 0 | return p->name; |
116 | 0 | } |
117 | 0 | return NULL; |
118 | 0 | } |
119 | | |
120 | | GSList * |
121 | | ws_get_all_compression_type_extensions_list(void) |
122 | 0 | { |
123 | 0 | GSList *extensions; |
124 | |
|
125 | 0 | extensions = NULL; /* empty list, to start with */ |
126 | |
|
127 | 0 | for (const struct compression_type *p = compression_types; |
128 | 0 | p->type != WS_FILE_UNCOMPRESSED; p++) |
129 | 0 | extensions = g_slist_prepend(extensions, (void *)p->extension); |
130 | |
|
131 | 0 | return extensions; |
132 | 0 | } |
133 | | |
134 | | GSList * |
135 | | ws_get_all_output_compression_type_names_list(void) |
136 | 0 | { |
137 | 0 | GSList *names; |
138 | |
|
139 | 0 | names = NULL; /* empty list, to start with */ |
140 | |
|
141 | 0 | for (const struct compression_type *p = compression_types; |
142 | 0 | p->type != WS_FILE_UNCOMPRESSED; p++) { |
143 | 0 | if (p->can_write_compressed) |
144 | 0 | names = g_slist_prepend(names, (void *)p->name); |
145 | 0 | } |
146 | |
|
147 | 0 | return names; |
148 | 0 | } |
149 | | |
150 | | typedef void* WFILE_T; |
151 | | |
152 | | struct ws_cwstream { |
153 | | WFILE_T fh; |
154 | | char* io_buffer; |
155 | | ws_compression_type ctype; |
156 | | }; |
157 | | |
158 | | static WFILE_T |
159 | | writecap_file_open(ws_cwstream* pfile, const char *filename) |
160 | 0 | { |
161 | 0 | WFILE_T fh; |
162 | 0 | switch (pfile->ctype) { |
163 | 0 | #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) |
164 | 0 | case WS_FILE_GZIP_COMPRESSED: |
165 | 0 | return gzwfile_open(filename); |
166 | 0 | #endif /* defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) */ |
167 | | #ifdef HAVE_LZ4FRAME_H |
168 | | case WS_FILE_LZ4_COMPRESSED: |
169 | | return lz4wfile_open(filename); |
170 | | #endif /* HAVE_LZ4FRAME_H */ |
171 | 0 | default: |
172 | 0 | fh = ws_fopen(filename, "wb"); |
173 | | /* Increase the size of the IO buffer if uncompressed. |
174 | | * Compression has its own buffer that reduces writes. |
175 | | */ |
176 | 0 | if (fh != NULL) { |
177 | 0 | size_t buffsize = IO_BUF_SIZE; |
178 | 0 | #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE |
179 | 0 | ws_statb64 statb; |
180 | |
|
181 | 0 | if (ws_stat64(filename, &statb) == 0) { |
182 | 0 | if (statb.st_blksize > IO_BUF_SIZE) { |
183 | 0 | buffsize = (size_t)statb.st_blksize; |
184 | 0 | } |
185 | 0 | } |
186 | 0 | #endif |
187 | 0 | pfile->io_buffer = (char *)g_malloc(buffsize); |
188 | 0 | setvbuf(fh, pfile->io_buffer, _IOFBF, buffsize); |
189 | | //ws_debug("buffsize %zu", buffsize); |
190 | 0 | } |
191 | 0 | return fh; |
192 | 0 | } |
193 | 0 | } |
194 | | |
195 | | static WFILE_T |
196 | | writecap_file_fdopen(ws_cwstream* pfile, int fd) |
197 | 0 | { |
198 | 0 | WFILE_T fh; |
199 | 0 | switch (pfile->ctype) { |
200 | 0 | #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) |
201 | 0 | case WS_FILE_GZIP_COMPRESSED: |
202 | 0 | return gzwfile_fdopen(fd); |
203 | 0 | #endif /* defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) */ |
204 | | #ifdef HAVE_LZ4FRAME_H |
205 | | case WS_FILE_LZ4_COMPRESSED: |
206 | | return lz4wfile_fdopen(fd); |
207 | | #endif /* HAVE_LZ4FRAME_H */ |
208 | 0 | default: |
209 | 0 | fh = ws_fdopen(fd, "wb"); |
210 | | /* Increase the size of the IO buffer if uncompressed. |
211 | | * Compression has its own buffer that reduces writes. |
212 | | */ |
213 | 0 | if (fh != NULL) { |
214 | 0 | size_t buffsize = IO_BUF_SIZE; |
215 | 0 | #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE |
216 | 0 | ws_statb64 statb; |
217 | |
|
218 | 0 | if (ws_fstat64(fd, &statb) == 0) { |
219 | 0 | if (statb.st_blksize > IO_BUF_SIZE) { |
220 | 0 | buffsize = (size_t)statb.st_blksize; |
221 | 0 | } |
222 | 0 | } |
223 | 0 | #endif |
224 | 0 | pfile->io_buffer = (char *)g_malloc(buffsize); |
225 | 0 | setvbuf(fh, pfile->io_buffer, _IOFBF, buffsize); |
226 | | //ws_debug("buffsize %zu", buffsize); |
227 | 0 | } |
228 | 0 | return fh; |
229 | 0 | } |
230 | 0 | } |
231 | | |
232 | | ws_cwstream* |
233 | | ws_cwstream_open(const char *filename, ws_compression_type ctype, int *err) |
234 | 0 | { |
235 | 0 | ws_cwstream* pfile; |
236 | 0 | *err = 0; |
237 | |
|
238 | 0 | pfile = g_new0(struct ws_cwstream, 1); |
239 | 0 | if (pfile == NULL) { |
240 | 0 | *err = errno; |
241 | 0 | return NULL; |
242 | 0 | } |
243 | 0 | pfile->ctype = ctype; |
244 | | /* XXX - Callers call g_strerror on failure, so should "standard" errors be used? */ |
245 | 0 | errno = FILE_ERR_CANT_OPEN; |
246 | 0 | void* fh = writecap_file_open(pfile, filename); |
247 | 0 | if (fh == NULL) { |
248 | 0 | *err = errno; |
249 | 0 | g_free(pfile); |
250 | 0 | return NULL; |
251 | 0 | } |
252 | | |
253 | 0 | pfile->fh = fh; |
254 | 0 | return pfile; |
255 | 0 | } |
256 | | |
257 | | ws_cwstream* |
258 | | ws_cwstream_fdopen(int fd, ws_compression_type ctype, int *err) |
259 | 0 | { |
260 | 0 | ws_cwstream* pfile; |
261 | 0 | *err = 0; |
262 | |
|
263 | 0 | pfile = g_new0(struct ws_cwstream, 1); |
264 | 0 | if (pfile == NULL) { |
265 | 0 | *err = errno; |
266 | 0 | return NULL; |
267 | 0 | } |
268 | 0 | pfile->ctype = ctype; |
269 | | /* XXX - Callers call g_strerror on failure, so should "standard" errors be used? */ |
270 | 0 | errno = FILE_ERR_CANT_OPEN; |
271 | 0 | WFILE_T fh = writecap_file_fdopen(pfile, fd); |
272 | 0 | if (fh == NULL) { |
273 | 0 | *err = errno; |
274 | 0 | g_free(pfile); |
275 | 0 | return NULL; |
276 | 0 | } |
277 | | |
278 | 0 | pfile->fh = fh; |
279 | 0 | return pfile; |
280 | 0 | } |
281 | | |
282 | | ws_cwstream* |
283 | | ws_cwstream_open_stdout(ws_compression_type ctype, int *err) |
284 | 0 | { |
285 | 0 | int new_fd; |
286 | 0 | ws_cwstream* pfile; |
287 | |
|
288 | 0 | new_fd = ws_dup(1); |
289 | 0 | if (new_fd == -1) { |
290 | 0 | *err = errno; |
291 | 0 | return NULL; |
292 | 0 | } |
293 | | #ifdef _WIN32 |
294 | | /* |
295 | | * Put the new descriptor into binary mode. |
296 | | * |
297 | | * XXX - even if the file format we're writing is a text |
298 | | * format? |
299 | | */ |
300 | | if (_setmode(new_fd, O_BINARY) == -1) { |
301 | | /* "Should not happen" */ |
302 | | *err = errno; |
303 | | ws_close(new_fd); |
304 | | return NULL; |
305 | | } |
306 | | #endif |
307 | | |
308 | 0 | pfile = ws_cwstream_fdopen(new_fd, ctype, err); |
309 | 0 | if (pfile == NULL) { |
310 | | /* Failed; close the new fd */ |
311 | 0 | ws_close(new_fd); |
312 | 0 | return NULL; |
313 | 0 | } |
314 | 0 | return pfile; |
315 | 0 | } |
316 | | |
317 | | /* Write to file */ |
318 | | bool |
319 | | ws_cwstream_write(ws_cwstream* pfile, const uint8_t* data, size_t data_length, |
320 | | uint64_t *bytes_written, int *err) |
321 | 0 | { |
322 | 0 | size_t nwritten; |
323 | |
|
324 | 0 | switch (pfile->ctype) { |
325 | 0 | #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) |
326 | 0 | case WS_FILE_GZIP_COMPRESSED: |
327 | 0 | nwritten = gzwfile_write(pfile->fh, data, (unsigned)data_length); |
328 | | /* |
329 | | * gzwfile_write() returns 0 on error. |
330 | | */ |
331 | 0 | if (nwritten == 0) { |
332 | 0 | *err = gzwfile_geterr(pfile->fh); |
333 | 0 | return false; |
334 | 0 | } |
335 | 0 | break; |
336 | 0 | #endif |
337 | | #ifdef HAVE_LZ4FRAME_H |
338 | | case WS_FILE_LZ4_COMPRESSED: |
339 | | nwritten = lz4wfile_write(pfile->fh, data, data_length); |
340 | | /* |
341 | | * lz4wfile_write() returns 0 on error. |
342 | | */ |
343 | | if (nwritten == 0) { |
344 | | *err = lz4wfile_geterr(pfile->fh); |
345 | | return false; |
346 | | } |
347 | | break; |
348 | | #endif /* HAVE_LZ4FRAME_H */ |
349 | 0 | default: |
350 | 0 | nwritten = fwrite(data, data_length, 1, pfile->fh); |
351 | 0 | if (nwritten != 1) { |
352 | 0 | if (ferror((FILE *)pfile->fh)) { |
353 | 0 | *err = errno; |
354 | 0 | } else { |
355 | 0 | *err = FILE_ERR_SHORT_WRITE; |
356 | 0 | } |
357 | 0 | return false; |
358 | 0 | } |
359 | 0 | break; |
360 | 0 | } |
361 | | |
362 | 0 | (*bytes_written) += data_length; |
363 | 0 | return true; |
364 | 0 | } |
365 | | |
366 | | bool |
367 | | ws_cwstream_flush(ws_cwstream* pfile, int *err) |
368 | 0 | { |
369 | 0 | switch (pfile->ctype) { |
370 | 0 | #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) |
371 | 0 | case WS_FILE_GZIP_COMPRESSED: |
372 | 0 | if (gzwfile_flush((GZWFILE_T)pfile->fh) == -1) { |
373 | 0 | if (err) { |
374 | 0 | *err = gzwfile_geterr((GZWFILE_T)pfile->fh); |
375 | 0 | } |
376 | 0 | return false; |
377 | 0 | } |
378 | 0 | break; |
379 | 0 | #endif |
380 | | #ifdef HAVE_LZ4FRAME_H |
381 | | case WS_FILE_LZ4_COMPRESSED: |
382 | | if (lz4wfile_flush((LZ4WFILE_T)pfile->fh) == -1) { |
383 | | if (err) { |
384 | | *err = lz4wfile_geterr((LZ4WFILE_T)pfile->fh); |
385 | | } |
386 | | return false; |
387 | | } |
388 | | break; |
389 | | #endif /* HAVE_LZ4FRAME_H */ |
390 | 0 | default: |
391 | 0 | if (fflush((FILE*)pfile->fh) == EOF) { |
392 | 0 | if (err) { |
393 | 0 | *err = errno; |
394 | 0 | } |
395 | 0 | return false; |
396 | 0 | } |
397 | 0 | } |
398 | 0 | return true; |
399 | 0 | } |
400 | | |
401 | | bool |
402 | | ws_cwstream_close(ws_cwstream* pfile, int *errp) |
403 | 0 | { |
404 | 0 | int err = 0; |
405 | |
|
406 | 0 | errno = FILE_ERR_CANT_CLOSE; |
407 | 0 | switch (pfile->ctype) { |
408 | 0 | #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) |
409 | 0 | case WS_FILE_GZIP_COMPRESSED: |
410 | 0 | err = gzwfile_close(pfile->fh); |
411 | 0 | break; |
412 | 0 | #endif |
413 | | #ifdef HAVE_LZ4FRAME_H |
414 | | case WS_FILE_LZ4_COMPRESSED: |
415 | | err = lz4wfile_close(pfile->fh); |
416 | | break; |
417 | | #endif /* HAVE_LZ4FRAME_H */ |
418 | 0 | default: |
419 | 0 | if (fclose(pfile->fh) == EOF) { |
420 | 0 | err = errno; |
421 | 0 | } |
422 | 0 | } |
423 | | |
424 | 0 | g_free(pfile->io_buffer); |
425 | 0 | g_free(pfile); |
426 | 0 | if (errp) { |
427 | 0 | *errp = err; |
428 | 0 | } |
429 | 0 | return err == 0; |
430 | 0 | } |
431 | | |
432 | | #ifdef USE_ZLIB_OR_ZLIBNG |
433 | | |
434 | 0 | #define GZBUFSIZE 4096 |
435 | | |
436 | | /* internal gzip file state data structure for writing */ |
437 | | struct gzip_writer { |
438 | | int fd; /* file descriptor */ |
439 | | int64_t pos; /* current position in uncompressed data */ |
440 | | unsigned size; /* buffer size, zero if not allocated yet */ |
441 | | unsigned want; /* requested buffer size, default is GZBUFSIZE */ |
442 | | unsigned char *in; /* input buffer */ |
443 | | unsigned char *out; /* output buffer (double-sized when reading) */ |
444 | | unsigned char *next; /* next output data to deliver or write */ |
445 | | int level; /* compression level */ |
446 | | int strategy; /* compression strategy */ |
447 | | int err; /* error code */ |
448 | | const char *err_info; /* additional error information string for some errors */ |
449 | | /* zlib deflate stream */ |
450 | | zlib_stream strm; /* stream structure in-place (not a pointer) */ |
451 | | }; |
452 | | |
453 | | GZWFILE_T |
454 | | gzwfile_open(const char *path) |
455 | 0 | { |
456 | 0 | int fd; |
457 | 0 | GZWFILE_T state; |
458 | 0 | int save_errno; |
459 | |
|
460 | 0 | fd = ws_open(path, O_BINARY|O_WRONLY|O_CREAT|O_TRUNC, 0666); |
461 | 0 | if (fd == -1) |
462 | 0 | return NULL; |
463 | 0 | state = gzwfile_fdopen(fd); |
464 | 0 | if (state == NULL) { |
465 | 0 | save_errno = errno; |
466 | 0 | ws_close(fd); |
467 | 0 | errno = save_errno; |
468 | 0 | } |
469 | 0 | return state; |
470 | 0 | } |
471 | | |
472 | | GZWFILE_T |
473 | | gzwfile_fdopen(int fd) |
474 | 0 | { |
475 | 0 | GZWFILE_T state; |
476 | | |
477 | | /* allocate zlib_writer structure to return */ |
478 | 0 | state = (GZWFILE_T)g_try_malloc(sizeof *state); |
479 | 0 | if (state == NULL) |
480 | 0 | return NULL; |
481 | 0 | state->fd = fd; |
482 | 0 | state->size = 0; /* no buffers allocated yet */ |
483 | 0 | state->want = GZBUFSIZE; /* requested buffer size */ |
484 | |
|
485 | 0 | state->level = Z_DEFAULT_COMPRESSION; |
486 | 0 | state->strategy = Z_DEFAULT_STRATEGY; |
487 | | |
488 | | /* initialize stream */ |
489 | 0 | state->err = Z_OK; /* clear error */ |
490 | 0 | state->err_info = NULL; /* clear additional error information */ |
491 | 0 | state->pos = 0; /* no uncompressed data yet */ |
492 | 0 | state->strm.avail_in = 0; /* no input data yet */ |
493 | | |
494 | | /* return stream */ |
495 | 0 | return state; |
496 | 0 | } |
497 | | |
498 | | /* Initialize state for writing a gzip file. Mark initialization by setting |
499 | | state->size to non-zero. Return -1, and set state->err and possibly |
500 | | state->err_info, on failure; return 0 on success. */ |
501 | | static int |
502 | | gz_init(GZWFILE_T state) |
503 | 0 | { |
504 | 0 | int ret; |
505 | 0 | zlib_streamp strm = &(state->strm); |
506 | | |
507 | | /* allocate input and output buffers */ |
508 | 0 | state->in = (unsigned char *)g_try_malloc(state->want); |
509 | 0 | state->out = (unsigned char *)g_try_malloc(state->want); |
510 | 0 | if (state->in == NULL || state->out == NULL) { |
511 | 0 | g_free(state->out); |
512 | 0 | g_free(state->in); |
513 | 0 | state->err = ENOMEM; |
514 | 0 | return -1; |
515 | 0 | } |
516 | | |
517 | | /* allocate deflate memory, set up for gzip compression */ |
518 | 0 | strm->zalloc = Z_NULL; |
519 | 0 | strm->zfree = Z_NULL; |
520 | 0 | strm->opaque = Z_NULL; |
521 | 0 | ret = ZLIB_PREFIX(deflateInit2)(strm, state->level, Z_DEFLATED, |
522 | 0 | 15 + 16, 8, state->strategy); |
523 | 0 | if (ret != Z_OK) { |
524 | 0 | g_free(state->out); |
525 | 0 | g_free(state->in); |
526 | 0 | if (ret == Z_MEM_ERROR) { |
527 | | /* This means "not enough memory". */ |
528 | 0 | state->err = ENOMEM; |
529 | 0 | } else { |
530 | | /* This "shouldn't happen". */ |
531 | 0 | state->err = FILE_ERR_INTERNAL; |
532 | 0 | state->err_info = "Unknown error from deflateInit2()"; |
533 | 0 | } |
534 | 0 | return -1; |
535 | 0 | } |
536 | | |
537 | | /* mark state as initialized */ |
538 | 0 | state->size = state->want; |
539 | | |
540 | | /* initialize write buffer */ |
541 | 0 | strm->avail_out = state->size; |
542 | 0 | strm->next_out = state->out; |
543 | 0 | state->next = strm->next_out; |
544 | 0 | return 0; |
545 | 0 | } |
546 | | |
547 | | /* Compress whatever is at avail_in and next_in and write to the output file. |
548 | | Return -1, and set state->err and possibly state->err_info, if there is |
549 | | an error writing to the output file; return 0 on success. |
550 | | flush is assumed to be a valid deflate() flush value. If flush is Z_FINISH, |
551 | | then the deflate() state is reset to start a new gzip stream. */ |
552 | | static int |
553 | | gz_comp(GZWFILE_T state, int flush) |
554 | 0 | { |
555 | 0 | int ret; |
556 | 0 | ssize_t got; |
557 | 0 | ptrdiff_t have; |
558 | 0 | zlib_streamp strm = &(state->strm); |
559 | | /* allocate memory if this is the first time through */ |
560 | 0 | if (state->size == 0 && gz_init(state) == -1) |
561 | 0 | return -1; |
562 | | |
563 | | /* run deflate() on provided input until it produces no more output */ |
564 | 0 | ret = Z_OK; |
565 | 0 | do { |
566 | | /* write out current buffer contents if full, or if flushing, but if |
567 | | doing Z_FINISH then don't write until we get to Z_STREAM_END */ |
568 | 0 | if (strm->avail_out == 0 || (flush != Z_NO_FLUSH && |
569 | 0 | (flush != Z_FINISH || ret == Z_STREAM_END))) { |
570 | 0 | have = strm->next_out - state->next; |
571 | 0 | if (have) { |
572 | 0 | got = ws_write(state->fd, state->next, (unsigned int)have); |
573 | 0 | if (got < 0) { |
574 | 0 | state->err = errno; |
575 | 0 | return -1; |
576 | 0 | } |
577 | 0 | if ((ptrdiff_t)got != have) { |
578 | 0 | state->err = FILE_ERR_SHORT_WRITE; |
579 | 0 | return -1; |
580 | 0 | } |
581 | 0 | } |
582 | 0 | if (strm->avail_out == 0) { |
583 | 0 | strm->avail_out = state->size; |
584 | 0 | strm->next_out = state->out; |
585 | 0 | } |
586 | 0 | state->next = strm->next_out; |
587 | 0 | } |
588 | | |
589 | | /* compress */ |
590 | 0 | have = strm->avail_out; |
591 | 0 | ret = ZLIB_PREFIX(deflate)(strm, flush); |
592 | 0 | if (ret == Z_STREAM_ERROR) { |
593 | | /* This "shouldn't happen". */ |
594 | 0 | state->err = FILE_ERR_INTERNAL; |
595 | 0 | state->err_info = "Z_STREAM_ERROR from deflate()"; |
596 | 0 | return -1; |
597 | 0 | } |
598 | 0 | have -= strm->avail_out; |
599 | 0 | } while (have); |
600 | | |
601 | | /* if that completed a deflate stream, allow another to start */ |
602 | 0 | if (flush == Z_FINISH) |
603 | 0 | ZLIB_PREFIX(deflateReset)(strm); |
604 | | |
605 | | /* all done, no errors */ |
606 | 0 | return 0; |
607 | 0 | } |
608 | | |
609 | | /* Write out len bytes from buf. Return 0, and set state->err, on |
610 | | failure or on an attempt to write 0 bytes (in which case state->err |
611 | | is Z_OK); return the number of bytes written on success. */ |
612 | | unsigned |
613 | | gzwfile_write(GZWFILE_T state, const void *buf, unsigned len) |
614 | 0 | { |
615 | 0 | unsigned put = len; |
616 | 0 | unsigned n; |
617 | 0 | zlib_streamp strm; |
618 | |
|
619 | 0 | strm = &(state->strm); |
620 | | |
621 | | /* check that there's no error */ |
622 | 0 | if (state->err != Z_OK) |
623 | 0 | return 0; |
624 | | |
625 | | /* if len is zero, avoid unnecessary operations */ |
626 | 0 | if (len == 0) |
627 | 0 | return 0; |
628 | | |
629 | | /* allocate memory if this is the first time through */ |
630 | 0 | if (state->size == 0 && gz_init(state) == -1) |
631 | 0 | return 0; |
632 | | |
633 | | /* for small len, copy to input buffer, otherwise compress directly */ |
634 | 0 | if (len < state->size) { |
635 | | /* copy to input buffer, compress when full */ |
636 | 0 | do { |
637 | 0 | if (strm->avail_in == 0) |
638 | 0 | strm->next_in = state->in; |
639 | 0 | n = state->size - strm->avail_in; |
640 | 0 | if (n > len) |
641 | 0 | n = len; |
642 | 0 | #ifdef z_const |
643 | 0 | DIAG_OFF(cast-qual) |
644 | 0 | memcpy((Bytef *)strm->next_in + strm->avail_in, buf, n); |
645 | 0 | DIAG_ON(cast-qual) |
646 | | #else /* z_const */ |
647 | | memcpy(strm->next_in + strm->avail_in, buf, n); |
648 | | #endif /* z_const */ |
649 | 0 | strm->avail_in += n; |
650 | 0 | state->pos += n; |
651 | 0 | buf = (const char *)buf + n; |
652 | 0 | len -= n; |
653 | 0 | if (len && gz_comp(state, Z_NO_FLUSH) == -1) |
654 | 0 | return 0; |
655 | 0 | } while (len); |
656 | 0 | } |
657 | 0 | else { |
658 | | /* consume whatever's left in the input buffer */ |
659 | 0 | if (strm->avail_in != 0 && gz_comp(state, Z_NO_FLUSH) == -1) |
660 | 0 | return 0; |
661 | | |
662 | | /* directly compress user buffer to file */ |
663 | 0 | strm->avail_in = len; |
664 | 0 | #ifdef z_const |
665 | 0 | strm->next_in = (z_const Bytef *)buf; |
666 | | #else /* z_const */ |
667 | | DIAG_OFF(cast-qual) |
668 | | strm->next_in = (Bytef *)buf; |
669 | | DIAG_ON(cast-qual) |
670 | | #endif /* z_const */ |
671 | 0 | state->pos += len; |
672 | 0 | if (gz_comp(state, Z_NO_FLUSH) == -1) |
673 | 0 | return 0; |
674 | 0 | } |
675 | | |
676 | | /* input was all buffered or compressed */ |
677 | 0 | return put; |
678 | 0 | } |
679 | | |
680 | | /* Flush out what we've written so far. Returns -1, and sets state->err, |
681 | | on failure; returns 0 on success. */ |
682 | | int |
683 | | gzwfile_flush(GZWFILE_T state) |
684 | 0 | { |
685 | | /* check that there's no error */ |
686 | 0 | if (state->err != Z_OK) |
687 | 0 | return -1; |
688 | | |
689 | | /* compress remaining data with Z_SYNC_FLUSH */ |
690 | 0 | gz_comp(state, Z_SYNC_FLUSH); |
691 | 0 | if (state->err != Z_OK) |
692 | 0 | return -1; |
693 | 0 | return 0; |
694 | 0 | } |
695 | | |
696 | | /* Flush out all data written, and close the file. Returns a Wiretap |
697 | | error on failure; returns 0 on success. */ |
698 | | int |
699 | | gzwfile_close(GZWFILE_T state) |
700 | 0 | { |
701 | 0 | int ret = 0; |
702 | | |
703 | | /* flush, free memory, and close file */ |
704 | 0 | if (gz_comp(state, Z_FINISH) == -1) |
705 | 0 | ret = state->err; |
706 | 0 | (void)ZLIB_PREFIX(deflateEnd)(&(state->strm)); |
707 | 0 | g_free(state->out); |
708 | 0 | g_free(state->in); |
709 | 0 | state->err = Z_OK; |
710 | 0 | if (ws_close(state->fd) == -1 && ret == 0) |
711 | 0 | ret = errno; |
712 | 0 | g_free(state); |
713 | 0 | return ret; |
714 | 0 | } |
715 | | |
716 | | int |
717 | | gzwfile_geterr(GZWFILE_T state) |
718 | 0 | { |
719 | 0 | return state->err; |
720 | 0 | } |
721 | | #endif /* USE_ZLIB_OR_ZLIBNG */ |
722 | | |
723 | | #ifdef HAVE_LZ4FRAME_H |
724 | | |
725 | | #define LZ4BUFSIZE 4194304 // 4MiB, maximum block size |
726 | | |
727 | | /* internal lz4 file state data structure for writing */ |
728 | | struct lz4_writer { |
729 | | int fd; /* file descriptor */ |
730 | | int64_t pos; /* current position in uncompressed data */ |
731 | | int64_t pos_out; |
732 | | size_t size_out; /* buffer size, zero if not allocated yet */ |
733 | | size_t want; /* requested buffer size, default is LZ4BUFSIZE */ |
734 | | size_t want_out; /* requested output buffer size, determined from want */ |
735 | | unsigned char *out; /* output buffer, containing uncompressed data */ |
736 | | int err; /* error code */ |
737 | | const char *err_info; /* additional error information string for some errors */ |
738 | | LZ4F_preferences_t lz4_prefs; |
739 | | LZ4F_cctx *lz4_cctx; |
740 | | }; |
741 | | |
742 | | LZ4WFILE_T |
743 | | lz4wfile_open(const char *path) |
744 | | { |
745 | | int fd; |
746 | | LZ4WFILE_T state; |
747 | | int save_errno; |
748 | | |
749 | | fd = ws_open(path, O_BINARY|O_WRONLY|O_CREAT|O_TRUNC, 0666); |
750 | | if (fd == -1) |
751 | | return NULL; |
752 | | state = lz4wfile_fdopen(fd); |
753 | | if (state == NULL) { |
754 | | save_errno = errno; |
755 | | ws_close(fd); |
756 | | errno = save_errno; |
757 | | } |
758 | | return state; |
759 | | } |
760 | | |
761 | | LZ4WFILE_T |
762 | | lz4wfile_fdopen(int fd) |
763 | | { |
764 | | LZ4WFILE_T state; |
765 | | |
766 | | /* allocate lz4_writer structure to return */ |
767 | | state = (LZ4WFILE_T)g_try_malloc(sizeof *state); |
768 | | if (state == NULL) |
769 | | return NULL; |
770 | | state->fd = fd; |
771 | | state->size_out = 0; /* no buffer allocated yet */ |
772 | | state->want = LZ4BUFSIZE; /* max input size (a block) */ |
773 | | |
774 | | memset(&state->lz4_prefs, 0, sizeof(LZ4F_preferences_t)); |
775 | | /* Use the same prefs as the lz4 command line utility defaults. */ |
776 | | state->lz4_prefs.frameInfo.blockMode = LZ4F_blockIndependent; /* Allows fast seek */ |
777 | | /* We could use LZ4F_blockLinked but start a new frame every so often |
778 | | * in order to allow fast seek. (Or implement fast seek for linked |
779 | | * blocks via dictionary loading.) Linked blocks have better compression |
780 | | * when blocks are small, as happens when flushing during live capture. |
781 | | */ |
782 | | state->lz4_prefs.frameInfo.contentChecksumFlag = 1; |
783 | | state->lz4_prefs.frameInfo.blockSizeID = LZ4F_max4MB; |
784 | | /* XXX - What should we set state->lz4_prefs.compressionLevel to? |
785 | | * The command line utility uses 1, recommends 9 as another option, and |
786 | | * also there's 12 (max). |
787 | | * |
788 | | * We could provide an API call or perhaps two or three preset options. |
789 | | */ |
790 | | state->lz4_prefs.compressionLevel = 1; |
791 | | |
792 | | state->want_out = LZ4F_compressBound(state->want, &state->lz4_prefs); |
793 | | /* |
794 | | * This size guarantees that we will always have enough room to |
795 | | * write the result of LZ4F_compressUpdate (or Flush or End), |
796 | | * so long as the output buffer is empty (i.e., we immediately |
797 | | * write to the output file anything the compressor hands back |
798 | | * instead of buffering.) |
799 | | */ |
800 | | |
801 | | /* initialize stream */ |
802 | | state->err = 0; /* clear error */ |
803 | | state->err_info = NULL; /* clear additional error information */ |
804 | | state->pos = 0; /* no uncompressed data yet */ |
805 | | state->pos_out = 0; |
806 | | |
807 | | /* return stream */ |
808 | | return state; |
809 | | } |
810 | | |
811 | | /* Writes len bytes from the output buffer to the file. |
812 | | * Return true on success; returns false and sets state->err on failure. |
813 | | */ |
814 | | static bool |
815 | | lz4_write_out(LZ4WFILE_T state, size_t len) |
816 | | { |
817 | | if (len > 0) { |
818 | | ssize_t got = ws_write(state->fd, state->out, (unsigned)len); |
819 | | if (got < 0) { |
820 | | state->err = errno; |
821 | | return false; |
822 | | } |
823 | | if ((unsigned)got != len) { |
824 | | state->err = FILE_ERR_SHORT_WRITE; |
825 | | return false; |
826 | | } |
827 | | state->pos_out += got; |
828 | | } |
829 | | return true; |
830 | | } |
831 | | |
832 | | /* Initialize state for writing an lz4 file. Mark initialization by setting |
833 | | state->size to non-zero. Return -1, and set state->err and possibly |
834 | | state->err_info, on failure; return 0 on success. */ |
835 | | static int |
836 | | lz4_init(LZ4WFILE_T state) |
837 | | { |
838 | | LZ4F_errorCode_t ret; |
839 | | |
840 | | /* create Compression context */ |
841 | | ret = LZ4F_createCompressionContext(&state->lz4_cctx, LZ4F_VERSION); |
842 | | if (LZ4F_isError(ret)) { |
843 | | state->err = FILE_ERR_CANT_WRITE; // XXX - FILE_ERR_COMPRESS? |
844 | | state->err_info = LZ4F_getErrorName(ret); |
845 | | return -1; |
846 | | } |
847 | | |
848 | | /* allocate buffer */ |
849 | | state->out = (unsigned char *)g_try_malloc(state->want_out); |
850 | | if (state->out == NULL) { |
851 | | g_free(state->out); |
852 | | LZ4F_freeCompressionContext(state->lz4_cctx); |
853 | | state->err = ENOMEM; |
854 | | return -1; |
855 | | } |
856 | | |
857 | | ret = LZ4F_compressBegin(state->lz4_cctx, state->out, state->want_out, &state->lz4_prefs); |
858 | | if (LZ4F_isError(ret)) { |
859 | | state->err = FILE_ERR_CANT_WRITE; // XXX - FILE_ERR_COMPRESS? |
860 | | state->err_info = LZ4F_getErrorName(ret); |
861 | | return -1; |
862 | | } |
863 | | if (!lz4_write_out(state, ret)) { |
864 | | return -1; |
865 | | } |
866 | | |
867 | | /* mark state as initialized */ |
868 | | state->size_out = state->want_out; |
869 | | |
870 | | return 0; |
871 | | } |
872 | | |
873 | | /* Write out len bytes from buf. Return 0, and set state->err, on |
874 | | failure or on an attempt to write 0 bytes (in which case state->err |
875 | | is 0); return the number of bytes written on success. */ |
876 | | size_t |
877 | | lz4wfile_write(LZ4WFILE_T state, const void *buf, size_t len) |
878 | | { |
879 | | size_t to_write; |
880 | | size_t put = len; |
881 | | |
882 | | /* check that there's no error */ |
883 | | if (state->err != 0) |
884 | | return 0; |
885 | | |
886 | | /* if len is zero, avoid unnecessary operations */ |
887 | | if (len == 0) |
888 | | return 0; |
889 | | |
890 | | /* allocate memory if this is the first time through */ |
891 | | if (state->size_out == 0 && lz4_init(state) == -1) |
892 | | return 0; |
893 | | |
894 | | do { |
895 | | to_write = MIN(len, state->want); |
896 | | size_t bytesWritten = LZ4F_compressUpdate(state->lz4_cctx, state->out, state->size_out, |
897 | | buf, to_write, NULL); |
898 | | if (LZ4F_isError(bytesWritten)) { |
899 | | state->err = FILE_ERR_CANT_WRITE; // XXX - FILE_ERR_COMPRESS? |
900 | | state->err_info = LZ4F_getErrorName(bytesWritten); |
901 | | return 0; |
902 | | } |
903 | | if (!lz4_write_out(state, bytesWritten)) { |
904 | | return 0; |
905 | | } |
906 | | state->pos += to_write; |
907 | | len -= to_write; |
908 | | } while (len); |
909 | | |
910 | | /* input was all buffered or compressed */ |
911 | | return put; |
912 | | } |
913 | | |
914 | | /* Flush out what we've written so far. Returns -1, and sets state->err, |
915 | | on failure; returns 0 on success. */ |
916 | | int |
917 | | lz4wfile_flush(LZ4WFILE_T state) |
918 | | { |
919 | | size_t bytesWritten; |
920 | | /* check that there's no error */ |
921 | | if (state->err != 0) |
922 | | return -1; |
923 | | |
924 | | bytesWritten = LZ4F_flush(state->lz4_cctx, state->out, state->size_out, NULL); |
925 | | if (LZ4F_isError(bytesWritten)) { |
926 | | // Should never happen if size_out >= LZ4F_compressBound(0, prefsPtr) |
927 | | state->err = FILE_ERR_INTERNAL; |
928 | | return -1; |
929 | | } |
930 | | if (!lz4_write_out(state, bytesWritten)) { |
931 | | return -1; |
932 | | } |
933 | | return 0; |
934 | | } |
935 | | |
936 | | /* Flush out all data written, and close the file. Returns a Wiretap |
937 | | error on failure; returns 0 on success. */ |
938 | | int |
939 | | lz4wfile_close(LZ4WFILE_T state) |
940 | | { |
941 | | int ret = 0; |
942 | | |
943 | | /* flush, free memory, and close file */ |
944 | | size_t bytesWritten = LZ4F_compressEnd(state->lz4_cctx, state->out, state->size_out, NULL); |
945 | | if (LZ4F_isError(bytesWritten)) { |
946 | | // Should never happen if size_out >= LZ4F_compressBound(0, prefsPtr) |
947 | | ret = FILE_ERR_INTERNAL; |
948 | | } |
949 | | if (!lz4_write_out(state, bytesWritten)) { |
950 | | ret = state->err; |
951 | | } |
952 | | g_free(state->out); |
953 | | LZ4F_freeCompressionContext(state->lz4_cctx); |
954 | | if (ws_close(state->fd) == -1 && ret == 0) |
955 | | ret = errno; |
956 | | g_free(state); |
957 | | return ret; |
958 | | } |
959 | | |
960 | | int |
961 | | lz4wfile_geterr(LZ4WFILE_T state) |
962 | | { |
963 | | return state->err; |
964 | | } |
965 | | #endif /* HAVE_LZ4FRAME_H */ |