summaryrefslogtreecommitdiffstats
path: root/wiretap/file_wrappers.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--wiretap/file_wrappers.c1572
1 files changed, 1243 insertions, 329 deletions
diff --git a/wiretap/file_wrappers.c b/wiretap/file_wrappers.c
index 67979a5b..f0c42b84 100644
--- a/wiretap/file_wrappers.c
+++ b/wiretap/file_wrappers.c
@@ -15,6 +15,9 @@
*/
#include "config.h"
+
+#define WS_LOG_DOMAIN LOG_DOMAIN_WIRETAP
+
#include "file_wrappers.h"
#include <assert.h>
@@ -24,14 +27,25 @@
#include <wsutil/file_util.h>
-#ifdef HAVE_ZLIB
+#if defined(HAVE_ZLIB) && !defined(HAVE_ZLIBNG)
+#define USE_ZLIB_OR_ZLIBNG
#define ZLIB_CONST
+#define ZLIB_PREFIX(x) x
#include <zlib.h>
-#endif /* HAVE_ZLIB */
+typedef z_stream zlib_stream;
+#endif /* defined(HAVE_ZLIB) && !defined(HAVE_ZLIBNG) */
+
+#ifdef HAVE_ZLIBNG
+#define USE_ZLIB_OR_ZLIBNG
+#define HAVE_INFLATEPRIME 1
+#define ZLIB_PREFIX(x) zng_ ## x
+#include <zlib-ng.h>
+typedef zng_stream zlib_stream;
+#endif /* HAVE_ZLIBNG */
#ifdef HAVE_ZSTD
#include <zstd.h>
-#endif
+#endif /* HAVE_ZSTD */
#ifdef HAVE_LZ4
#include <lz4.h>
@@ -39,24 +53,11 @@
#if LZ4_VERSION_NUMBER >= 10703
#define USE_LZ4
#include <lz4frame.h>
-#endif
-#endif
-
-/*
- * See RFC 1952:
- *
- * https://tools.ietf.org/html/rfc1952
- *
- * for a description of the gzip file format.
- *
- * Some other compressed file formats we might want to support:
- *
- * XZ format: https://tukaani.org/xz/
- *
- * Bzip2 format: https://www.sourceware.org/bzip2/
- *
- * Lzip format: https://www.nongnu.org/lzip/
- */
+#ifndef LZ4F_BLOCK_HEADER_SIZE /* Added in LZ4_VERSION_NUMBER 10902 */
+#define LZ4F_BLOCK_HEADER_SIZE 4
+#endif /* LZ4F_BLOCK_HEADER_SIZE */
+#endif /* LZ4_VERSION_NUMBER >= 10703 */
+#endif /* HAVE_LZ4 */
/*
* List of compression types supported.
@@ -65,22 +66,58 @@ static struct compression_type {
wtap_compression_type type;
const char *extension;
const char *description;
+ const char *name;
+ const bool can_write_compressed;
} compression_types[] = {
-#ifdef HAVE_ZLIB
- { WTAP_GZIP_COMPRESSED, "gz", "gzip compressed" },
-#endif
+#ifdef USE_ZLIB_OR_ZLIBNG
+ { WTAP_GZIP_COMPRESSED, "gz", "gzip compressed", "gzip", true },
+#endif /* USE_ZLIB_OR_ZLIBNG */
#ifdef HAVE_ZSTD
- { WTAP_ZSTD_COMPRESSED, "zst", "zstd compressed" },
-#endif
+ { WTAP_ZSTD_COMPRESSED, "zst", "zstd compressed", "zstd", false },
+#endif /* HAVE_ZSTD */
#ifdef USE_LZ4
- { WTAP_LZ4_COMPRESSED, "lz4", "lz4 compressed" },
-#endif
- { WTAP_UNCOMPRESSED, NULL, NULL }
+ { WTAP_LZ4_COMPRESSED, "lz4", "lz4 compressed", "lz4", true },
+#endif /* USE_LZ4 */
+ { WTAP_UNCOMPRESSED, NULL, NULL, "none", true },
+ { WTAP_UNKNOWN_COMPRESSION, NULL, NULL, NULL, false },
};
static wtap_compression_type file_get_compression_type(FILE_T stream);
wtap_compression_type
+wtap_name_to_compression_type(const char *name)
+{
+ for (struct compression_type *p = compression_types;
+ p->type != WTAP_UNKNOWN_COMPRESSION; p++) {
+ if (!g_strcmp0(name, p->name))
+ return p->type;
+ }
+ return WTAP_UNKNOWN_COMPRESSION;
+}
+
+wtap_compression_type
+wtap_extension_to_compression_type(const char *ext)
+{
+ for (struct compression_type *p = compression_types;
+ p->type != WTAP_UNKNOWN_COMPRESSION; p++) {
+ if (!g_strcmp0(ext, p->extension))
+ return p->type;
+ }
+ return WTAP_UNKNOWN_COMPRESSION;
+}
+
+bool
+wtap_can_write_compression_type(wtap_compression_type compression_type)
+{
+ for (struct compression_type *p = compression_types; p->type != WTAP_UNKNOWN_COMPRESSION; p++) {
+ if (compression_type == p->type)
+ return p->can_write_compressed;
+ }
+
+ return false;
+}
+
+wtap_compression_type
wtap_get_compression_type(wtap *wth)
{
return file_get_compression_type((wth->fh == NULL) ? wth->random_fh : wth->fh);
@@ -117,13 +154,30 @@ wtap_get_all_compression_type_extensions_list(void)
for (struct compression_type *p = compression_types;
p->type != WTAP_UNCOMPRESSED; p++)
- extensions = g_slist_prepend(extensions, (gpointer)p->extension);
+ extensions = g_slist_prepend(extensions, (void *)p->extension);
return extensions;
}
+GSList *
+wtap_get_all_output_compression_type_names_list(void)
+{
+ GSList *names;
+
+ names = NULL; /* empty list, to start with */
+
+ for (struct compression_type *p = compression_types;
+ p->type != WTAP_UNCOMPRESSED; p++) {
+ if (p->can_write_compressed)
+ names = g_slist_prepend(names, (void *)p->name);
+ }
+
+ return names;
+}
+
/* #define GZBUFSIZE 8192 */
#define GZBUFSIZE 4096
+#define LZ4BUFSIZE 4194304 // 4MiB, maximum block size
/* values for wtap_reader compression */
typedef enum {
@@ -148,11 +202,11 @@ typedef enum {
* imposes a limit on the buffer size when we're reading a
* gzipped file.
*
- * Thus, we use guint for the buffer sizes, offsets, amount available
+ * Thus, we use unsigned for the buffer sizes, offsets, amount available
* from the buffer, etc.
*
* If we want an even bigger buffer for uncompressed data, or for
- * some other form of compression, then the guint-sized values should
+ * some other form of compression, then the unsigned-sized values should
* be in structure values used only for reading gzipped files, and
* other values should be used for uncompressed data or data
* compressed using other algorithms (e.g., in a union).
@@ -160,53 +214,61 @@ typedef enum {
#define MAX_READ_BUF_SIZE (1U << 30)
struct wtap_reader_buf {
- guint8 *buf; /* buffer */
- guint8 *next; /* next byte to deliver from buffer */
- guint avail; /* number of bytes available to deliver at next */
+ uint8_t *buf; /* buffer */
+ uint8_t *next; /* next byte to deliver from buffer */
+ unsigned avail; /* number of bytes available to deliver at next */
};
struct wtap_reader {
int fd; /* file descriptor */
- gint64 raw_pos; /* current position in file (just to not call lseek()) */
- gint64 pos; /* current position in uncompressed data */
- guint size; /* buffer size */
+ int64_t raw_pos; /* current position in file (just to not call lseek()) */
+ int64_t pos; /* current position in uncompressed data */
+ unsigned size; /* buffer size */
struct wtap_reader_buf in; /* input buffer, containing compressed data */
struct wtap_reader_buf out; /* output buffer, containing uncompressed data */
- gboolean eof; /* TRUE if end of input file reached */
- gint64 start; /* where the gzip data started, for rewinding */
- gint64 raw; /* where the raw data started, for seeking */
+ bool eof; /* true if end of input file reached */
+ int64_t start; /* where the gzip data started, for rewinding */
+ int64_t raw; /* where the raw data started, for seeking */
compression_t compression; /* type of compression, if any */
compression_t last_compression; /* last known compression type */
- gboolean is_compressed; /* FALSE if completely uncompressed, TRUE otherwise */
+ bool is_compressed; /* false if completely uncompressed, true otherwise */
/* seek request */
- gint64 skip; /* amount to skip (already rewound if backwards) */
- gboolean seek_pending; /* TRUE if seek request pending */
+ int64_t skip; /* amount to skip (already rewound if backwards) */
+ bool seek_pending; /* true if seek request pending */
/* error information */
int err; /* error code */
const char *err_info; /* additional error information string for some errors */
-#ifdef HAVE_ZLIB
+ /*
+ * Decompression stream information.
+ *
+ * XXX - should this be a union?
+ */
+#ifdef USE_ZLIB_OR_ZLIBNG
/* zlib inflate stream */
- z_stream strm; /* stream structure in-place (not a pointer) */
- gboolean dont_check_crc; /* TRUE if we aren't supposed to check the CRC */
-#endif
- /* fast seeking */
- GPtrArray *fast_seek;
- void *fast_seek_cur;
+ zlib_stream strm; /* stream structure in-place (not a pointer) */
+ bool dont_check_crc; /* true if we aren't supposed to check the CRC */
+#endif /* USE_ZLIB_OR_ZLIBNG */
#ifdef HAVE_ZSTD
ZSTD_DCtx *zstd_dctx;
-#endif
+#endif /* HAVE_ZSTD */
#ifdef USE_LZ4
LZ4F_dctx *lz4_dctx;
-#endif
+ LZ4F_frameInfo_t lz4_info;
+ unsigned char lz4_hdr[LZ4F_HEADER_SIZE_MAX];
+#endif /* USE_LZ4 */
+
+ /* fast seeking */
+ GPtrArray *fast_seek;
+ void *fast_seek_cur;
};
/* Current read offset within a buffer. */
-static guint
+static unsigned
offset_in_buffer(struct wtap_reader_buf *buf)
{
/* buf->next points to the next byte to read, and buf->buf points
@@ -215,11 +277,11 @@ offset_in_buffer(struct wtap_reader_buf *buf)
This will fit in an unsigned int, because it can't be bigger
than the size of the buffer, which is an unsigned int. */
- return (guint)(buf->next - buf->buf);
+ return (unsigned)(buf->next - buf->buf);
}
/* Number of bytes of data that are in a buffer. */
-static guint
+static unsigned
bytes_in_buffer(struct wtap_reader_buf *buf)
{
/* buf->next + buf->avail points just past the last byte of data in
@@ -227,9 +289,9 @@ bytes_in_buffer(struct wtap_reader_buf *buf)
Thus, (buf->next + buf->avail) - buf->buf is the number of bytes
of data in the buffer.
- This will fit in an guint, because it can't be bigger
- than the size of the buffer, which is a guint. */
- return (guint)((buf->next + buf->avail) - buf->buf);
+ This will fit in an unsigned, because it can't be bigger
+ than the size of the buffer, which is a unsigned. */
+ return (unsigned)((buf->next + buf->avail) - buf->buf);
}
/* Reset a buffer, discarding all data in the buffer, so we read into
@@ -244,7 +306,7 @@ buf_reset(struct wtap_reader_buf *buf)
static int
buf_read(FILE_T state, struct wtap_reader_buf *buf)
{
- guint space_left, to_read;
+ unsigned space_left, to_read;
unsigned char *read_ptr;
ssize_t ret;
@@ -273,9 +335,9 @@ buf_read(FILE_T state, struct wtap_reader_buf *buf)
return -1;
}
if (ret == 0)
- state->eof = TRUE;
+ state->eof = true;
state->raw_pos += ret;
- buf->avail += (guint)ret;
+ buf->avail += (unsigned)ret;
return 0;
}
@@ -292,23 +354,33 @@ fill_in_buffer(FILE_T state)
}
#define ZLIB_WINSIZE 32768
+#define LZ4_WINSIZE 65536
struct fast_seek_point {
- gint64 out; /* corresponding offset in uncompressed data */
- gint64 in; /* offset in input file of first full byte */
+ int64_t out; /* corresponding offset in uncompressed data */
+ int64_t in; /* offset in input file of first full byte */
compression_t compression;
union {
struct {
#ifdef HAVE_INFLATEPRIME
int bits; /* number of bits (1-7) from byte at in - 1, or 0 */
-#endif
+#endif /* HAVE_INFLATEPRIME */
unsigned char window[ZLIB_WINSIZE]; /* preceding 32K of uncompressed data */
/* be gentle with Z_STREAM_END, 8 bytes more... Another solution would be to comment checks out */
- guint32 adler;
- guint32 total_out;
+ uint32_t adler;
+ uint32_t total_out;
} zlib;
+#ifdef USE_LZ4
+ struct {
+ LZ4F_frameInfo_t lz4_info;
+ unsigned char lz4_hdr[LZ4F_HEADER_SIZE_MAX];
+#if 0
+ unsigned char window[LZ4_WINSIZE]; /* preceding 64K of uncompressed data */
+#endif
+ } lz4;
+#endif
} data;
};
@@ -318,13 +390,13 @@ struct zlib_cur_seek_point {
unsigned int have;
};
-#define SPAN G_GINT64_CONSTANT(1048576)
+#define SPAN INT64_C(1048576)
static struct fast_seek_point *
-fast_seek_find(FILE_T file, gint64 pos)
+fast_seek_find(FILE_T file, int64_t pos)
{
struct fast_seek_point *smallest = NULL;
struct fast_seek_point *item;
- guint low, i, max;
+ unsigned low, i, max;
if (!file->fast_seek)
return NULL;
@@ -346,42 +418,101 @@ fast_seek_find(FILE_T file, gint64 pos)
}
static void
-fast_seek_header(FILE_T file, gint64 in_pos, gint64 out_pos,
+fast_seek_header(FILE_T file, int64_t in_pos, int64_t out_pos,
compression_t compression)
{
struct fast_seek_point *item = NULL;
+ if (!file->fast_seek) {
+ return;
+ }
+
if (file->fast_seek->len != 0)
item = (struct fast_seek_point *)file->fast_seek->pdata[file->fast_seek->len - 1];
+ /* fast_seek_header always adds a fast seek point, even if less than
+ * SPAN from the last one. That is because it used for new streams
+ * (including concatenated streams) where the compression type
+ * or, for LZ4, compression options, may change.
+ */
if (!item || item->out < out_pos) {
struct fast_seek_point *val = g_new(struct fast_seek_point,1);
val->in = in_pos;
val->out = out_pos;
val->compression = compression;
+#ifdef USE_LZ4
+ if (compression == LZ4) {
+ val->data.lz4.lz4_info = file->lz4_info;
+ memcpy(val->data.lz4.lz4_hdr, file->lz4_hdr, LZ4F_HEADER_SIZE_MAX);
+ }
+#endif /* USE_LZ4 */
g_ptr_array_add(file->fast_seek, val);
}
}
static void
-fast_seek_reset(
-#ifdef HAVE_ZLIB
- FILE_T state)
-#else
- FILE_T state _U_)
-#endif
+fast_seek_reset(FILE_T state)
{
-#ifdef HAVE_ZLIB
- if (state->compression == ZLIB && state->fast_seek_cur != NULL) {
- struct zlib_cur_seek_point *cur = (struct zlib_cur_seek_point *) state->fast_seek_cur;
+ switch (state->compression) {
+
+ case UNKNOWN:
+ break;
- cur->have = 0;
+ case UNCOMPRESSED:
+ /* Nothing to do */
+ break;
+
+ case ZLIB:
+#ifdef USE_ZLIB_OR_ZLIBNG
+ if (state->fast_seek_cur != NULL) {
+ struct zlib_cur_seek_point *cur = (struct zlib_cur_seek_point *) state->fast_seek_cur;
+
+ cur->have = 0;
+ }
+#else
+ /* This "cannot happen" */
+ ws_assert_not_reached();
+#endif /* USE_ZLIB_OR_ZLIBNG */
+ break;
+
+ case GZIP_AFTER_HEADER:
+ break;
+
+ case ZSTD:
+#ifdef HAVE_ZSTD
+ /* Anything to do? */
+#else
+ /* This "cannot happen" */
+ ws_assert_not_reached();
+#endif /* HAVE_ZSTD */
+ break;
+
+ case LZ4:
+#ifdef HAVE_LZ4
+ /* Anything to do? */
+#else
+ /* This "cannot happen" */
+ ws_assert_not_reached();
+#endif /* HAVE_LZ4 */
+ break;
+
+ /* Add other compression types here */
+
+ default:
+ /* This "cannot happen" */
+ ws_assert_not_reached();
+ break;
}
-#endif
}
-#ifdef HAVE_ZLIB
+static bool
+uncompressed_fill_out_buffer(FILE_T state)
+{
+ if (buf_read(state, &state->out) < 0)
+ return false;
+ return true;
+}
/* Get next byte from input, or -1 if end or error.
*
@@ -399,10 +530,18 @@ fast_seek_reset(
(state->in.avail == 0 ? -1 : \
(state->in.avail--, *(state->in.next)++)))
+
+/*
+ * Gzipped files, using compression from zlib or zlib-ng.
+ *
+ * https://tools.ietf.org/html/rfc1952 (RFC 1952)
+ */
+#ifdef USE_ZLIB_OR_ZLIBNG
+
/* Get a one-byte integer and return 0 on success and the value in *ret.
Otherwise -1 is returned, state->err is set, and *ret is not modified. */
static int
-gz_next1(FILE_T state, guint8 *ret)
+gz_next1(FILE_T state, uint8_t *ret)
{
int ch;
@@ -423,9 +562,9 @@ gz_next1(FILE_T state, guint8 *ret)
in *ret. Otherwise -1 is returned, state->err is set, and *ret is not
modified. */
static int
-gz_next2(FILE_T state, guint16 *ret)
+gz_next2(FILE_T state, uint16_t *ret)
{
- guint16 val;
+ uint16_t val;
int ch;
val = GZ_GETC();
@@ -438,7 +577,7 @@ gz_next2(FILE_T state, guint16 *ret)
}
return -1;
}
- val += (guint16)ch << 8;
+ val += (uint16_t)ch << 8;
*ret = val;
return 0;
}
@@ -447,14 +586,14 @@ gz_next2(FILE_T state, guint16 *ret)
in *ret. Otherwise -1 is returned, state->err is set, and *ret is not
modified. */
static int
-gz_next4(FILE_T state, guint32 *ret)
+gz_next4(FILE_T state, uint32_t *ret)
{
- guint32 val;
+ uint32_t val;
int ch;
val = GZ_GETC();
val += (unsigned)GZ_GETC() << 8;
- val += (guint32)GZ_GETC() << 16;
+ val += (uint32_t)GZ_GETC() << 16;
ch = GZ_GETC();
if (ch == -1) {
if (state->err == 0) {
@@ -464,7 +603,7 @@ gz_next4(FILE_T state, guint32 *ret)
}
return -1;
}
- val += (guint32)ch << 24;
+ val += (uint32_t)ch << 24;
*ret = val;
return 0;
}
@@ -511,7 +650,7 @@ gz_skipzstr(FILE_T state)
}
static void
-zlib_fast_seek_add(FILE_T file, struct zlib_cur_seek_point *point, int bits, gint64 in_pos, gint64 out_pos)
+zlib_fast_seek_add(FILE_T file, struct zlib_cur_seek_point *point, int bits, int64_t in_pos, int64_t out_pos)
{
/* it's for sure after gzip header, so file->fast_seek->len != 0 */
struct fast_seek_point *item = (struct fast_seek_point *)file->fast_seek->pdata[file->fast_seek->len - 1];
@@ -519,7 +658,7 @@ zlib_fast_seek_add(FILE_T file, struct zlib_cur_seek_point *point, int bits, gin
#ifndef HAVE_INFLATEPRIME
if (bits)
return;
-#endif
+#endif /* HAVE_INFLATEPRIME */
/* Glib has got Balanced Binary Trees (GTree) but I couldn't find a way to do quick search for nearest (and smaller) value to seek (It's what fast_seek_find() do)
* Inserting value in middle of sorted array is expensive, so we want to add only in the end.
@@ -532,7 +671,7 @@ zlib_fast_seek_add(FILE_T file, struct zlib_cur_seek_point *point, int bits, gin
val->compression = ZLIB;
#ifdef HAVE_INFLATEPRIME
val->data.zlib.bits = bits;
-#endif
+#endif /* HAVE_INFLATEPRIME */
if (point->pos != 0) {
unsigned int left = ZLIB_WINSIZE - point->pos;
@@ -554,18 +693,27 @@ zlib_fast_seek_add(FILE_T file, struct zlib_cur_seek_point *point, int bits, gin
*
* The same applies to strm.total_out.
*/
- val->data.zlib.adler = (guint32) file->strm.adler;
- val->data.zlib.total_out = (guint32) file->strm.total_out;
+ val->data.zlib.adler = (uint32_t) file->strm.adler;
+ val->data.zlib.total_out = (uint32_t) file->strm.total_out;
g_ptr_array_add(file->fast_seek, val);
}
}
-static void /* gz_decomp */
-zlib_read(FILE_T state, unsigned char *buf, unsigned int count)
+/*
+ * Based on what gz_decomp() in zlib does.
+ */
+static void
+zlib_fill_out_buffer(FILE_T state)
{
int ret = 0; /* XXX */
- guint32 crc, len;
+ uint32_t crc, len;
+#ifdef HAVE_ZLIBNG
+ zng_streamp strm = &(state->strm);
+#else /* HAVE_ZLIBNG */
z_streamp strm = &(state->strm);
+#endif /* HAVE_ZLIBNG */
+ unsigned char *buf = state->out.buf;
+ unsigned int count = state->size << 1;
unsigned char *buf2 = buf;
unsigned int count2 = count;
@@ -589,18 +737,18 @@ zlib_read(FILE_T state, unsigned char *buf, unsigned int count)
strm->next_in = state->in.next;
/* decompress and handle errors */
#ifdef Z_BLOCK
- ret = inflate(strm, Z_BLOCK);
-#else
- ret = inflate(strm, Z_NO_FLUSH);
-#endif
+ ret = ZLIB_PREFIX(inflate)(strm, Z_BLOCK);
+#else /* Z_BLOCK */
+ ret = ZLIB_PREFIX(inflate)(strm, Z_NO_FLUSH);
+#endif /* Z_BLOCK */
state->in.avail = strm->avail_in;
#ifdef z_const
DIAG_OFF(cast-qual)
state->in.next = (unsigned char *)strm->next_in;
DIAG_ON(cast-qual)
-#else
+#else /* z_const */
state->in.next = strm->next_in;
-#endif
+#endif /* z_const */
if (ret == Z_STREAM_ERROR) {
state->err = WTAP_ERR_DECOMPRESS;
state->err_info = strm->msg;
@@ -626,14 +774,14 @@ DIAG_ON(cast-qual)
* XXX - Z_BUF_ERROR?
*/
- strm->adler = crc32(strm->adler, buf2, count2 - strm->avail_out);
+ strm->adler = ZLIB_PREFIX(crc32)(strm->adler, buf2, count2 - strm->avail_out);
#ifdef Z_BLOCK
if (state->fast_seek_cur != NULL) {
struct zlib_cur_seek_point *cur = (struct zlib_cur_seek_point *) state->fast_seek_cur;
unsigned int ready = count2 - strm->avail_out;
if (ready < ZLIB_WINSIZE) {
- guint left = ZLIB_WINSIZE - cur->pos;
+ unsigned left = ZLIB_WINSIZE - cur->pos;
if (ready >= left) {
memcpy(cur->window + cur->pos, buf2, left);
@@ -660,7 +808,7 @@ DIAG_ON(cast-qual)
if (cur->have >= ZLIB_WINSIZE && ret != Z_STREAM_END && (strm->data_type & 128) && !(strm->data_type & 64))
zlib_fast_seek_add(state, cur, (strm->data_type & 7), state->raw_pos - strm->avail_in, state->pos + (count - strm->avail_out));
}
-#endif
+#endif /* Z_BLOCK */
buf2 = (buf2 + count2 - strm->avail_out);
count2 = strm->avail_out;
@@ -692,22 +840,21 @@ DIAG_ON(cast-qual)
state->fast_seek_cur = NULL;
}
}
-#endif
+#endif /* USE_ZLIB_OR_ZLIBNG */
+/*
+ * Check for a gzip header.
+ *
+ * Based on the gzip-specific stuff gz_head() from zlib does.
+ */
static int
-gz_head(FILE_T state)
+check_for_zlib_compression(FILE_T state)
{
- guint already_read;
-
- /* get some data in the input buffer */
- if (state->in.avail == 0) {
- if (fill_in_buffer(state) == -1)
- return -1;
- if (state->in.avail == 0)
- return 0;
- }
-
- /* look for the gzip magic header bytes 31 and 139 */
+ /*
+ * Look for the gzip header. The first two bytes are 31 and 139,
+ * and if we find it, return success if we support gzip and an
+ * error if we don't.
+ */
if (state->in.next[0] == 31) {
state->in.avail--;
state->in.next++;
@@ -729,11 +876,11 @@ gz_head(FILE_T state)
* and 139 in the second byte of the file. For now, in
* those cases, you lose.
*/
-#ifdef HAVE_ZLIB
- guint8 cm;
- guint8 flags;
- guint16 len;
- guint16 hcrc;
+#ifdef USE_ZLIB_OR_ZLIBNG
+ uint8_t cm;
+ uint8_t flags;
+ uint16_t len;
+ uint16_t hcrc;
state->in.avail--;
state->in.next++;
@@ -815,10 +962,10 @@ gz_head(FILE_T state)
}
/* set up for decompression */
- inflateReset(&(state->strm));
- state->strm.adler = crc32(0L, Z_NULL, 0);
+ ZLIB_PREFIX(inflateReset)(&(state->strm));
+ state->strm.adler = ZLIB_PREFIX(crc32)(0L, Z_NULL, 0);
state->compression = ZLIB;
- state->is_compressed = TRUE;
+ state->is_compressed = true;
#ifdef Z_BLOCK
if (state->fast_seek) {
struct zlib_cur_seek_point *cur = g_new(struct zlib_cur_seek_point,1);
@@ -829,12 +976,12 @@ gz_head(FILE_T state)
fast_seek_header(state, state->raw_pos - state->in.avail, state->pos, GZIP_AFTER_HEADER);
}
#endif /* Z_BLOCK */
- return 0;
-#else /* HAVE_ZLIB */
+ return 1;
+#else /* USE_ZLIB_OR_ZLIBNG */
state->err = WTAP_ERR_DECOMPRESSION_NOT_SUPPORTED;
state->err_info = "reading gzip-compressed files isn't supported";
return -1;
-#endif /* HAVE_ZLIB */
+#endif /* USE_ZLIB_OR_ZLIBNG */
}
/*
@@ -856,14 +1003,60 @@ gz_head(FILE_T state)
state->in.next--;
}
}
-#ifdef HAVE_LIBXZ
- /* { 0xFD, '7', 'z', 'X', 'Z', 0x00 } */
- /* FD 37 7A 58 5A 00 */
-#endif
+ return 0;
+}
+
+/*
+ * Zstandard compression.
+ *
+ * https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md
+ */
+#ifdef HAVE_ZSTD
+static bool
+zstd_fill_out_buffer(FILE_T state)
+{
+ ws_assert(state->out.avail == 0);
+
+ if (state->in.avail == 0 && fill_in_buffer(state) == -1)
+ return false;
+
+ ZSTD_outBuffer output = {state->out.buf, state->size << 1, 0};
+ ZSTD_inBuffer input = {state->in.next, state->in.avail, 0};
+ const size_t ret = ZSTD_decompressStream(state->zstd_dctx, &output, &input);
+ if (ZSTD_isError(ret)) {
+ state->err = WTAP_ERR_DECOMPRESS;
+ state->err_info = ZSTD_getErrorName(ret);
+ return false;
+ }
+
+ state->in.next = state->in.next + input.pos;
+ state->in.avail -= (unsigned)input.pos;
+
+ state->out.next = output.dst;
+ state->out.avail = (unsigned)output.pos;
+
+ if (ret == 0) {
+ state->last_compression = state->compression;
+ state->compression = UNKNOWN;
+ }
+ return true;
+}
+#endif /* HAVE_ZSTD */
+
+/*
+ * Check for a Zstandard header.
+ */
+static int
+check_for_zstd_compression(FILE_T state)
+{
+ /*
+ * Look for the Zstandard header, and, if we find it, return
+ * success if we support Zstandard and an error if we don't.
+ */
if (state->in.avail >= 4
- && state->in.buf[0] == 0x28 && state->in.buf[1] == 0xb5
- && state->in.buf[2] == 0x2f && state->in.buf[3] == 0xfd) {
+ && state->in.next[0] == 0x28 && state->in.next[1] == 0xb5
+ && state->in.next[2] == 0x2f && state->in.next[3] == 0xfd) {
#ifdef HAVE_ZSTD
const size_t ret = ZSTD_initDStream(state->zstd_dctx);
if (ZSTD_isError(ret)) {
@@ -872,23 +1065,290 @@ gz_head(FILE_T state)
return -1;
}
+ fast_seek_header(state, state->raw_pos - state->in.avail, state->pos, ZSTD);
state->compression = ZSTD;
- state->is_compressed = TRUE;
- return 0;
-#else
+ state->is_compressed = true;
+ return 1;
+#else /* HAVE_ZSTD */
state->err = WTAP_ERR_DECOMPRESSION_NOT_SUPPORTED;
state->err_info = "reading zstd-compressed files isn't supported";
return -1;
+#endif /* HAVE_ZSTD */
+ }
+ return 0;
+}
+
+/*
+ * lz4 compression.
+ *
+ * https://github.com/lz4/lz4/blob/dev/doc/lz4_Frame_format.md
+ */
+#ifdef USE_LZ4
+static void
+lz4_fast_seek_add(FILE_T file, struct zlib_cur_seek_point *point _U_, int64_t in_pos, int64_t out_pos)
+{
+ if (!file->fast_seek) {
+ return;
+ }
+
+ struct fast_seek_point *item = NULL;
+
+ if (file->fast_seek->len != 0)
+ item = (struct fast_seek_point *)file->fast_seek->pdata[file->fast_seek->len - 1];
+
+ /* As of Glib 2.68 GTree has g_tree_upper_bound, or we could use a
+ * wmem_tree. However, since our initial read is usually sequential
+ * only adding seek points at the end of the ptr array is fast and fine.
+ */
+
+ /* don't bother adding jump points between very small blocks (min SPAN) */
+ if (!item || item->out + SPAN < out_pos) {
+ struct fast_seek_point *val = g_new(struct fast_seek_point,1);
+ val->in = in_pos;
+ val->out = out_pos;
+ val->compression = LZ4;
+#if 0
+ if (point->pos != 0) {
+ unsigned int left = LZ4_WINSIZE - point->pos;
+
+ memcpy(val->data.zlib.window, point->window + point->pos, left);
+ memcpy(val->data.zlib.window + left, point->window, point->pos);
+ } else
+ memcpy(val->data.zlib.window, point->window, ZZ4_WINSIZE);
#endif
+
+ val->data.lz4.lz4_info = file->lz4_info;
+ memcpy(val->data.lz4.lz4_hdr, file->lz4_hdr, LZ4F_HEADER_SIZE_MAX);
+ g_ptr_array_add(file->fast_seek, val);
+ }
+}
+
+static bool
+lz4_fill_out_buffer(FILE_T state)
+{
+ ws_assert(state->out.avail == 0);
+
+ if (state->in.avail == 0 && fill_in_buffer(state) == -1)
+ return false;
+
+ /*
+ * We should be at the start of a block. First, determine the size of
+ * the block. We tell LZ4F_decompress that there's no room to put
+ * the decompressed block; this will make it read the block size
+ * header and stop, returning the size of the block (plus next
+ * header) as hint of how much input to hand next.
+ */
+
+ size_t outBufSize = 0; // Zero so we don't actually consume the block
+ size_t inBufSize;
+
+ size_t compressedSize = 0;
+
+ do {
+ /* get more input for decompress() */
+ if (state->in.avail == 0 && fill_in_buffer(state) == -1)
+ break;
+ if (state->eof) {
+ state->err = WTAP_ERR_SHORT_READ;
+ state->err_info = NULL;
+ break;
+ }
+
+ inBufSize = state->in.avail;
+ compressedSize = LZ4F_decompress(state->lz4_dctx, state->out.buf, &outBufSize, state->in.next, &inBufSize, NULL);
+
+ if (LZ4F_isError(compressedSize)) {
+ state->err = WTAP_ERR_DECOMPRESS;
+ state->err_info = LZ4F_getErrorName(compressedSize);
+ return false;
+ }
+
+ state->in.next += inBufSize;
+ state->in.avail -= inBufSize;
+
+ if (compressedSize == 0) {
+ /* End of Frame */
+ state->last_compression = state->compression;
+ state->compression = UNKNOWN;
+ return true;
+ }
+
+ ws_assert(outBufSize == 0);
+
+ } while (compressedSize < LZ4F_BLOCK_HEADER_SIZE);
+
+ /*
+ * We don't want to add a fast seek point for the end of frame,
+ * especially if there's another frame or other stream after it,
+ * which would have the same out position. So add it after the
+ * reading the block size - but point to where the block size
+ * is so that we'll fast seek to the block size again.
+ */
+ if (state->lz4_info.blockMode == LZ4F_blockIndependent) {
+ /*
+ * XXX - If state->lz4_info.blockMode == LZ4F_blockLinked, it doesn't
+ * seem like the LZ4 Frame API can handle this, we would need to use
+ * the low level Block API and pass the last 64KiB window of data to
+ * LZ4_setStreamDecode and use LZ4_decompress_safe_continue (similar
+ * to gzip). So for now we can't do fast seek with it (we do add one
+ * header at the frame beginning so that concatenated frames and other
+ * decompression streams work.)
+ */
+ lz4_fast_seek_add(state, NULL, state->raw_pos - state->in.avail - LZ4F_BLOCK_HEADER_SIZE, state->pos);
+ }
+
+ // Now actually read the entire next block - but not the next header
+ compressedSize -= LZ4F_BLOCK_HEADER_SIZE;
+ state->out.next = state->out.buf;
+
+ if (compressedSize > state->size) {
+ /*
+ * What is this? Either bogus, or some new variant of LZ4 Frames with
+ * a larger block size we don't support. We could have a buffer
+ * overrun if we try to process it.
+ *
+ * TODO - We could realloc here.
+ */
+ state->err = WTAP_ERR_DECOMPRESSION_NOT_SUPPORTED;
+ state->err_info = "lz4 compressed block size too large";
+ return false;
+ }
+
+ size_t ret;
+ do {
+ /* get more input for decompress() */
+ if (state->in.avail == 0 && fill_in_buffer(state) == -1)
+ break;
+ if (state->eof) {
+ state->err = WTAP_ERR_SHORT_READ;
+ state->err_info = NULL;
+ break;
+ }
+
+ outBufSize = (state->size << 1) - offset_in_buffer(&state->out);
+ inBufSize = MIN(state->in.avail, compressedSize);
+ ret = LZ4F_decompress(state->lz4_dctx, state->out.next, &outBufSize, state->in.next, &inBufSize, NULL);
+
+ if (LZ4F_isError(ret)) {
+ state->err = WTAP_ERR_DECOMPRESS;
+ state->err_info = LZ4F_getErrorName(ret);
+ return false;
+ }
+ state->in.next += inBufSize;
+ state->in.avail -= inBufSize;
+ compressedSize -= inBufSize;
+
+ state->out.next += outBufSize;
+ state->out.avail += outBufSize;
+ } while (compressedSize != 0);
+
+ state->out.next = state->out.buf;
+
+#if 0
+ /* This is an alternative implementation using the lower-level
+ * LZ4 Block API. Doing something like this might be necessary
+ * to handle linked blocks, because the Frame API doesn't have
+ * a method to reset the dictionary / window.
+ */
+ int outBufSize = state->size << 1;
+ uint32_t compressedSize;
+ if (gz_next4(state, &compressedSize) == -1) {
+ return false;
+ }
+ if (compressedSize == 0) {
+ /* EndMark */
+ if (state->lz4_info.contentChecksumFlag) {
+ uint32_t xxHash;
+ if (gz_next4(state, &xxHash) == -1) {
+ return false;
+ }
+ /* XXX - check hash? */
+ }
+ state->last_compression = state->compression;
+ state->compression = UNKNOWN;
+ return true;
+ }
+ bool uncompressed = compressedSize >> 31;
+ compressedSize &= 0x7FFFFFFF;
+ if (compressedSize > state->size) {
+ // TODO - we could realloc here
+ state->err = WTAP_ERR_DECOMPRESSION_NOT_SUPPORTED;
+ state->err_info = "lz4 compressed block size too large";
+ return false;
}
+ /*
+ * We have to read an entire block as we're using the low-level
+ * Block API instead of the LZ4 Frame API.
+ */
+ if (compressedSize > (unsigned)state->in.avail) {
+ memmove(state->in.buf, state->in.next, state->in.avail);
+ state->in.next = state->in.buf;
+ while ((unsigned)state->in.avail < compressedSize) {
+ if (state->eof) {
+ state->err = WTAP_ERR_SHORT_READ;
+ state->err_info = NULL;
+ return false;
+ }
+ if (fill_in_buffer(state) == -1) {
+ return false;
+ }
+ }
+ }
+
+ int decompressedSize;
+ if (uncompressed) {
+ memcpy(state->out.buf, state->in.buf, compressedSize);
+ decompressedSize = compressedSize;
+ } else {
+ decompressedSize = LZ4_decompress_safe(state->in.next, state->out.buf, compressedSize, outBufSize);
+ //const size_t ret = LZ4F_decompress(state->lz4_dctx, state->out.buf, &outBufSize, state->in.next, &inBufSize, NULL);
+ if (LZ4F_isError(decompressedSize)) {
+ state->err = WTAP_ERR_DECOMPRESS;
+ state->err_info = LZ4F_getErrorName(decompressedSize);
+ return false;
+ }
+ }
+
+ /*
+ * We assume LZ4F_decompress() will not set inBufSize to a
+ * value > state->in.avail.
+ */
+ state->in.next += compressedSize;
+ state->in.avail -= compressedSize;
+
+ state->out.next = state->out.buf;
+ state->out.avail = (unsigned)decompressedSize;
+
+ if (state->lz4_info.blockChecksumFlag == LZ4F_blockChecksumEnabled) {
+ uint32_t xxHash;
+ if (gz_next4(state, &xxHash) == -1) {
+ return false;
+ }
+ /* XXX - check hash? */
+ }
+#endif
+ return true;
+}
+#endif /* USE_LZ4 */
+
+/*
+ * Check for an lz4 header.
+ */
+static int
+check_for_lz4_compression(FILE_T state)
+{
+ /*
+ * Look for the lz4 header, and, if we find it, return success
+ * if we support lz4 and an error if we don't.
+ */
if (state->in.avail >= 4
- && state->in.buf[0] == 0x04 && state->in.buf[1] == 0x22
- && state->in.buf[2] == 0x4d && state->in.buf[3] == 0x18) {
+ && state->in.next[0] == 0x04 && state->in.next[1] == 0x22
+ && state->in.next[2] == 0x4d && state->in.next[3] == 0x18) {
#ifdef USE_LZ4
#if LZ4_VERSION_NUMBER >= 10800
LZ4F_resetDecompressionContext(state->lz4_dctx);
-#else
+#else /* LZ4_VERSION_NUMBER >= 10800 */
LZ4F_freeDecompressionContext(state->lz4_dctx);
const LZ4F_errorCode_t ret = LZ4F_createDecompressionContext(&state->lz4_dctx, LZ4F_VERSION);
if (LZ4F_isError(ret)) {
@@ -896,19 +1356,157 @@ gz_head(FILE_T state)
state->err_info = LZ4F_getErrorName(ret);
return -1;
}
-#endif
+#endif /* LZ4_VERSION_NUMBER >= 10800 */
+ size_t headerSize = LZ4F_HEADER_SIZE_MAX;
+#if LZ4_VERSION_NUMBER >= 10903
+ /*
+ * In 1.9.3+ we can handle a silly edge case of a tiny valid
+ * frame at the end of a file that is smaller than the maximum
+ * header size. (lz4frame.h added the function in 1.9.0, but
+ * only for the static library; it wasn't exported until 1.9.3)
+ */
+ while (state->in.avail < LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH) {
+ if (fill_in_buffer(state) == -1) {
+ return -1;
+ }
+ if (state->eof) {
+ state->err = WTAP_ERR_SHORT_READ;
+ state->err_info = NULL;
+ return 0;
+ }
+ }
+ headerSize = LZ4F_headerSize(state->in.next, state->in.avail);
+ if (LZ4F_isError(headerSize)) {
+ state->err = WTAP_ERR_DECOMPRESS;
+ state->err_info = LZ4F_getErrorName(headerSize);
+ return -1;
+ }
+#endif /* LZ4_VERSION_NUMBER >= 10903 */
+ while (state->in.avail < headerSize) {
+ if (fill_in_buffer(state) == -1) {
+ return -1;
+ }
+ if (state->eof) {
+ state->err = WTAP_ERR_SHORT_READ;
+ state->err_info = NULL;
+ return 0;
+ }
+ }
+ size_t inBufSize = state->in.avail;
+ memcpy(state->lz4_hdr, state->in.next, headerSize);
+ const LZ4F_errorCode_t err = LZ4F_getFrameInfo(state->lz4_dctx, &state->lz4_info, state->in.next, &inBufSize);
+ if (LZ4F_isError(err)) {
+ state->err = WTAP_ERR_DECOMPRESS;
+ state->err_info = LZ4F_getErrorName(err);
+ return -1;
+ }
+
+ /*
+ * XXX - We could check state->lz4_info.blockSizeID here, and
+ * only realloc the buffers to a larger value if the max
+ * block size is bigger than state->size. Also we could fail
+ * on unknown values?
+ */
+ state->in.avail -= inBufSize;
+ state->in.next += inBufSize;
+
+ fast_seek_header(state, state->raw_pos - state->in.avail, state->pos, LZ4);
state->compression = LZ4;
- state->is_compressed = TRUE;
- return 0;
-#else
+ state->is_compressed = true;
+ return 1;
+#else /* USE_LZ4 */
state->err = WTAP_ERR_DECOMPRESSION_NOT_SUPPORTED;
state->err_info = "reading lz4-compressed files isn't supported";
return -1;
-#endif
+#endif /* USE_LZ4 */
+ }
+ return 0;
+}
+
+typedef int (*compression_type_test)(FILE_T);
+
+static compression_type_test const compression_type_tests[] = {
+ check_for_zlib_compression,
+ check_for_zstd_compression,
+ check_for_lz4_compression,
+};
+
+/*
+ * Used when we haven't yet determined whether we have a compressed file
+ * and, if we do, what sort of compressed file it is.
+ *
+ * Based on the non-gzip-specific stuff that gz_head() from zlib does.
+ */
+static int
+check_for_compression(FILE_T state)
+{
+ /*
+ * If this isn't the first frame / compressed stream, ensure that
+ * we're starting at the beginning of the buffer. This shouldn't
+ * get called much.
+ *
+ * This is to avoid edge cases where a previous frame finished but
+ * state->in.next is close to the end of the buffer so there isn't
+ * much room to put the start of the next frame.
+ * This also lets us put back bytes if things go wrong.
+ */
+ if (state->in.next != state->in.buf) {
+ memmove(state->in.buf, state->in.next, state->in.avail);
+ state->in.next = state->in.buf;
}
+ /* get some data in the input buffer */
+ if (state->in.avail == 0) {
+ if (fill_in_buffer(state) == -1)
+ return -1;
+ if (state->in.avail == 0)
+ return 0;
+ }
+
+ /*
+ * Check for the compression types we support.
+ */
+ for (size_t i = 0; i < G_N_ELEMENTS(compression_type_tests); i++) {
+ int ret;
+
+ ret = compression_type_tests[i](state);
+ if (ret == -1)
+ return -1; /* error */
+ if (ret == 1)
+ return 0; /* found it */
+ }
+
+ /*
+ * Some other compressed file formats we might want to support:
+ *
+ * XZ format:
+ * https://tukaani.org/xz/
+ * https://github.com/tukaani-project/xz
+ * https://github.com/tukaani-project/xz/blob/master/doc/xz-file-format.txt
+ *
+ * Bzip2 format:
+ * https://www.sourceware.org/bzip2/
+ * https://gitlab.com/bzip2/bzip2/
+ * https://github.com/dsnet/compress/blob/master/doc/bzip2-format.pdf
+ * (GitHub won't render it; download and open it)
+ *
+ * Lzip format:
+ * https://www.nongnu.org/lzip/
+ */
+
+ /*
+ * We didn't see anything that looks like a header for any type of
+ * compressed file that we support, so just do uncompressed I/O.
+ *
+ * XXX - This fast seek data is for the case where a compressed stream
+ * ends and is followed by an uncompressed portion. It only works if
+ * the uncompressed portion is at the end, as we don't constantly scan
+ * for magic bytes in the middle of uncompressed data. (Concatenated
+ * compressed streams _do_ work, even streams of different compression types.)
+ */
if (state->fast_seek)
- fast_seek_header(state, state->raw_pos - state->in.avail - state->out.avail, state->pos, UNCOMPRESSED);
+ fast_seek_header(state, state->raw_pos - state->in.avail, state->pos, UNCOMPRESSED);
+
/* doing raw i/o, save start of raw data for seeking, copy any leftover
input to output -- this assumes that the output buffer is larger than
@@ -917,10 +1515,9 @@ gz_head(FILE_T state)
state->out.next = state->out.buf;
/* not a compressed file -- copy everything we've read into the
input buffer to the output buffer and fall to raw i/o */
- already_read = bytes_in_buffer(&state->in);
- if (already_read != 0) {
- memcpy(state->out.buf, state->in.buf, already_read);
- state->out.avail = already_read;
+ if (state->in.avail) {
+ memcpy(state->out.buf, state->in.next, state->in.avail);
+ state->out.avail = state->in.avail;
/* Now discard everything in the input buffer */
buf_reset(&state->in);
@@ -929,98 +1526,77 @@ gz_head(FILE_T state)
return 0;
}
-static int /* gz_make */
+/*
+ * Based on what gz_make() in zlib does.
+ */
+static int
fill_out_buffer(FILE_T state)
{
- if (state->compression == UNKNOWN) { /* look for compression header */
- if (gz_head(state) == -1)
+ if (state->compression == UNKNOWN) {
+ /*
+ * We don't yet know whether the file is compressed,
+ * so check for a compressed-file header.
+ */
+ if (check_for_compression(state) == -1)
return -1;
- if (state->out.avail != 0) /* got some data from gz_head() */
+ if (state->out.avail != 0) /* got some data from check_for_compression() */
return 0;
}
- if (state->compression == UNCOMPRESSED) { /* straight copy */
- if (buf_read(state, &state->out) < 0)
- return -1;
- }
-#ifdef HAVE_ZLIB
- else if (state->compression == ZLIB) { /* decompress */
- zlib_read(state, state->out.buf, state->size << 1);
- }
-#endif
-#ifdef HAVE_ZSTD
- else if (state->compression == ZSTD) {
- ws_assert(state->out.avail == 0);
- if (state->in.avail == 0 && fill_in_buffer(state) == -1)
- return -1;
+ /*
+ * We got no data from check_for_compression(), or we didn't call
+ * it as we already know the compression type, so read some more
+ * data.
+ */
+ switch (state->compression) {
- ZSTD_outBuffer output = {state->out.buf, state->size << 1, 0};
- ZSTD_inBuffer input = {state->in.next, state->in.avail, 0};
- const size_t ret = ZSTD_decompressStream(state->zstd_dctx, &output, &input);
- if (ZSTD_isError(ret)) {
- state->err = WTAP_ERR_DECOMPRESS;
- state->err_info = ZSTD_getErrorName(ret);
+ case UNCOMPRESSED:
+ /* straight copy */
+ if (!uncompressed_fill_out_buffer(state))
return -1;
- }
-
- state->in.next = state->in.next + input.pos;
- state->in.avail -= (guint)input.pos;
+ break;
- state->out.next = output.dst;
- state->out.avail = (guint)output.pos;
-
- if (ret == 0) {
- state->last_compression = state->compression;
- state->compression = UNKNOWN;
- }
- }
-#endif
-#ifdef USE_LZ4
- else if (state->compression == LZ4) {
- ws_assert(state->out.avail == 0);
+#ifdef USE_ZLIB_OR_ZLIBNG
+ case ZLIB:
+ /* zlib (gzip) decompress */
+ zlib_fill_out_buffer(state);
+ break;
+#endif /* USE_ZLIB_OR_ZLIBNG */
- if (state->in.avail == 0 && fill_in_buffer(state) == -1)
+#ifdef HAVE_ZSTD
+ case ZSTD:
+ /* zstd decompress */
+ if (!zstd_fill_out_buffer(state))
return -1;
+ break;
+#endif /* HAVE_ZSTD */
- size_t outBufSize = state->size << 1;
- size_t inBufSize = state->in.avail;
- const size_t ret = LZ4F_decompress(state->lz4_dctx, state->out.buf, &outBufSize, state->in.next, &inBufSize, NULL);
- if (LZ4F_isError(ret)) {
- state->err = WTAP_ERR_DECOMPRESS;
- state->err_info = LZ4F_getErrorName(ret);
+#ifdef USE_LZ4
+ case LZ4:
+ /* lz4 decompress */
+ if (!lz4_fill_out_buffer(state))
return -1;
- }
-
- /*
- * We assume LZ4F_decompress() will not set inBufSize to a
- * value > state->in.avail.
- */
- state->in.next = state->in.next + inBufSize;
- state->in.avail -= (guint)inBufSize;
-
- state->out.next = state->out.buf;
- state->out.avail = (guint)outBufSize;
+ break;
+#endif /* USE_LZ4 */
- if (ret == 0) {
- state->last_compression = state->compression;
- state->compression = UNKNOWN;
- }
+ default:
+ /* Unknown compression type; keep reading */
+ break;
}
-#endif
return 0;
}
static int
-gz_skip(FILE_T state, gint64 len)
+gz_skip(FILE_T state, int64_t len)
{
- guint n;
+ unsigned n;
/* skip over len bytes or reach end-of-file, whichever comes first */
while (len)
if (state->out.avail != 0) {
/* We have stuff in the output buffer; skip over
it. */
- n = (gint64)state->out.avail > len ? (unsigned)len : state->out.avail;
+ n = (int64_t)state->out.avail > len ? (unsigned)len : state->out.avail;
state->out.avail -= n;
state->out.next += n;
state->pos += n;
@@ -1050,10 +1626,10 @@ static void
gz_reset(FILE_T state)
{
buf_reset(&state->out); /* no output data available */
- state->eof = FALSE; /* not at end of file */
+ state->eof = false; /* not at end of file */
state->compression = UNKNOWN; /* look for compression header */
- state->seek_pending = FALSE; /* no seek request pending */
+ state->seek_pending = false; /* no seek request pending */
state->err = 0; /* clear error */
state->err_info = NULL;
state->pos = 0; /* no uncompressed data yet */
@@ -1083,15 +1659,15 @@ file_fdopen(int fd)
*/
#ifdef _STATBUF_ST_BLKSIZE
ws_statb64 st;
-#endif
+#endif /* _STATBUF_ST_BLKSIZE */
#ifdef HAVE_ZSTD
size_t zstd_buf_size;
-#endif
- guint want = GZBUFSIZE;
+#endif /* HAVE_ZSTD */
+ unsigned want = GZBUFSIZE;
FILE_T state;
#ifdef USE_LZ4
size_t ret;
-#endif
+#endif /* USE_LZ4 */
if (fd == -1)
return NULL;
@@ -1108,7 +1684,7 @@ file_fdopen(int fd)
state->fd = fd;
/* we don't yet know whether it's compressed */
- state->is_compressed = FALSE;
+ state->is_compressed = false;
state->last_compression = UNKNOWN;
/* save the current position for rewinding (only if reading) */
@@ -1130,7 +1706,7 @@ file_fdopen(int fd)
* Yes, st_blksize can be bigger than an int; apparently,
* it's a long on LP64 Linux, for example.
*
- * If the value is too big to fit into a guint,
+ * If the value is too big to fit into a unsigned,
* just use the maximum read buffer size.
*
* On top of that, the Single UNIX Speification says that
@@ -1152,29 +1728,39 @@ file_fdopen(int fd)
* (We only support 32-bit and 64-bit 2's-complement platforms.)
*/
if (st.st_blksize <= (long)MAX_READ_BUF_SIZE)
- want = (guint)st.st_blksize;
+ want = (unsigned)st.st_blksize;
else
want = MAX_READ_BUF_SIZE;
/* XXX, verify result? */
}
-#endif
+#endif /* _STATBUF_ST_BLKSIZE */
#ifdef HAVE_ZSTD
/* we should have separate input and output buf sizes */
zstd_buf_size = ZSTD_DStreamInSize();
if (zstd_buf_size > want) {
if (zstd_buf_size <= MAX_READ_BUF_SIZE)
- want = (guint)zstd_buf_size;
+ want = (unsigned)zstd_buf_size;
else
want = MAX_READ_BUF_SIZE;
}
zstd_buf_size = ZSTD_DStreamOutSize();
if (zstd_buf_size > want) {
if (zstd_buf_size <= MAX_READ_BUF_SIZE)
- want = (guint)zstd_buf_size;
+ want = (unsigned)zstd_buf_size;
else
want = MAX_READ_BUF_SIZE;
}
-#endif
+#endif /* HAVE_ZSTD */
+#ifdef USE_LZ4
+ if (LZ4BUFSIZE > want) {
+ if (LZ4BUFSIZE <= MAX_READ_BUF_SIZE) {
+ want = LZ4BUFSIZE;
+ } else {
+ goto err;
+ }
+ }
+#endif /* USE_LZ4 */
+
/* allocate buffers */
state->in.buf = (unsigned char *)g_try_malloc(want);
state->in.next = state->in.buf;
@@ -1187,48 +1773,48 @@ file_fdopen(int fd)
goto err;
}
-#ifdef HAVE_ZLIB
+#ifdef USE_ZLIB_OR_ZLIBNG
/* allocate inflate memory */
state->strm.zalloc = Z_NULL;
state->strm.zfree = Z_NULL;
state->strm.opaque = Z_NULL;
state->strm.avail_in = 0;
state->strm.next_in = Z_NULL;
- if (inflateInit2(&(state->strm), -15) != Z_OK) { /* raw inflate */
+ if (ZLIB_PREFIX(inflateInit2)(&(state->strm), -15) != Z_OK) { /* raw inflate */
goto err;
}
/* for now, assume we should check the crc */
- state->dont_check_crc = FALSE;
-#endif
+ state->dont_check_crc = false;
+#endif /* USE_ZLIB_OR_ZLIBNG */
#ifdef HAVE_ZSTD
state->zstd_dctx = ZSTD_createDCtx();
if (state->zstd_dctx == NULL) {
goto err;
}
-#endif
+#endif /* HAVE_ZSTD */
#ifdef USE_LZ4
ret = LZ4F_createDecompressionContext(&state->lz4_dctx, LZ4F_VERSION);
if (LZ4F_isError(ret)) {
goto err;
}
-#endif
+#endif /* USE_LZ4 */
/* return stream */
return state;
err:
-#ifdef HAVE_ZLIB
- inflateEnd(&state->strm);
-#endif
+#ifdef USE_ZLIB_OR_ZLIBNG
+ ZLIB_PREFIX(inflateEnd)(&state->strm);
+#endif /* USE_ZLIB_OR_ZLIBNG */
#ifdef HAVE_ZSTD
ZSTD_freeDCtx(state->zstd_dctx);
-#endif
+#endif /* HAVE_ZSTD */
#ifdef USE_LZ4
LZ4F_freeDecompressionContext(state->lz4_dctx);
-#endif
+#endif /* USE_LZ4 */
g_free(state->out.buf);
g_free(state->in.buf);
g_free(state);
@@ -1241,9 +1827,9 @@ file_open(const char *path)
{
int fd;
FILE_T ft;
-#ifdef HAVE_ZLIB
+#ifdef USE_ZLIB_OR_ZLIBNG
const char *suffixp;
-#endif
+#endif /* USE_ZLIB_OR_ZLIBNG */
/* open file and do correct filename conversions.
@@ -1264,7 +1850,7 @@ file_open(const char *path)
return NULL;
}
-#ifdef HAVE_ZLIB
+#ifdef USE_ZLIB_OR_ZLIBNG
/*
* If this file's name ends in ".caz", it's probably a compressed
* Windows Sniffer file. The compression is gzip, but if we
@@ -1281,24 +1867,24 @@ file_open(const char *path)
suffixp = strrchr(path, '.');
if (suffixp != NULL) {
if (g_ascii_strcasecmp(suffixp, ".caz") == 0)
- ft->dont_check_crc = TRUE;
+ ft->dont_check_crc = true;
}
-#endif
+#endif /* USE_ZLIB_OR_ZLIBNG */
return ft;
}
void
-file_set_random_access(FILE_T stream, gboolean random_flag _U_, GPtrArray *seek)
+file_set_random_access(FILE_T stream, bool random_flag _U_, GPtrArray *seek)
{
stream->fast_seek = seek;
}
-gint64
-file_seek(FILE_T file, gint64 offset, int whence, int *err)
+int64_t
+file_seek(FILE_T file, int64_t offset, int whence, int *err)
{
struct fast_seek_point *here;
- guint n;
+ unsigned n;
if (whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END) {
ws_assert_not_reached();
@@ -1318,7 +1904,7 @@ file_seek(FILE_T file, gint64 offset, int whence, int *err)
XXX - we don't actually use this yet, but, for uncompressed
files, we could optimize it, if desired, by directly using
ws_lseek64(). */
- if (gz_skip(file, G_MAXINT64) == -1) {
+ if (gz_skip(file, INT64_MAX) == -1) {
*err = file->err;
return -1;
}
@@ -1334,7 +1920,7 @@ file_seek(FILE_T file, gint64 offset, int whence, int *err)
which we're skipping; update the offset to include that. */
offset += file->skip;
}
- file->seek_pending = FALSE;
+ file->seek_pending = false;
/*
* Are we moving at all?
@@ -1370,7 +1956,7 @@ file_seek(FILE_T file, gint64 offset, int whence, int *err)
* not at all likely to see files big enough to ever
* see a negative offset that large.
*/
- guint adjustment = (unsigned)(-offset);
+ unsigned adjustment = (unsigned)(-offset);
file->out.avail += adjustment;
file->out.next -= adjustment;
@@ -1389,9 +1975,9 @@ file_seek(FILE_T file, gint64 offset, int whence, int *err)
* Yes. Adjust appropriately.
*
* offset is < an unsigned and thus fits in an unsigned,
- * so we can cast it to guint safely.
+ * so we can cast it to unsigned safely.
*/
- file->out.avail -= (guint)offset;
+ file->out.avail -= (unsigned)offset;
file->out.next += offset;
file->pos += offset;
return file->pos;
@@ -1400,15 +1986,21 @@ file_seek(FILE_T file, gint64 offset, int whence, int *err)
/*
* We're not seeking within the buffer. Do we have "fast seek" data
- * for the location to which we will be seeking, and is the offset
- * outside the span for compressed files or is this an uncompressed
- * file?
+ * for the location to which we will be seeking, and are we either
+ * seeking backwards or is the fast seek point past what is in the
+ * buffer? (We don't want to "fast seek" backwards to a point that
+ * we've already read and buffered if we're actually seeking forwards.)
*
- * XXX, profile
+ * It might in certain cases be faster to continue reading linearly
+ * foward rather than jump to the fast seek point if the distance
+ * to the fast seek point is small, but we might only be able to do that
+ * if the compression context doesn't change (which for LZ4 includes if
+ * we jump to a LZ4 with different options.)
+ * XXX - profile different buffer and SPAN sizes
*/
if ((here = fast_seek_find(file, file->pos + offset)) &&
- (offset < 0 || offset > SPAN || here->compression == UNCOMPRESSED)) {
- gint64 off, off2;
+ (offset < 0 || here->out >= file->pos + file->out.avail)) {
+ int64_t off, off2;
/*
* Yes. Use that data to do the seek.
@@ -1416,22 +2008,43 @@ file_seek(FILE_T file, gint64 offset, int whence, int *err)
* has been called on this file, which should never be the case
* for a pipe.
*/
-#ifdef HAVE_ZLIB
- if (here->compression == ZLIB) {
+ switch (here->compression) {
+
+#ifdef USE_ZLIB_OR_ZLIBNG
+ case ZLIB:
#ifdef HAVE_INFLATEPRIME
off = here->in - (here->data.zlib.bits ? 1 : 0);
-#else
+#else /* HAVE_INFLATEPRIME */
off = here->in;
-#endif
+#endif /* HAVE_INFLATEPRIME */
off2 = here->out;
- } else if (here->compression == GZIP_AFTER_HEADER) {
+ break;
+
+ case GZIP_AFTER_HEADER:
off = here->in;
off2 = here->out;
- } else
-#endif
- {
+ break;
+#endif /* USE_ZLIB_OR_ZLIBNG */
+
+#ifdef USE_LZ4
+ case LZ4:
+ ws_debug("fast seek lz4");
+ off = here->in;
+ off2 = here->out;
+ break;
+#endif /* USE_LZ4 */
+
+ case UNCOMPRESSED:
+ /* In an uncompressed portion, seek directly to the offset */
off2 = (file->pos + offset);
off = here->in + (off2 - here->out);
+ break;
+
+ default:
+ /* Otherwise, seek to the fast seek point to do any needed setup. */
+ off = here->in;
+ off2 = here->out;
+ break;
}
if (ws_lseek64(file->fd, off, SEEK_SET) == -1) {
@@ -1442,17 +2055,18 @@ file_seek(FILE_T file, gint64 offset, int whence, int *err)
file->raw_pos = off;
buf_reset(&file->out);
- file->eof = FALSE;
- file->seek_pending = FALSE;
+ file->eof = false;
+ file->seek_pending = false;
file->err = 0;
file->err_info = NULL;
buf_reset(&file->in);
-#ifdef HAVE_ZLIB
- if (here->compression == ZLIB) {
- z_stream *strm = &file->strm;
+ switch (here->compression) {
- inflateReset(strm);
+#ifdef USE_ZLIB_OR_ZLIBNG
+ case ZLIB: {
+ zlib_stream*strm = &file->strm;
+ ZLIB_PREFIX(inflateReset)(strm);
strm->adler = here->data.zlib.adler;
strm->total_out = here->data.zlib.total_out;
#ifdef HAVE_INFLATEPRIME
@@ -1468,30 +2082,85 @@ file_seek(FILE_T file, gint64 offset, int whence, int *err)
*err = state->err;
return -1;
}
- (void)inflatePrime(strm, here->data.zlib.bits, ret >> (8 - here->data.zlib.bits));
+ (void)ZLIB_PREFIX(inflatePrime)(strm, here->data.zlib.bits, ret >> (8 - here->data.zlib.bits));
}
-#endif
- (void)inflateSetDictionary(strm, here->data.zlib.window, ZLIB_WINSIZE);
+#endif /* HAVE_INFLATEPRIME */
+ (void)ZLIB_PREFIX(inflateSetDictionary)(strm, here->data.zlib.window, ZLIB_WINSIZE);
file->compression = ZLIB;
- } else if (here->compression == GZIP_AFTER_HEADER) {
- z_stream *strm = &file->strm;
+ break;
+ }
- inflateReset(strm);
- strm->adler = crc32(0L, Z_NULL, 0);
+ case GZIP_AFTER_HEADER: {
+ zlib_stream* strm = &file->strm;
+ ZLIB_PREFIX(inflateReset)(strm);
+ strm->adler = ZLIB_PREFIX(crc32)(0L, Z_NULL, 0);
file->compression = ZLIB;
- } else
-#endif
+ break;
+ }
+#endif /* USE_ZLIB_OR_ZLIBNG */
+
+#ifdef USE_LZ4
+ case LZ4:
+ /* If the frame information seems to have changed (i.e., we fast
+ * seeked into a different frame that also has different flags
+ * and options), then reset the context and re-read it.
+ * Unfortunately the API doesn't provide a method to set the
+ * context options explicitly based on an already read
+ * LZ4F_frameInfo_t.
+ */
+ if (memcmp(&file->lz4_info, &here->data.lz4.lz4_info, sizeof(LZ4F_frameInfo_t)) != 0) {
+#if LZ4_VERSION_NUMBER >= 10800
+ LZ4F_resetDecompressionContext(file->lz4_dctx);
+#else /* LZ4_VERSION_NUMBER >= 10800 */
+ LZ4F_freeDecompressionContext(file->lz4_dctx);
+ const LZ4F_errorCode_t ret = LZ4F_createDecompressionContext(&file->lz4_dctx, LZ4F_VERSION);
+ if (LZ4F_isError(ret)) {
+ file->err = WTAP_ERR_INTERNAL;
+ file->err_info = LZ4F_getErrorName(ret);
+ return -1;
+ }
+#endif /* LZ4_VERSION_NUMBER >= 10800 */
+ size_t hdr_size = LZ4F_HEADER_SIZE_MAX;
+ const LZ4F_errorCode_t frame_err = LZ4F_getFrameInfo(file->lz4_dctx, &file->lz4_info, here->data.lz4.lz4_hdr, &hdr_size);
+ if (LZ4F_isError(frame_err)) {
+ file->err = WTAP_ERR_DECOMPRESS;
+ file->err_info = LZ4F_getErrorName(frame_err);
+ return -1;
+ }
+ }
+ file->lz4_info = here->data.lz4.lz4_info;
+ file->compression = LZ4;
+ break;
+#endif /* USE_LZ4 */
+
+#ifdef HAVE_ZSTD
+ case ZSTD:
+ {
+ const size_t ret = ZSTD_initDStream(file->zstd_dctx);
+ if (ZSTD_isError(ret)) {
+ file->err = WTAP_ERR_DECOMPRESS;
+ file->err_info = ZSTD_getErrorName(ret);
+ return -1;
+ }
+ file->compression = ZSTD;
+ break;
+ }
+#endif /* HAVE_ZSTD */
+
+ default:
file->compression = here->compression;
+ break;
+ }
offset = (file->pos + offset) - off2;
file->pos = off2;
- /* g_print("OK! %ld\n", offset); */
+ ws_debug("Fast seek OK! %"PRId64, offset);
if (offset) {
/* Don't skip forward yet, wait until we want to read from
the file; that way, if we do multiple seeks in a row,
all involving forward skips, they will be combined. */
- file->seek_pending = TRUE;
+ file->seek_pending = true;
file->skip = offset;
}
return file->pos + offset;
@@ -1520,8 +2189,8 @@ file_seek(FILE_T file, gint64 offset, int whence, int *err)
}
file->raw_pos += (offset - file->out.avail);
buf_reset(&file->out);
- file->eof = FALSE;
- file->seek_pending = FALSE;
+ file->eof = false;
+ file->seek_pending = false;
file->err = 0;
file->err_info = NULL;
buf_reset(&file->in);
@@ -1563,7 +2232,7 @@ file_seek(FILE_T file, gint64 offset, int whence, int *err)
*
* Skip what's in output buffer (one less gzgetc() check).
*/
- n = (gint64)file->out.avail > offset ? (unsigned)offset : file->out.avail;
+ n = (int64_t)file->out.avail > offset ? (unsigned)offset : file->out.avail;
file->out.avail -= n;
file->out.next += n;
file->pos += n;
@@ -1574,20 +2243,20 @@ file_seek(FILE_T file, gint64 offset, int whence, int *err)
/* Don't skip forward yet, wait until we want to read from
the file; that way, if we do multiple seeks in a row,
all involving forward skips, they will be combined. */
- file->seek_pending = TRUE;
+ file->seek_pending = true;
file->skip = offset;
}
return file->pos + offset;
}
-gint64
+int64_t
file_tell(FILE_T stream)
{
/* return position */
return stream->pos + (stream->seek_pending ? stream->skip : 0);
}
-gint64
+int64_t
file_tell_raw(FILE_T stream)
{
return stream->raw_pos;
@@ -1604,7 +2273,7 @@ file_fstat(FILE_T stream, ws_statb64 *statb, int *err)
return 0;
}
-gboolean
+bool
file_iscompressed(FILE_T stream)
{
return stream->is_compressed;
@@ -1645,7 +2314,7 @@ file_get_compression_type(FILE_T stream)
int
file_read(void *buf, unsigned int len, FILE_T file)
{
- guint got, n;
+ unsigned got, n;
/* if len is zero, avoid unnecessary operations */
if (len == 0)
@@ -1653,7 +2322,7 @@ file_read(void *buf, unsigned int len, FILE_T file)
/* process a skip request */
if (file->seek_pending) {
- file->seek_pending = FALSE;
+ file->seek_pending = false;
if (gz_skip(file, file->skip) == -1)
return -1;
}
@@ -1722,7 +2391,7 @@ file_peekc(FILE_T file)
/* process a skip request */
if (file->seek_pending) {
- file->seek_pending = FALSE;
+ file->seek_pending = false;
if (gz_skip(file, file->skip) == -1)
return -1;
}
@@ -1778,7 +2447,7 @@ file_getc(FILE_T file)
char *
file_getsp(char *buf, int len, FILE_T file)
{
- guint left, n;
+ unsigned left, n;
char *str;
unsigned char *eol;
@@ -1792,7 +2461,7 @@ file_getsp(char *buf, int len, FILE_T file)
/* process a skip request */
if (file->seek_pending) {
- file->seek_pending = FALSE;
+ file->seek_pending = false;
if (gz_skip(file, file->skip) == -1)
return NULL;
}
@@ -1864,7 +2533,7 @@ file_eof(FILE_T file)
* I/O stream. Also returns an error string for some errors.
*/
int
-file_error(FILE_T fh, gchar **err_info)
+file_error(FILE_T fh, char **err_info)
{
if (fh->err!=0 && err_info) {
/* g_strdup() returns NULL for NULL argument */
@@ -1879,7 +2548,7 @@ file_clearerr(FILE_T stream)
/* clear error and end-of-file */
stream->err = 0;
stream->err_info = NULL;
- stream->eof = FALSE;
+ stream->eof = false;
}
void
@@ -1890,15 +2559,15 @@ file_fdclose(FILE_T file)
file->fd = -1;
}
-gboolean
+bool
file_fdreopen(FILE_T file, const char *path)
{
int fd;
if ((fd = ws_open(path, O_RDONLY|O_BINARY, 0000)) == -1)
- return FALSE;
+ return false;
file->fd = fd;
- return TRUE;
+ return true;
}
void
@@ -1908,15 +2577,15 @@ file_close(FILE_T file)
/* free memory and close file */
if (file->size) {
-#ifdef HAVE_ZLIB
- inflateEnd(&(file->strm));
-#endif
+#ifdef USE_ZLIB_OR_ZLIBNG
+ ZLIB_PREFIX(inflateEnd)(&(file->strm));
+#endif /* USE_ZLIB_OR_ZLIBNG */
#ifdef HAVE_ZSTD
ZSTD_freeDCtx(file->zstd_dctx);
-#endif
+#endif /* HAVE_ZSTD */
#ifdef USE_LZ4
LZ4F_freeDecompressionContext(file->lz4_dctx);
-#endif
+#endif /* USE_LZ4 */
g_free(file->out.buf);
g_free(file->in.buf);
}
@@ -1933,13 +2602,13 @@ file_close(FILE_T file)
ws_close(fd);
}
-#ifdef HAVE_ZLIB
+#ifdef USE_ZLIB_OR_ZLIBNG
/* internal gzip file state data structure for writing */
struct wtap_writer {
int fd; /* file descriptor */
- gint64 pos; /* current position in uncompressed data */
- guint size; /* buffer size, zero if not allocated yet */
- guint want; /* requested buffer size, default is GZBUFSIZE */
+ int64_t pos; /* current position in uncompressed data */
+ unsigned size; /* buffer size, zero if not allocated yet */
+ unsigned want; /* requested buffer size, default is GZBUFSIZE */
unsigned char *in; /* input buffer */
unsigned char *out; /* output buffer (double-sized when reading) */
unsigned char *next; /* next output data to deliver or write */
@@ -1948,7 +2617,7 @@ struct wtap_writer {
int err; /* error code */
const char *err_info; /* additional error information string for some errors */
/* zlib deflate stream */
- z_stream strm; /* stream structure in-place (not a pointer) */
+ zlib_stream strm; /* stream structure in-place (not a pointer) */
};
GZWFILE_T
@@ -2003,7 +2672,11 @@ static int
gz_init(GZWFILE_T state)
{
int ret;
+#ifdef HAVE_ZLIBNG
+ zng_streamp strm = &(state->strm);
+#else /* HAVE_ZLIBNG */
z_streamp strm = &(state->strm);
+#endif /* HAVE_ZLIBNG */
/* allocate input and output buffers */
state->in = (unsigned char *)g_try_malloc(state->want);
@@ -2019,7 +2692,7 @@ gz_init(GZWFILE_T state)
strm->zalloc = Z_NULL;
strm->zfree = Z_NULL;
strm->opaque = Z_NULL;
- ret = deflateInit2(strm, state->level, Z_DEFLATED,
+ ret = ZLIB_PREFIX(deflateInit2)(strm, state->level, Z_DEFLATED,
15 + 16, 8, state->strategy);
if (ret != Z_OK) {
g_free(state->out);
@@ -2056,8 +2729,11 @@ gz_comp(GZWFILE_T state, int flush)
int ret;
ssize_t got;
ptrdiff_t have;
+#ifdef HAVE_ZLIBNG
+ zng_streamp strm = &(state->strm);
+#else /* HAVE_ZLIBNG */
z_streamp strm = &(state->strm);
-
+#endif /* HAVE_ZLIBNG */
/* allocate memory if this is the first time through */
if (state->size == 0 && gz_init(state) == -1)
return -1;
@@ -2090,7 +2766,7 @@ gz_comp(GZWFILE_T state, int flush)
/* compress */
have = strm->avail_out;
- ret = deflate(strm, flush);
+ ret = ZLIB_PREFIX(deflate)(strm, flush);
if (ret == Z_STREAM_ERROR) {
/* This "shouldn't happen". */
state->err = WTAP_ERR_INTERNAL;
@@ -2102,7 +2778,7 @@ gz_comp(GZWFILE_T state, int flush)
/* if that completed a deflate stream, allow another to start */
if (flush == Z_FINISH)
- deflateReset(strm);
+ ZLIB_PREFIX(deflateReset)(strm);
/* all done, no errors */
return 0;
@@ -2112,11 +2788,15 @@ gz_comp(GZWFILE_T state, int flush)
failure or on an attempt to write 0 bytes (in which case state->err
is Z_OK); return the number of bytes written on success. */
unsigned
-gzwfile_write(GZWFILE_T state, const void *buf, guint len)
+gzwfile_write(GZWFILE_T state, const void *buf, unsigned len)
{
- guint put = len;
- guint n;
+ unsigned put = len;
+ unsigned n;
+#ifdef HAVE_ZLIBNG
+ zng_streamp strm;
+#else /* HAVE_ZLIBNG */
z_streamp strm;
+#endif /* HAVE_ZLIBNG */
strm = &(state->strm);
@@ -2145,9 +2825,9 @@ gzwfile_write(GZWFILE_T state, const void *buf, guint len)
DIAG_OFF(cast-qual)
memcpy((Bytef *)strm->next_in + strm->avail_in, buf, n);
DIAG_ON(cast-qual)
-#else
+#else /* z_const */
memcpy(strm->next_in + strm->avail_in, buf, n);
-#endif
+#endif /* z_const */
strm->avail_in += n;
state->pos += n;
buf = (const char *)buf + n;
@@ -2165,11 +2845,11 @@ DIAG_ON(cast-qual)
strm->avail_in = len;
#ifdef z_const
strm->next_in = (z_const Bytef *)buf;
-#else
+#else /* z_const */
DIAG_OFF(cast-qual)
strm->next_in = (Bytef *)buf;
DIAG_ON(cast-qual)
-#endif
+#endif /* z_const */
state->pos += len;
if (gz_comp(state, Z_NO_FLUSH) == -1)
return 0;
@@ -2205,7 +2885,7 @@ gzwfile_close(GZWFILE_T state)
/* flush, free memory, and close file */
if (gz_comp(state, Z_FINISH) == -1)
ret = state->err;
- (void)deflateEnd(&(state->strm));
+ (void)ZLIB_PREFIX(deflateEnd)(&(state->strm));
g_free(state->out);
g_free(state->in);
state->err = Z_OK;
@@ -2220,8 +2900,242 @@ gzwfile_geterr(GZWFILE_T state)
{
return state->err;
}
-#endif
+#endif /* USE_ZLIB_OR_ZLIBNG */
+
+#ifdef USE_LZ4
+/* internal lz4 file state data structure for writing */
+struct lz4_writer {
+ int fd; /* file descriptor */
+ int64_t pos; /* current position in uncompressed data */
+ int64_t pos_out;
+ size_t size_out; /* buffer size, zero if not allocated yet */
+ size_t want; /* requested buffer size, default is LZ4BUFSIZE */
+ size_t want_out; /* requested output buffer size, determined from want */
+ unsigned char *out; /* output buffer, containing uncompressed data */
+ int err; /* error code */
+ const char *err_info; /* additional error information string for some errors */
+ LZ4F_preferences_t lz4_prefs;
+ LZ4F_cctx *lz4_cctx;
+};
+
+LZ4WFILE_T
+lz4wfile_open(const char *path)
+{
+ int fd;
+ LZ4WFILE_T state;
+ int save_errno;
+
+ fd = ws_open(path, O_BINARY|O_WRONLY|O_CREAT|O_TRUNC, 0666);
+ if (fd == -1)
+ return NULL;
+ state = lz4wfile_fdopen(fd);
+ if (state == NULL) {
+ save_errno = errno;
+ ws_close(fd);
+ errno = save_errno;
+ }
+ return state;
+}
+
+LZ4WFILE_T
+lz4wfile_fdopen(int fd)
+{
+ LZ4WFILE_T state;
+
+ /* allocate wtap_writer structure to return */
+ state = (LZ4WFILE_T)g_try_malloc(sizeof *state);
+ if (state == NULL)
+ return NULL;
+ state->fd = fd;
+ state->size_out = 0; /* no buffer allocated yet */
+ state->want = LZ4BUFSIZE; /* max input size (a block) */
+ state->want_out = LZ4F_compressBound(state->want, &state->lz4_prefs);
+ /*
+ * This size guarantees that we will always have enough room to
+ * write the result of LZ4F_compressUpdate (or Flush or End),
+ * so long as the output buffer is empty (i.e., we immediately
+ * write to the output file anything the compressor hands back
+ * instead of buffering.)
+ */
+
+ memset(&state->lz4_prefs, 0, sizeof(LZ4F_preferences_t));
+ /* Use the same prefs as the lz4 command line utility defaults. */
+ state->lz4_prefs.frameInfo.blockMode = LZ4F_blockIndependent; /* Allows fast seek */
+ state->lz4_prefs.frameInfo.contentChecksumFlag = 1;
+ state->lz4_prefs.frameInfo.blockSizeID = LZ4F_max4MB;
+ /* XXX - What should we set state->lz4_prefs.compressionLevel to?
+ * The command line utility uses 1, recommends 9 as another option, and
+ * also there's 12 (max).
+ *
+ * We could provide an API call or perhaps two or three preset options.
+ */
+ state->lz4_prefs.compressionLevel = 1;
+
+ /* initialize stream */
+ state->err = 0; /* clear error */
+ state->err_info = NULL; /* clear additional error information */
+ state->pos = 0; /* no uncompressed data yet */
+ state->pos_out = 0;
+
+ /* return stream */
+ return state;
+}
+
+/* Writes len bytes from the output buffer to the file.
+ * Return true on success; returns false and sets state->err on failure.
+ */
+static bool
+lz4_write_out(LZ4WFILE_T state, size_t len)
+{
+ if (len > 0) {
+ ssize_t got = ws_write(state->fd, state->out, len);
+ if (got < 0) {
+ state->err = errno;
+ return false;
+ }
+ if ((unsigned)got != len) {
+ state->err = WTAP_ERR_SHORT_WRITE;
+ return false;
+ }
+ state->pos_out += got;
+ }
+ return true;
+}
+
+/* Initialize state for writing an lz4 file. Mark initialization by setting
+ state->size to non-zero. Return -1, and set state->err and possibly
+ state->err_info, on failure; return 0 on success. */
+static int
+lz4_init(LZ4WFILE_T state)
+{
+ LZ4F_errorCode_t ret;
+
+ /* create Compression context */
+ ret = LZ4F_createCompressionContext(&state->lz4_cctx, LZ4F_VERSION);
+ if (LZ4F_isError(ret)) {
+ state->err = WTAP_ERR_CANT_WRITE; // XXX - WTAP_ERR_COMPRESS?
+ state->err_info = LZ4F_getErrorName(ret);
+ return -1;
+ }
+
+ /* allocate buffer */
+ state->out = (unsigned char *)g_try_malloc(state->want_out);
+ if (state->out == NULL) {
+ g_free(state->out);
+ LZ4F_freeCompressionContext(state->lz4_cctx);
+ state->err = ENOMEM;
+ return -1;
+ }
+
+ ret = LZ4F_compressBegin(state->lz4_cctx, state->out, state->want_out, &state->lz4_prefs);
+ if (LZ4F_isError(ret)) {
+ state->err = WTAP_ERR_CANT_WRITE; // XXX - WTAP_ERR_COMPRESS?
+ state->err_info = LZ4F_getErrorName(ret);
+ return -1;
+ }
+ if (!lz4_write_out(state, ret)) {
+ return -1;
+ }
+
+ /* mark state as initialized */
+ state->size_out = state->want_out;
+
+ return 0;
+}
+
+/* Write out len bytes from buf. Return 0, and set state->err, on
+ failure or on an attempt to write 0 bytes (in which case state->err
+ is 0); return the number of bytes written on success. */
+size_t
+lz4wfile_write(LZ4WFILE_T state, const void *buf, size_t len)
+{
+ size_t to_write;
+ size_t put = len;
+ /* check that there's no error */
+ if (state->err != 0)
+ return 0;
+
+ /* if len is zero, avoid unnecessary operations */
+ if (len == 0)
+ return 0;
+
+ /* allocate memory if this is the first time through */
+ if (state->size_out == 0 && lz4_init(state) == -1)
+ return 0;
+
+ do {
+ to_write = MIN(len, state->want);
+ size_t bytesWritten = LZ4F_compressUpdate(state->lz4_cctx, state->out, state->size_out,
+ buf, to_write, NULL);
+ if (LZ4F_isError(bytesWritten)) {
+ state->err = WTAP_ERR_CANT_WRITE; // XXX - WTAP_ERR_COMPRESS?
+ state->err_info = LZ4F_getErrorName(bytesWritten);
+ return 0;
+ }
+ if (!lz4_write_out(state, bytesWritten)) {
+ return 0;
+ }
+ state->pos += to_write;
+ len -= to_write;
+ } while (len);
+
+ /* input was all buffered or compressed */
+ return put;
+}
+
+/* Flush out what we've written so far. Returns -1, and sets state->err,
+ on failure; returns 0 on success. */
+int
+lz4wfile_flush(LZ4WFILE_T state)
+{
+ size_t bytesWritten;
+ /* check that there's no error */
+ if (state->err != 0)
+ return -1;
+
+ bytesWritten = LZ4F_flush(state->lz4_cctx, state->out, state->size_out, NULL);
+ if (LZ4F_isError(bytesWritten)) {
+ // Should never happen if size_out >= LZ4F_compressBound(0, prefsPtr)
+ state->err = WTAP_ERR_INTERNAL;
+ return -1;
+ }
+ if (!lz4_write_out(state, bytesWritten)) {
+ return -1;
+ }
+ return 0;
+}
+
+/* Flush out all data written, and close the file. Returns a Wiretap
+ error on failure; returns 0 on success. */
+int
+lz4wfile_close(LZ4WFILE_T state)
+{
+ int ret = 0;
+
+ /* flush, free memory, and close file */
+ size_t bytesWritten = LZ4F_compressEnd(state->lz4_cctx, state->out, state->size_out, NULL);
+ if (LZ4F_isError(bytesWritten)) {
+ // Should never happen if size_out >= LZ4F_compressBound(0, prefsPtr)
+ ret = WTAP_ERR_INTERNAL;
+ }
+ if (!lz4_write_out(state, bytesWritten)) {
+ ret = state->err;
+ }
+ g_free(state->out);
+ LZ4F_freeCompressionContext(state->lz4_cctx);
+ if (ws_close(state->fd) == -1 && ret == 0)
+ ret = errno;
+ g_free(state);
+ return ret;
+}
+
+int
+lz4wfile_geterr(LZ4WFILE_T state)
+{
+ return state->err;
+}
+#endif /* USE_LZ4 */
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*