diff options
author | Guy Harris <guy@alum.mit.edu> | 2011-04-08 00:28:37 +0000 |
---|---|---|
committer | Guy Harris <guy@alum.mit.edu> | 2011-04-08 00:28:37 +0000 |
commit | ab261a32813b2bd6b5c12fc081aed71bc5b0c833 (patch) | |
tree | 19edd070267f48bb82aa4b554762f918be830f26 | |
parent | f989235a87861ca214adcf66b880d460fd4104fe (diff) | |
download | wireshark-ab261a32813b2bd6b5c12fc081aed71bc5b0c833.tar.gz |
From Jakub Zawadzki:
Steal file_wrappers functions from zlib v2.
svn path=/trunk/; revision=36513
-rw-r--r-- | wiretap/file_access.c | 16 | ||||
-rw-r--r-- | wiretap/file_wrappers.c | 816 | ||||
-rw-r--r-- | wiretap/file_wrappers.h | 37 | ||||
-rw-r--r-- | wiretap/wtap-int.h | 33 | ||||
-rw-r--r-- | wiretap/wtap.c | 2 |
5 files changed, 675 insertions, 229 deletions
diff --git a/wiretap/file_access.c b/wiretap/file_access.c index 60a4eac110..9ac254a27f 100644 --- a/wiretap/file_access.c +++ b/wiretap/file_access.c @@ -768,15 +768,15 @@ static wtap_dumper* wtap_dump_alloc_wdh(int filetype, int encap, int snaplen, gboolean compressed, int *err); static gboolean wtap_dump_open_finish(wtap_dumper *wdh, int filetype, gboolean compressed, int *err); -static FILE_T wtap_dump_file_open(wtap_dumper *wdh, const char *filename); -static FILE_T wtap_dump_file_fdopen(wtap_dumper *wdh, int fd); +static WFILE_T wtap_dump_file_open(wtap_dumper *wdh, const char *filename); +static WFILE_T wtap_dump_file_fdopen(wtap_dumper *wdh, int fd); static int wtap_dump_file_close(wtap_dumper *wdh); wtap_dumper* wtap_dump_open(const char *filename, int filetype, int encap, int snaplen, gboolean compressed, int *err) { wtap_dumper *wdh; - FILE_T fh; + WFILE_T fh; /* Check whether we can open a capture file with that file type and that encapsulation. */ @@ -828,7 +828,7 @@ wtap_dumper* wtap_dump_fdopen(int fd, int filetype, int encap, int snaplen, gboolean compressed, int *err) { wtap_dumper *wdh; - FILE_T fh; + WFILE_T fh; /* Check whether we can open a capture file with that file type and that encapsulation. */ @@ -1010,7 +1010,7 @@ gboolean wtap_dump_set_addrinfo_list(wtap_dumper *wdh, struct addrinfo *addrinfo /* internally open a file for writing (compressed or not) */ #ifdef HAVE_LIBZ -static FILE_T wtap_dump_file_open(wtap_dumper *wdh, const char *filename) +static WFILE_T wtap_dump_file_open(wtap_dumper *wdh, const char *filename) { if(wdh->compressed) { return gzopen(filename, "wb"); @@ -1019,7 +1019,7 @@ static FILE_T wtap_dump_file_open(wtap_dumper *wdh, const char *filename) } } #else -static FILE_T wtap_dump_file_open(wtap_dumper *wdh _U_, const char *filename) +static WFILE_T wtap_dump_file_open(wtap_dumper *wdh _U_, const char *filename) { return ws_fopen(filename, "wb"); } @@ -1027,7 +1027,7 @@ static FILE_T wtap_dump_file_open(wtap_dumper *wdh _U_, const char *filename) /* internally open a file for writing (compressed or not) */ #ifdef HAVE_LIBZ -static FILE_T wtap_dump_file_fdopen(wtap_dumper *wdh, int fd) +static WFILE_T wtap_dump_file_fdopen(wtap_dumper *wdh, int fd) { if(wdh->compressed) { return gzdopen(fd, "wb"); @@ -1036,7 +1036,7 @@ static FILE_T wtap_dump_file_fdopen(wtap_dumper *wdh, int fd) } } #else -static FILE_T wtap_dump_file_fdopen(wtap_dumper *wdh _U_, int fd) +static WFILE_T wtap_dump_file_fdopen(wtap_dumper *wdh _U_, int fd) { return fdopen(fd, "wb"); } diff --git a/wiretap/file_wrappers.c b/wiretap/file_wrappers.c index b8682ff205..fce6d1b9a7 100644 --- a/wiretap/file_wrappers.c +++ b/wiretap/file_wrappers.c @@ -20,111 +20,411 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ + +/* file_access interface based heavily on zlib gzread.c and gzlib.c from zlib + * Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler + * under licence: + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. +*/ + #ifdef HAVE_CONFIG_H #include "config.h" #endif -/* - * Do this now, to get close() defined, before we muck with the definition - * of HAVE_UNISTD_H. See below for the full sad story of why we do that. - */ #ifdef HAVE_UNISTD_H #include <unistd.h> #endif /* HAVE_UNISTD_H */ -/* - * OK, now this is tricky. - * - * At least on FreeBSD 3.2, "/usr/include/zlib.h" includes - * "/usr/include/zconf.h", which, if HAVE_UNISTD_H is defined, - * #defines "z_off_t" to be "off_t", and if HAVE_UNISTD_H is - * not defined, #defines "z_off_t" to be "long" if it's not - * already #defined. - * - * In 4.4-Lite-derived systems such as FreeBSD, "off_t" is - * "long long int", not "long int", so the definition of "z_off_t" - - * and therefore the types of the arguments to routines such as - * "gzseek()", as declared, with prototypes, in "zlib.h" - depends - * on whether HAVE_UNISTD_H is defined prior to including "zlib.h"! - * - * It's not defined in the FreeBSD 3.2 "zlib", so if we include "zlib.h" - * after defining HAVE_UNISTD_H, we get a misdeclaration of "gzseek()", - * and, if we're building with "zlib" support, anything that seeks - * on a file may not work. - * - * Other BSDs may have the same problem, if they haven't done something - * such as defining HAVE_UNISTD_H in "zconf.h". - * - * "config.h" defines HAVE_UNISTD_H, on all systems that have it, and all - * 4.4-Lite-derived BSDs have it. Therefore, given that "zlib.h" is included - * by "file_wrappers.h", that means that unless we include "zlib.h" before - * we include "config.h", we get a misdeclaration of "gzseek()". - * - * Unfortunately, it's "config.h" that tells us whether we have "zlib" - * in the first place, so we don't know whether to include "zlib.h" - * until we include "config.h".... - * - * A similar problem appears to occur with "gztell()", at least on - * NetBSD. - * - * To add further complication, on recent versions, at least, of OpenBSD, - * the Makefile for zlib defines HAVE_UNISTD_H. - * - * So what we do is, on all OSes other than OpenBSD, *undefine* HAVE_UNISTD_H - * before including "wtap-int.h" (it handles including "zlib.h" if HAVE_ZLIB - * is defined, and it includes "wtap.h", which we include to get the - * WTAP_ERR_ZLIB values), and, if we have zlib, make "file_seek()" and - * "file_tell()" subroutines, so that the only calls to "gzseek()" and - * "gztell()" are in this file, which, by dint of the hackery described - * above, manages to correctly declare "gzseek()" and "gztell()". - * - * On OpenBSD, we forcibly *define* HAVE_UNISTD_H if it's not defined. - * - * Hopefully, the BSDs will, over time, remove the test for HAVE_UNISTD_H - * from "zconf.h", so that "gzseek()" and "gztell()" will be declared - * with the correct signature regardless of whether HAVE_UNISTD_H is - * defined, so that if they change the signature we don't have to worry - * about making sure it's defined or not defined. - * - * DO NOT, UNDER ANY CIRCUMSTANCES, REMOVE THE FOLLOWING LINES, OR MOVE - * THEM AFTER THE INCLUDE OF "wtap-int.h"! Doing so will cause any program - * using Wiretap to read capture files to fail miserably on a FreeBSD - * 3.2 or 3.3 system - and possibly some other BSD systems - if zlib is - * installed. If you *must* have HAVE_UNISTD_H defined before including - * "wtap-int.h", put "file_error()" into a file by itself, which can - * cheerfully include "wtap.h" and get "gzseek()" misdeclared, and include - * just "zlib.h" in this file - *after* undefining HAVE_UNISTD_H. - * - * XXX - this is not working with zlib 1.2.4 and, at least on some non- - * 4.4-Lite-derived systems (such as some Linux distributions), with - * some earlier versions of zlib. Perhaps the configure script should - * check whether off_t is long or not and, if it is, just trust zlib - * to work without this mess. That won't help with zlib 1.2.4, as - * if you build and install that on OS X, which *is* 4.4-Lite-derived, - * it still doesn't work right. - */ -#ifdef __OpenBSD__ -#ifndef HAVE_UNISTD_H -#define HAVE_UNISTD_H -#endif /* HAVE_UNISTD_H */ -#else /* __OpenBSD__ */ -#undef HAVE_UNISTD_H -#endif /* __OpenBSD__ */ - #include <errno.h> #include <stdio.h> -#ifdef HAVE_LIBZ #ifdef HAVE_FCNTL_H #include <fcntl.h> #endif /* HAVE_FCNTL_H */ #include <string.h> -#endif /* HAVE_LIBZ */ #include "wtap-int.h" #include "file_wrappers.h" #include <wsutil/file_util.h> +/* #define GZBUFSIZE 8192 */ +#define GZBUFSIZE 4096 + +/* values for gz_state compression */ +#define UNKNOWN 0 /* look for a gzip header */ +#define UNCOMPRESSED 1 /* copy input directly */ + +#ifdef HAVE_LIBZ +#define ZLIB 2 /* decompress a zlib stream */ +#endif + +/* XXX, lseek64() instead of ws_lseek()? */ + +static int /* gz_load */ +raw_read(FILE_T state, unsigned char *buf, unsigned int count, unsigned *have) +{ + int ret; + + *have = 0; + do { + ret = read(state->fd, buf + *have, count - *have); + if (ret <= 0) + break; + *have += ret; + } while (*have < count); + if (ret < 0) { + state->err = errno; + return -1; + } + if (ret == 0) + state->eof = 1; + return 0; +} + +static int /* gz_avail */ +fill_in_buffer(FILE_T state) +{ + if (state->err) + return -1; + if (state->eof == 0) { + if (raw_read(state, state->in, state->size, (unsigned *)&(state->avail_in)) == -1) + return -1; + state->next_in = state->in; + } + return 0; +} + +#ifdef HAVE_LIBZ + +/* Get next byte from input, or -1 if end or error. */ +#define NEXT() ((state->avail_in == 0 && fill_in_buffer(state) == -1) ? -1 : \ + (state->avail_in == 0 ? -1 : \ + (state->avail_in--, *(state->next_in)++))) + +/* Get a four-byte little-endian integer and return 0 on success and the value + in *ret. Otherwise -1 is returned and *ret is not modified. */ +static int +gz_next4(FILE_T state, guint32 *ret) +{ + guint32 val; + int ch; + + val = NEXT(); + val += (unsigned)NEXT() << 8; + val += (guint32)NEXT() << 16; + ch = NEXT(); + if (ch == -1) + return -1; + val += (guint32)ch << 24; + *ret = val; + return 0; +} + +static int /* gz_decomp */ +zlib_read(FILE_T state, unsigned char *buf, unsigned int count) +{ + int ret; + guint32 crc, len; + z_streamp strm = &(state->strm); + + strm->avail_out = count; + strm->next_out = buf; + + /* fill output buffer up to end of deflate stream */ + do { + /* get more input for inflate() */ + if (state->avail_in == 0 && fill_in_buffer(state) == -1) + return -1; + if (state->avail_in == 0) { + state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR; + return -1; + } + + strm->avail_in = state->avail_in; + strm->next_in = state->next_in; + /* decompress and handle errors */ + ret = inflate(strm, Z_NO_FLUSH); + state->avail_in = strm->avail_in; + state->next_in = strm->next_in; + if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) { + state->err = WTAP_ERR_ZLIB + Z_STREAM_ERROR; + return -1; + } + if (ret == Z_MEM_ERROR) { + state->err = WTAP_ERR_ZLIB + Z_MEM_ERROR; /* ENOMEM? */ + return -1; + } + if (ret == Z_DATA_ERROR) { /* deflate stream invalid */ + state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR; + return -1; + } + } while (strm->avail_out && ret != Z_STREAM_END); + + /* update available output and crc check value */ + state->next = buf; + state->have = count - strm->avail_out; + strm->adler = crc32(strm->adler, state->next, state->have); + + /* check gzip trailer if at end of deflate stream */ + if (ret == Z_STREAM_END) { + if (gz_next4(state, &crc) == -1 || gz_next4(state, &len) == -1) { + state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR; + return -1; + } + if (crc != strm->adler) { + state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR; + return -1; + } + if (len != (strm->total_out & 0xffffffffL)) { + state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR; + return -1; + } + state->compression = UNKNOWN; /* ready for next stream, once have is 0 */ + } + + /* good decompression */ + return 0; +} +#endif + +static int +gz_head(FILE_T state) +{ + /* get some data in the input buffer */ + if (state->avail_in == 0) { + if (fill_in_buffer(state) == -1) + return -1; + if (state->avail_in == 0) + return 0; + } + + /* look for the gzip magic header bytes 31 and 139 */ +#ifdef HAVE_LIBZ + if (state->next_in[0] == 31) { + state->avail_in--; + state->next_in++; + if (state->avail_in == 0 && fill_in_buffer(state) == -1) + return -1; + if (state->avail_in && state->next_in[0] == 139) { + unsigned len; + int flags; + + /* we have a gzip header, woo hoo! */ + state->avail_in--; + state->next_in++; + + /* skip rest of header */ + if (NEXT() != 8) { /* compression method */ + state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR; + return -1; + } + flags = NEXT(); + if (flags & 0xe0) { /* reserved flag bits */ + state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR; + return -1; + } + NEXT(); /* modification time */ + NEXT(); + NEXT(); + NEXT(); + NEXT(); /* extra flags */ + NEXT(); /* operating system */ + if (flags & 4) { /* extra field */ + len = (unsigned)NEXT(); + len += (unsigned)NEXT() << 8; + while (len--) + if (NEXT() < 0) + break; + } + if (flags & 8) /* file name */ + while (NEXT() > 0) + ; + if (flags & 16) /* comment */ + while (NEXT() > 0) + ; + if (flags & 2) { /* header crc */ + NEXT(); + NEXT(); + } + /* an unexpected end of file is not checked for here -- it will be + noticed on the first request for uncompressed data */ + + /* set up for decompression */ + inflateReset(&(state->strm)); + state->strm.adler = crc32(0L, Z_NULL, 0); + state->compression = ZLIB; + return 0; + } + else { + /* not a gzip file -- save first byte (31) and fall to raw i/o */ + state->out[0] = 31; + state->have = 1; + } + } +#endif +#ifdef HAVE_LIBXZ + /* { 0xFD, '7', 'z', 'X', 'Z', 0x00 } */ + /* FD 37 7A 58 5A 00 */ +#endif + + /* 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 + the input buffer, which also assures space for gzungetc() */ + state->raw = state->pos; + state->next = state->out; + if (state->avail_in) { + memcpy(state->next + state->have, state->next_in, state->avail_in); + state->have += state->avail_in; + state->avail_in = 0; + } + state->compression = UNCOMPRESSED; + return 0; +} + +static int /* gz_make */ +fill_out_buffer(FILE_T state) +{ + if (state->compression == UNKNOWN) { /* look for gzip header */ + if (gz_head(state) == -1) + return -1; + if (state->have) /* got some data from gz_head() */ + return 0; + } + if (state->compression == UNCOMPRESSED) { /* straight copy */ + if (raw_read(state, state->out, state->size /* << 1 */, &(state->have)) == -1) + return -1; + state->next = state->out; + } +#ifdef HAVE_LIBZ + else if (state->compression == ZLIB) { /* decompress */ + if (zlib_read(state, state->out, state->size << 1) == -1) + return -1; + } +#endif + return 0; +} + +static int +gz_skip(FILE_T state, gint64 len) +{ + unsigned n; + + /* skip over len bytes or reach end-of-file, whichever comes first */ + while (len) + /* skip over whatever is in output buffer */ + if (state->have) { + n = (gint64)state->have > len ? (unsigned)len : state->have; + state->have -= n; + state->next += n; + state->pos += n; + len -= n; + } + + /* output buffer empty -- return if we're at the end of the input */ + else if (state->eof && state->avail_in == 0) + break; + + /* need more data to skip -- load up output buffer */ + else { + /* get more output, looking for header if required */ + if (fill_out_buffer(state) == -1) + return -1; + } + return 0; +} + +static void +gz_reset(FILE_T state) +{ + state->have = 0; /* no output data available */ + state->eof = 0; /* not at end of file */ + state->compression = UNKNOWN; /* look for gzip header */ + + state->seek = 0; /* no seek request pending */ + state->err = 0; /* clear error */ + state->pos = 0; /* no uncompressed data yet */ + state->avail_in = 0; /* no input data yet */ +} + +FILE_T +filed_open(int fd) +{ +#ifdef _STATBUF_ST_BLKSIZE /* XXX, _STATBUF_ST_BLKSIZE portable? */ + struct stat st; +#endif + int want = GZBUFSIZE; + FILE_T state; + + if (fd == -1) + return NULL; + + /* allocate gzFile structure to return */ + state = g_try_malloc(sizeof(wtap_reader)); + if (state == NULL) + return NULL; + + /* open the file with the appropriate mode (or just use fd) */ + state->fd = fd; + + /* save the current position for rewinding (only if reading) */ + state->start = ws_lseek(state->fd, 0, SEEK_CUR); + if (state->start == -1) state->start = 0; + + /* initialize stream */ + gz_reset(state); + +#ifdef _STATBUF_ST_BLKSIZE + if (fstat(fd, &st) >= 0) { + want = st.st_blksize; + /* XXX, verify result? */ + } +#endif + + /* allocate buffers */ + state->in = g_try_malloc(want); + state->out = g_try_malloc(want << 1); + state->size = want; + if (state->in == NULL || state->out == NULL) { + g_free(state->out); + g_free(state->in); + g_free(state); + errno = ENOMEM; + return NULL; + } #ifdef HAVE_LIBZ + /* 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 */ + g_free(state->out); + g_free(state->in); + g_free(state); + errno = ENOMEM; + return NULL; + } +#endif + gz_head(state); /* read first chunk */ + + /* return stream */ + return state; +} FILE_T file_open(const char *path) @@ -133,16 +433,18 @@ file_open(const char *path) FILE_T ft; int oflag; + /* O_LARGEFILE? */ oflag = O_RDONLY; #ifdef _WIN32 oflag |= O_BINARY; #endif + /* open file and do correct filename conversions */ if ((fd = ws_open(path, oflag, 0666)) == -1) return NULL; - /* open zlib file handle */ - ft = gzdopen(fd, "rb"); + /* open file handle */ + ft = filed_open(fd); if (ft == NULL) { ws_close(fd); return NULL; @@ -152,136 +454,272 @@ file_open(const char *path) } gint64 -file_seek(void *stream, gint64 offset, int whence, int *err) +file_seek(FILE_T file, gint64 offset, int whence, int *err) { - gint64 ret; - - /* XXX - z_off_t is usually long, won't work >= 2GB! */ - ret = (gint64) gzseek(stream, (z_off_t)offset, whence); - if (ret == -1) { - /* - * XXX - "gzseek()", as of zlib 1.1.4, doesn't set - * "z_err" for the stream, so "gzerror()" could return - * a bogus Z_OK. - * - * As this call failed, we know "gzerror()" shouldn't - * return Z_OK; if it does, we assume that "errno" is - * the real error. - */ - *err = file_error(stream); - if (*err == 0) + unsigned n; + + /* check that there's no error */ + if (file->err) { + *err = file->err; + return -1; + } + + /* can only seek from start or relative to current position */ + if (whence != SEEK_SET && whence != SEEK_CUR) { + g_assert_not_reached(); +/* + *err = EINVAL; + return -1; + */ + } + + /* normalize offset to a SEEK_CUR specification */ + if (whence == SEEK_SET) + offset -= file->pos; + else if (file->seek) + offset += file->skip; + file->seek = 0; + + /* if within raw area while reading, just go there */ + if (file->compression == UNCOMPRESSED && file->pos + offset >= file->raw) { + if (ws_lseek(file->fd, offset - file->have, SEEK_CUR) == -1) { + *err = errno; + return -1; + } + file->have = 0; + file->eof = 0; + file->seek = 0; + file->err = 0; + file->avail_in = 0; + file->pos += offset; + return file->pos; + } + + /* calculate skip amount, rewinding if needed for back seek when reading */ + if (offset < 0) { + offset += file->pos; + if (offset < 0) { /* before start of file! */ + /* *err = ???; */ + return -1; + } + /* rewind, then skip to offset */ + + /* back up and start over */ + if (ws_lseek(file->fd, file->start, SEEK_SET) == -1) { *err = errno; + return -1; + } + gz_reset(file); } - return ret; + + /* skip what's in output buffer (one less gzgetc() check) */ + n = (gint64)file->have > offset ? (unsigned)offset : file->have; + file->have -= n; + file->next += n; + file->pos += n; + offset -= n; + + /* request skip (if not zero) */ + if (offset) { + file->seek = 1; + file->skip = offset; + } + return file->pos + offset; } gint64 -file_tell(void *stream) +file_tell(FILE_T stream) { - /* XXX - z_off_t is usually long, won't work >= 2GB! */ - return (gint64)gztell(stream); + /* return position */ + return stream->pos + (stream->seek ? stream->skip : 0); } -/* - * Routine to return a Wiretap error code (0 for no error, an errno - * for a file error, or a WTAP_ERR_ code for other errors) for an - * I/O stream. - */ -int -file_error(void *fh) +int +file_read(void *buf, unsigned int len, FILE_T file) { - int errnum; + unsigned got, n; - gzerror(fh, &errnum); - switch (errnum) { + /* check that we're reading and that there's no error */ + if (file->err) + return -1; - case Z_OK: /* no error */ + /* if len is zero, avoid unnecessary operations */ + if (len == 0) return 0; - case Z_STREAM_END: /* EOF - not an error */ - return 0; + /* process a skip request */ + if (file->seek) { + file->seek = 0; + if (gz_skip(file, file->skip) == -1) + return -1; + } - case Z_ERRNO: /* file I/O error */ - return errno; + /* get len bytes to buf, or less than len if at the end */ + got = 0; + do { + /* first just try copying data from the output buffer */ + if (file->have) { + n = file->have > len ? len : file->have; + memcpy(buf, file->next, n); + file->next += n; + file->have -= n; + } - default: - return WTAP_ERR_ZLIB + errnum; - } + /* output buffer empty -- return if we're at the end of the input */ + else if (file->eof && file->avail_in == 0) + break; + + /* need output data -- for small len or new stream load up our output buffer */ + else if (file->compression == UNKNOWN || len < (file->size << 1)) { + /* get more output, looking for header if required */ + if (fill_out_buffer(file) == -1) + return -1; + continue; /* no progress yet -- go back to memcpy() above */ + + } else if (file->compression == UNCOMPRESSED) { /* large len -- read directly into user buffer */ + if (raw_read(file, buf, len, &n) == -1) + return -1; + } +#ifdef HAVE_LIBZ + /* large len -- decompress directly into user buffer */ + else { /* file->compression == ZLIB */ + if (zlib_read(file, buf, len) == -1) + return -1; + n = file->have; + file->have = 0; + } +#endif + /* update progress */ + len -= n; + buf = (char *)buf + n; + got += n; + file->pos += n; + } while (len); + + return (int)got; } -#else /* HAVE_LIBZ */ +int +file_getc(FILE_T file) +{ + unsigned char buf[1]; + int ret; -gint64 -file_seek(void *stream, gint64 offset, int whence, int *err) + /* check that we're reading and that there's no error */ + if (file->err) + return -1; + + /* try output buffer (no need to check for skip request) */ + if (file->have) { + file->have--; + file->pos++; + return *(file->next)++; + } + + ret = file_read(buf, 1, file); + return ret < 1 ? -1 : buf[0]; +} + +char * +file_gets(char *buf, int len, FILE_T file) { - gint64 ret; -#ifdef _WIN32 - gint64 pos; -#endif + unsigned left, n; + char *str; + unsigned char *eol; -#ifdef _WIN32 - /* Win32 version using fsetpos/fgetpos */ - /* XXX - using fsetpos/fgetpos this way is UNDOCUMENTED, but I don't see a any better way :-( */ - /* _lseeki64(_fileno(stream)) doesn't work as this will mangle the internal FILE handling data */ - switch(whence) { - case(SEEK_SET): - /* do nothing */ - break; - case(SEEK_CUR): - /* adjust offset */ - /* XXX - CURRENTLY UNTESTED!!! */ - ret = fgetpos(stream, &pos); - if(ret != 0) { - *err = errno; - return ret; - } - offset += pos; - break; - case(SEEK_END): - default: - g_assert_not_reached(); - } - ret = fsetpos(stream, &offset); - if(ret != 0) { - *err = errno; + /* check parameters */ + if (buf == NULL || len < 1) + return NULL; + + /* check that there's no error */ + if (file->err) + return NULL; + + /* process a skip request */ + if (file->seek) { + file->seek = 0; + if (gz_skip(file, file->skip) == -1) + return NULL; } - /* XXX - won't work >= 2GB! */ - /*ret = (gint64) fseek(stream, (long) offset, whence); - if(ret == -1) { - *err = errno; - }*/ -#else - /* "basic" version using fseek */ - /* XXX - won't work >= 2GB! */ - ret = (gint64) fseek(stream, (long) offset, whence); - if (ret == -1) - *err = file_error(stream); -#endif - /*g_warning("Seek %" G_GINT64_MODIFIER "d whence %u ret %" G_GINT64_MODIFIER "d size %u", offset, whence, ret, sizeof(fpos_t));*/ - return ret; + + /* copy output bytes up to new line or len - 1, whichever comes first -- + append a terminating zero to the string (we don't check for a zero in + the contents, let the user worry about that) */ + str = buf; + left = (unsigned)len - 1; + if (left) do { + /* assure that something is in the output buffer */ + if (file->have == 0) { + if (fill_out_buffer(file) == -1) + return NULL; /* error */ + if (file->have == 0) { /* end of file */ + if (buf == str) /* got bupkus */ + return NULL; + break; /* got something -- return it */ + } + } + + /* look for end-of-line in current output buffer */ + n = file->have > left ? left : file->have; + eol = memchr(file->next, '\n', n); + if (eol != NULL) + n = (unsigned)(eol - file->next) + 1; + + /* copy through end-of-line, or remainder if not found */ + memcpy(buf, file->next, n); + file->have -= n; + file->next += n; + file->pos += n; + left -= n; + buf += n; + } while (left && eol == NULL); + + /* found end-of-line or out of space -- terminate string and return it */ + buf[0] = 0; + return str; } -gint64 -file_tell(void *stream) +int +file_eof(FILE_T file) { -#ifdef _WIN32 - /* Win32 version using _telli64 */ - /* XXX - CURRENTLY UNTESTED!!! */ - return _telli64(_fileno((FILE *)stream)); -#else - /* "basic" version using ftell */ - /* XXX - ftell returns a long - won't work >= 2GB! */ - return (gint64) ftell(stream); -#endif + /* return end-of-file state */ + return (file->eof && file->avail_in == 0 && file->have == 0); } +/* + * Routine to return a Wiretap error code (0 for no error, an errno + * for a file error, or a WTAP_ERR_ code for other errors) for an + * I/O stream. + */ int -file_error(void *fh) +file_error(FILE_T fh) { - if (ferror((FILE *) fh)) - return errno; - else - return 0; + return fh->err; +} + +void +file_clearerr(FILE_T stream) +{ + /* clear error and end-of-file */ + stream->err = 0; + stream->eof = 0; +} + +int +file_close(FILE_T file) +{ + int fd = file->fd; + + /* free memory and close file */ + if (file->size) { +#ifdef HAVE_LIBZ + inflateEnd(&(file->strm)); +#endif + g_free(file->out); + g_free(file->in); + } + file->err = 0; + g_free(file); + return close(fd); } -#endif /* HAVE_LIBZ */ diff --git a/wiretap/file_wrappers.h b/wiretap/file_wrappers.h index 96e0c3482e..249de02a2e 100644 --- a/wiretap/file_wrappers.h +++ b/wiretap/file_wrappers.h @@ -24,35 +24,18 @@ #ifndef __FILE_H__ #define __FILE_H__ -extern gint64 file_seek(void *stream, gint64 offset, int whence, int *err); -extern gint64 file_tell(void *stream); -extern int file_error(void *fh); - -#ifdef HAVE_LIBZ +extern gint64 file_seek(FILE_T stream, gint64 offset, int whence, int *err); +extern gint64 file_tell(FILE_T stream); +extern int file_error(FILE_T fh); extern FILE_T file_open(const char *path); -#define filed_open(fildes) gzdopen(fildes, "rb") -#define file_read(buf, count, file) gzread((file),(buf),(unsigned)(count)) -#define file_close gzclose -#define file_getc gzgetc -#define file_gets(buf, len, file) gzgets((file), (buf), (len)) -#define file_eof gzeof - -#ifdef HAVE_GZCLEARERR -#define file_clearerr gzclearerr -#endif - -#else /* No zLib */ - -#define file_open(path) ws_fopen(path, "rb") -#define filed_open(fildes) fdopen(fildes, "rb") -#define file_read(buf, count, file) fread((buf), (1), (count), (file)) -#define file_close fclose -#define file_getc fgetc -#define file_gets fgets -#define file_eof feof -/* #define file_clearerr clearerr */ +extern FILE_T filed_open(int fildes); +extern int file_read(void *buf, unsigned int count, FILE_T file); +extern int file_close(FILE_T file); +extern int file_getc(FILE_T stream); +extern char *file_gets(char *buf, int len, FILE_T stream); +extern int file_eof(FILE_T stream); +extern void file_clearerr(FILE_T stream); -#endif /* HAVE_LIBZ */ #endif /* __FILE_H__ */ diff --git a/wiretap/wtap-int.h b/wiretap/wtap-int.h index ac206b06eb..feab2df258 100644 --- a/wiretap/wtap-int.h +++ b/wiretap/wtap-int.h @@ -37,11 +37,38 @@ #ifdef HAVE_LIBZ #include <zlib.h> -#define FILE_T gzFile +#define WFILE_T gzFile #else /* No zLib */ -#define FILE_T FILE * +#define WFILE_T FILE * #endif /* HAVE_LIBZ */ +typedef struct { + int fd; /* file descriptor */ + gint64 pos; /* current position in uncompressed data */ + unsigned size; /* buffer size */ + unsigned char *in; /* input buffer */ + unsigned char *out; /* output buffer (double-sized when reading) */ + unsigned char *next; /* next output data to deliver or write */ + + unsigned have; /* amount of output data unused at next */ + int 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 */ + int compression; /* 0: ?, 1: uncompressed, 2: zlib */ + /* seek request */ + gint64 skip; /* amount to skip (already rewound if backwards) */ + int seek; /* true if seek request pending */ + /* error information */ + int err; /* error code */ + + unsigned int avail_in; /* number of bytes available at next_in */ + unsigned char *next_in; /* next input byte */ +#ifdef HAVE_LIBZ + /* zlib inflate stream */ + z_stream strm; /* stream structure in-place (not a pointer) */ +#endif +} wtap_reader, *FILE_T; + #include "wtap.h" typedef gboolean (*subtype_read_func)(struct wtap*, int*, char**, gint64*); @@ -83,7 +110,7 @@ typedef gboolean (*subtype_write_func)(struct wtap_dumper*, typedef gboolean (*subtype_close_func)(struct wtap_dumper*, int*); struct wtap_dumper { - FILE* fh; + WFILE_T fh; int file_type; int snaplen; int encap; diff --git a/wiretap/wtap.c b/wiretap/wtap.c index cdf2cd8d10..80d5a204fa 100644 --- a/wiretap/wtap.c +++ b/wiretap/wtap.c @@ -661,11 +661,9 @@ wtap_close(wtap *wth) void wtap_cleareof(wtap *wth _U_) { -#ifdef file_clearerr /* Reset EOF */ if (file_eof(wth->fh)) file_clearerr(wth->fh); -#endif } void wtap_set_cb_new_ipv4(wtap *wth, wtap_new_ipv4_callback_t add_new_ipv4) { |