Mercurial > dovecot > core-2.2
changeset 10693:74d9dbee1399 HEAD
zlib: Reimplemented gz/bz2 input streams by using the uncompression functions directly.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sat, 13 Feb 2010 01:15:12 +0200 |
parents | d73634c82feb |
children | 9f0014f19bd3 |
files | src/plugins/zlib/istream-bzlib.c src/plugins/zlib/istream-zlib.c src/plugins/zlib/istream-zlib.h src/plugins/zlib/zlib-plugin.c |
diffstat | 4 files changed, 576 insertions(+), 151 deletions(-) [+] |
line wrap: on
line diff
--- a/src/plugins/zlib/istream-bzlib.c Sat Feb 13 00:42:59 2010 +0200 +++ b/src/plugins/zlib/istream-bzlib.c Sat Feb 13 01:15:12 2010 +0200 @@ -1,23 +1,279 @@ -/* Copyright (c) 2005-2010 Dovecot authors, see the included COPYING file */ +/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */ #include "lib.h" + +#ifdef HAVE_BZLIB + #include "istream-internal.h" #include "istream-zlib.h" - -#ifdef HAVE_BZLIB -#include <stdio.h> #include <bzlib.h> -#define BZLIB_INCLUDE +#define CHUNK_SIZE (1024*64) + +struct bzlib_istream { + struct istream_private istream; + + bz_stream zs; + uoff_t eof_offset; + size_t prev_size; + + unsigned int marked:1; + unsigned int zs_closed:1; +}; + +static void i_stream_bzlib_close(struct iostream_private *stream) +{ + struct bzlib_istream *zstream = (struct bzlib_istream *)stream; + + if (!zstream->zs_closed) { + (void)BZ2_bzDecompressEnd(&zstream->zs); + zstream->zs_closed = TRUE; + } +} + +static ssize_t i_stream_bzlib_read(struct istream_private *stream) +{ + struct bzlib_istream *zstream = (struct bzlib_istream *)stream; + const unsigned char *data; + uoff_t high_offset; + size_t size; + int ret; + + high_offset = stream->istream.v_offset + (stream->pos - stream->skip); + if (zstream->eof_offset == high_offset) { + stream->istream.eof = TRUE; + return -1; + } + + if (stream->pos + CHUNK_SIZE > stream->buffer_size) { + /* try to keep at least CHUNK_SIZE available */ + if (!zstream->marked && stream->skip > 0) { + /* don't try to keep anything cached if we don't + have a seek mark. */ + i_stream_compress(stream); + } + if (stream->max_buffer_size == 0 || + stream->buffer_size < stream->max_buffer_size) + i_stream_grow_buffer(stream, CHUNK_SIZE); + + if (stream->pos == stream->buffer_size) { + if (stream->skip > 0) { + /* lose our buffer cache */ + i_stream_compress(stream); + } + + if (stream->pos == stream->buffer_size) + return -2; /* buffer full */ + } + } + + if (zstream->zs.avail_in == 0) { + /* need to read more data. try to read a full CHUNK_SIZE */ + i_stream_skip(stream->parent, zstream->prev_size); + if (i_stream_read_data(stream->parent, &data, &size, + CHUNK_SIZE-1) == -1 && size == 0) { + if (stream->parent->stream_errno != 0) { + stream->istream.stream_errno = + stream->parent->stream_errno; + } else { + /* unexpected eof */ + i_assert(stream->parent->eof); + stream->istream.stream_errno = EINVAL; + } + return -1; + } + zstream->prev_size = size; + if (size == 0) { + /* no more input */ + i_assert(!stream->istream.blocking); + return 0; + } + + zstream->zs.next_in = (char *)data; + zstream->zs.avail_in = size; + } + + size = stream->buffer_size - stream->pos; + zstream->zs.next_out = (char *)stream->w_buffer + stream->pos; + zstream->zs.avail_out = size; + ret = BZ2_bzDecompress(&zstream->zs); + + size -= zstream->zs.avail_in; + stream->pos += size; + + switch (ret) { + case BZ_OK: + break; + case BZ_PARAM_ERROR: + i_unreached(); + case BZ_DATA_ERROR: + case BZ_DATA_ERROR_MAGIC: + stream->istream.stream_errno = EINVAL; + return -1; + case BZ_MEM_ERROR: + i_fatal_status(FATAL_OUTOFMEM, "bzlib: Out of memory"); + case BZ_STREAM_END: + zstream->eof_offset = stream->istream.v_offset + stream->pos; + if (size == 0) { + stream->istream.eof = TRUE; + return -1; + } + break; + default: + i_fatal("BZ2_bzDecompress() failed with %d", ret); + } + if (size == 0) { + /* read more input */ + return i_stream_bzlib_read(stream); + } + return size; +} + +static void i_stream_bzlib_init(struct bzlib_istream *zstream) +{ + int ret; -#define gzFile BZFILE -#define gzdopen BZ2_bzdopen -#define gzclose BZ2_bzclose -#define gzread BZ2_bzread -#define gzseek BZ2_bzseek -#define gzerror BZ2_bzerror -#define Z_ERRNO BZ_IO_ERROR + ret = BZ2_bzDecompressInit(&zstream->zs, 0, 0); + switch (ret) { + case BZ_OK: + break; + case BZ_MEM_ERROR: + i_fatal_status(FATAL_OUTOFMEM, "bzlib: Out of memory"); + case BZ_CONFIG_ERROR: + i_fatal("Wrong bzlib library version (broken compilation)"); + case BZ_PARAM_ERROR: + i_fatal("bzlib: Invalid parameters"); + default: + i_fatal("BZ2_bzDecompressInit() failed with %d", ret); + } +} + +static void i_stream_bzlib_reset(struct bzlib_istream *zstream) +{ + struct istream_private *stream = &zstream->istream; + + i_stream_seek(stream->parent, 0); + zstream->eof_offset = (uoff_t)-1; + zstream->zs.next_in = NULL; + zstream->zs.avail_in = 0; + + stream->skip = stream->pos = 0; + stream->istream.v_offset = 0; + + (void)BZ2_bzDecompressEnd(&zstream->zs); + i_stream_bzlib_init(zstream); +} + +static void +i_stream_bzlib_seek(struct istream_private *stream, uoff_t v_offset, bool mark) +{ + struct bzlib_istream *zstream = (struct bzlib_istream *) stream; + uoff_t start_offset = stream->istream.v_offset - stream->skip; + + if (v_offset < start_offset) { + /* have to seek backwards */ + i_stream_bzlib_reset(zstream); + start_offset = 0; + } + + if (v_offset <= start_offset + stream->pos) { + /* seeking backwards within what's already cached */ + stream->skip = v_offset - start_offset; + stream->istream.v_offset = v_offset; + } else { + /* read and cache forward */ + do { + size_t avail = stream->pos - stream->skip; + + if (stream->istream.v_offset + avail >= v_offset) { + i_stream_skip(&stream->istream, + v_offset - + stream->istream.v_offset); + break; + } + + i_stream_skip(&stream->istream, avail); + } while (i_stream_bzlib_read(stream) >= 0); -#define i_stream_create_zlib i_stream_create_bzlib -#include "istream-zlib.c" + if (stream->istream.v_offset != v_offset) { + /* some failure, we've broken it */ + if (stream->istream.stream_errno != 0) { + i_error("bzlib_istream.seek() failed: %s", + strerror(stream->istream.stream_errno)); + i_stream_close(&stream->istream); + } else { + /* unexpected EOF. allow it since we may just + want to check if there's anything.. */ + i_assert(stream->istream.eof); + } + } + } + + if (mark) { + i_stream_compress(stream); + zstream->marked = TRUE; + } +} + +static const struct stat * +i_stream_bzlib_stat(struct istream_private *stream, bool exact) +{ + struct bzlib_istream *zstream = (struct bzlib_istream *) stream; + const struct stat *st; + size_t size; + + st = i_stream_stat(stream->parent, exact); + if (st == NULL) + return NULL; + + if (zstream->eof_offset == (uoff_t)-1 && !exact) + return st; + + stream->statbuf = *st; + if (zstream->eof_offset == (uoff_t)-1) { + uoff_t old_offset = stream->istream.v_offset; + + do { + (void)i_stream_get_data(&stream->istream, &size); + i_stream_skip(&stream->istream, size); + } while (i_stream_bzlib_read(stream) > 0); + + i_stream_seek(&stream->istream, old_offset); + if (zstream->eof_offset == (uoff_t)-1) + return NULL; + } + stream->statbuf.st_size = zstream->eof_offset; + return &stream->statbuf; +} + +static void i_stream_bzlib_sync(struct istream_private *stream) +{ + struct bzlib_istream *zstream = (struct bzlib_istream *) stream; + + i_stream_bzlib_reset(zstream); +} + +struct istream *i_stream_create_bz2(struct istream *input) +{ + struct bzlib_istream *zstream; + + zstream = i_new(struct bzlib_istream, 1); + zstream->eof_offset = (uoff_t)-1; + + i_stream_bzlib_init(zstream); + + zstream->istream.iostream.close = i_stream_bzlib_close; + zstream->istream.max_buffer_size = input->real_stream->max_buffer_size; + zstream->istream.read = i_stream_bzlib_read; + zstream->istream.seek = i_stream_bzlib_seek; + zstream->istream.stat = i_stream_bzlib_stat; + zstream->istream.sync = i_stream_bzlib_sync; + + zstream->istream.istream.readable_fd = FALSE; + zstream->istream.istream.blocking = input->blocking; + zstream->istream.istream.seekable = input->seekable; + + return i_stream_create(&zstream->istream, input, + i_stream_get_fd(input)); +} #endif
--- a/src/plugins/zlib/istream-zlib.c Sat Feb 13 00:42:59 2010 +0200 +++ b/src/plugins/zlib/istream-zlib.c Sat Feb 13 01:15:12 2010 +0200 @@ -1,69 +1,183 @@ -/* Copyright (c) 2005-2010 Dovecot authors, see the included COPYING file */ +/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */ + +#include "lib.h" + +#ifdef HAVE_ZLIB + +#include "crc32.h" +#include "istream-internal.h" +#include "istream-zlib.h" +#include <zlib.h> -#ifdef BZLIB_INCLUDE -# define BUILD_SOURCE -#else -# include "config.h" -# undef HAVE_CONFIG_H -# if _FILE_OFFSET_BITS == 64 -# define _LARGEFILE64_SOURCE -# endif -# include "lib.h" -# include "istream-internal.h" -# include "istream-zlib.h" -# include <zlib.h> +#define CHUNK_SIZE (1024*64) + +#define GZ_HEADER_MIN_SIZE 10 +#define GZ_TRAILER_SIZE 8 -# ifdef HAVE_ZLIB -# define BUILD_SOURCE -# define HAVE_GZSEEK -# endif -#endif - -#ifdef BUILD_SOURCE -/* Default maximum buffer size. Seeking backwards is very expensive, so keep - this pretty large */ -#define DEFAULT_MAX_BUFFER_SIZE (1024*1024) - -#include <unistd.h> +#define GZ_MAGIC1 0x1f +#define GZ_MAGIC2 0x8b +#define GZ_FLAG_FHCRC 0x02 +#define GZ_FLAG_FEXTRA 0x04 +#define GZ_FLAG_FNAME 0x08 +#define GZ_FLAG_FCOMMENT 0x10 struct zlib_istream { struct istream_private istream; - int fd; - gzFile *file; - uoff_t cached_size; - uoff_t seek_offset; + z_stream zs; + uoff_t eof_offset; + size_t prev_size; + uint32_t crc32; + unsigned int gz:1; unsigned int marked:1; + unsigned int header_read:1; + unsigned int trailer_read:1; + unsigned int zs_closed:1; }; static void i_stream_zlib_close(struct iostream_private *stream) { struct zlib_istream *zstream = (struct zlib_istream *)stream; - if (zstream->file != NULL) { - gzclose(zstream->file); - zstream->file = NULL; + if (!zstream->zs_closed) { + (void)inflateEnd(&zstream->zs); + zstream->zs_closed = TRUE; + } +} + +static int i_stream_zlib_read_header(struct istream_private *stream) +{ + struct zlib_istream *zstream = (struct zlib_istream *)stream; + const unsigned char *data; + size_t size; + unsigned int pos, fextra_size; + int ret; + + ret = i_stream_read_data(stream->parent, &data, &size, + zstream->prev_size); + if (size == zstream->prev_size) { + if (ret == -1) + stream->istream.stream_errno = EINVAL; + return ret; + } + zstream->prev_size = size; + + if (size < GZ_HEADER_MIN_SIZE) + return 0; + pos = GZ_HEADER_MIN_SIZE; + + if (data[0] != GZ_MAGIC1 || data[1] != GZ_MAGIC2) { + /* missing gzip magic header */ + stream->istream.stream_errno = EINVAL; + return -1; + } + if ((data[3] & GZ_FLAG_FEXTRA) != 0) { + if (pos + 2 < size) + return 0; + + fextra_size = data[pos] + (data[pos+1] << 8); + pos += 2; + if (pos + fextra_size < size) + return 0; + pos += fextra_size; + } + if ((data[3] & GZ_FLAG_FNAME) != 0) { + do { + if (pos == size) + return 0; + } while (data[pos++] != '\0'); } + if ((data[3] & GZ_FLAG_FCOMMENT) != 0) { + do { + if (pos == size) + return 0; + } while (data[pos++] != '\0'); + } + if ((data[3] & GZ_FLAG_FHCRC) != 0) { + if (pos + 2 < size) + return 0; + pos += 2; + } + i_stream_skip(stream->parent, pos); + return 1; +} + +static uint32_t data_get_uint32(const unsigned char *data) +{ + return data[0] | (data[1] << 8) | (data[2] << 16) | + ((uint32_t)data[3] << 24); +} + +static int i_stream_zlib_read_trailer(struct zlib_istream *zstream) +{ + struct istream_private *stream = &zstream->istream; + const unsigned char *data; + size_t size; + int ret; + + ret = i_stream_read_data(stream->parent, &data, &size, + GZ_TRAILER_SIZE-1); + if (size == zstream->prev_size) { + if (ret == -1) + stream->istream.stream_errno = EINVAL; + return ret; + } + zstream->prev_size = size; + + if (size < GZ_TRAILER_SIZE) + return 0; + + if (data_get_uint32(data) != zstream->crc32) { + stream->istream.stream_errno = EINVAL; + return -1; + } + i_stream_skip(stream->parent, GZ_TRAILER_SIZE); + zstream->trailer_read = TRUE; + return 1; } static ssize_t i_stream_zlib_read(struct istream_private *stream) { struct zlib_istream *zstream = (struct zlib_istream *)stream; + const unsigned char *data; + uoff_t high_offset; size_t size; - const char *errstr; - int ret, errnum; + int ret; - if (stream->pos == stream->buffer_size) { + high_offset = stream->istream.v_offset + (stream->pos - stream->skip); + if (zstream->eof_offset == high_offset) { + if (!zstream->trailer_read) { + do { + ret = i_stream_zlib_read_trailer(zstream); + } while (ret == 0 && stream->istream.blocking); + if (ret <= 0) + return ret; + } + stream->istream.eof = TRUE; + return -1; + } + + if (!zstream->header_read) { + do { + ret = i_stream_zlib_read_header(stream); + } while (ret == 0 && stream->istream.blocking); + if (ret <= 0) + return ret; + zstream->header_read = TRUE; + zstream->prev_size = 0; + } + + if (stream->pos + CHUNK_SIZE > stream->buffer_size) { + /* try to keep at least CHUNK_SIZE available */ if (!zstream->marked && stream->skip > 0) { /* don't try to keep anything cached if we don't have a seek mark. */ i_stream_compress(stream); - } else if (stream->max_buffer_size == 0 || - stream->buffer_size < stream->max_buffer_size) { - /* buffer is full - grow it */ - i_stream_grow_buffer(stream, I_STREAM_MIN_SIZE); } + if (stream->max_buffer_size == 0 || + stream->buffer_size < stream->max_buffer_size) + i_stream_grow_buffer(stream, CHUNK_SIZE); if (stream->pos == stream->buffer_size) { if (stream->skip > 0) { @@ -76,44 +190,112 @@ } } - size = stream->buffer_size - stream->pos; - - ret = -1; + if (zstream->zs.avail_in == 0) { + /* need to read more data. try to read a full CHUNK_SIZE */ + i_stream_skip(stream->parent, zstream->prev_size); + if (i_stream_read_data(stream->parent, &data, &size, + CHUNK_SIZE-1) == -1 && size == 0) { + if (stream->parent->stream_errno != 0) { + stream->istream.stream_errno = + stream->parent->stream_errno; + } else { + /* unexpected eof */ + i_assert(stream->parent->eof); + stream->istream.stream_errno = EINVAL; + } + return -1; + } + zstream->prev_size = size; + if (size == 0) { + /* no more input */ + i_assert(!stream->istream.blocking); + return 0; + } - i_assert(zstream->seek_offset == stream->istream.v_offset + - (stream->pos - stream->skip)); - do { - ret = gzread(zstream->file, stream->w_buffer + stream->pos, - size); - } while (ret < 0 && errno == EINTR && stream->istream.blocking); - - if (ret == 0) { - /* EOF */ - stream->istream.eof = TRUE; - return -1; + zstream->zs.next_in = (void *)data; + zstream->zs.avail_in = size; } - if (ret < 0) { - errstr = gzerror(zstream->file, &errnum); - if (errnum != Z_ERRNO) { - i_error("gzread() failed: %s", errstr); - stream->istream.stream_errno = EINVAL; - return -1; + size = stream->buffer_size - stream->pos; + zstream->zs.next_out = stream->w_buffer + stream->pos; + zstream->zs.avail_out = size; + ret = inflate(&zstream->zs, Z_SYNC_FLUSH); + + size -= zstream->zs.avail_out; + zstream->crc32 = crc32_data_more(zstream->crc32, + stream->w_buffer + stream->pos, size); + stream->pos += size; + + switch (ret) { + case Z_OK: + break; + case Z_NEED_DICT: + case Z_DATA_ERROR: + stream->istream.stream_errno = EINVAL; + return -1; + case Z_MEM_ERROR: + i_fatal_status(FATAL_OUTOFMEM, "zlib: Out of memory"); + case Z_STREAM_END: + zstream->eof_offset = stream->istream.v_offset + stream->pos; + i_stream_skip(stream->parent, + zstream->prev_size - zstream->zs.avail_in); + zstream->zs.avail_in = 0; + zstream->prev_size = 0; + + if (!zstream->trailer_read) { + /* try to read and verify the trailer, we might not + be called again. */ + if (i_stream_zlib_read_trailer(zstream) < 0) + return -1; } - if (errno == EAGAIN) { - i_assert(!stream->istream.blocking); - ret = 0; - } else { - i_assert(errno != 0); - stream->istream.stream_errno = errno; - return -1; - } + break; + default: + i_fatal("inflate() failed with %d", ret); + } + if (size == 0) { + /* read more input */ + return i_stream_zlib_read(stream); } + return size; +} - zstream->seek_offset += ret; - stream->pos += ret; - i_assert(ret > 0); - return ret; +static void i_stream_zlib_init(struct zlib_istream *zstream) +{ + int ret; + + ret = inflateInit2(&zstream->zs, -15); + switch (ret) { + case Z_OK: + break; + case Z_MEM_ERROR: + i_fatal_status(FATAL_OUTOFMEM, "zlib: Out of memory"); + case Z_VERSION_ERROR: + i_fatal("Wrong zlib library version (broken compilation)"); + case Z_STREAM_ERROR: + i_fatal("zlib: Invalid parameters"); + default: + i_fatal("inflateInit() failed with %d", ret); + } + zstream->header_read = !zstream->gz; + zstream->trailer_read = !zstream->gz; +} + +static void i_stream_zlib_reset(struct zlib_istream *zstream) +{ + struct istream_private *stream = &zstream->istream; + + i_stream_seek(stream->parent, 0); + zstream->eof_offset = (uoff_t)-1; + zstream->crc32 = 0; + + zstream->zs.next_in = NULL; + zstream->zs.avail_in = 0; + + stream->skip = stream->pos = 0; + stream->istream.v_offset = 0; + + (void)inflateEnd(&zstream->zs); + i_stream_zlib_init(zstream); } static void @@ -122,32 +304,12 @@ struct zlib_istream *zstream = (struct zlib_istream *) stream; uoff_t start_offset = stream->istream.v_offset - stream->skip; -#ifndef HAVE_GZSEEK - if (v_offset < start_offset) { - /* need to reopen, but since closing the file closes the - file descriptor we'll have to duplicate it first. */ - int fd = dup(zstream->fd); - if (fd == -1) { - stream->istream.stream_errno = errno; - i_error("zlib istream: dup() failed: %m"); - i_stream_close(&stream->istream); - return; - } - gzclose(zstream->file); - zstream->fd = fd; - stream->fd = fd; - zstream->file = gzdopen(zstream->fd, "r"); - } -#else if (v_offset < start_offset) { /* have to seek backwards */ - gzseek(zstream->file, v_offset, SEEK_SET); - zstream->seek_offset = v_offset; + i_stream_zlib_reset(zstream); + start_offset = 0; + } - stream->skip = stream->pos = 0; - stream->istream.v_offset = v_offset; - } else -#endif if (v_offset <= start_offset + stream->pos) { /* seeking backwards within what's already cached */ stream->skip = v_offset - start_offset; @@ -156,6 +318,7 @@ /* read and cache forward */ do { size_t avail = stream->pos - stream->skip; + if (stream->istream.v_offset + avail >= v_offset) { i_stream_skip(&stream->istream, v_offset - @@ -190,27 +353,30 @@ i_stream_zlib_stat(struct istream_private *stream, bool exact) { struct zlib_istream *zstream = (struct zlib_istream *) stream; + const struct stat *st; size_t size; - if (fstat(zstream->fd, &stream->statbuf) < 0) { - i_error("zlib_istream.fstat() failed: %m"); + st = i_stream_stat(stream->parent, exact); + if (st == NULL) return NULL; - } - if (!exact) - return &stream->statbuf; + if (zstream->eof_offset == (uoff_t)-1 && !exact) + return st; - if (zstream->cached_size == (uoff_t)-1) { + stream->statbuf = *st; + if (zstream->eof_offset == (uoff_t)-1) { uoff_t old_offset = stream->istream.v_offset; + do { (void)i_stream_get_data(&stream->istream, &size); i_stream_skip(&stream->istream, size); } while (i_stream_zlib_read(stream) > 0); - zstream->cached_size = stream->istream.v_offset; i_stream_seek(&stream->istream, old_offset); + if (zstream->eof_offset == (uoff_t)-1) + return NULL; } - stream->statbuf.st_size = zstream->cached_size; + stream->statbuf.st_size = zstream->eof_offset; return &stream->statbuf; } @@ -218,33 +384,41 @@ { struct zlib_istream *zstream = (struct zlib_istream *) stream; - zstream->cached_size = (uoff_t)-1; + i_stream_zlib_reset(zstream); } -struct istream *i_stream_create_zlib(int fd) +static struct istream *i_stream_create_zlib(struct istream *input, bool gz) { struct zlib_istream *zstream; - struct stat st; zstream = i_new(struct zlib_istream, 1); - zstream->fd = fd; - zstream->file = gzdopen(fd, "r"); - zstream->cached_size = (uoff_t)-1; + zstream->eof_offset = (uoff_t)-1; + zstream->gz = gz; + + i_stream_zlib_init(zstream); zstream->istream.iostream.close = i_stream_zlib_close; - zstream->istream.max_buffer_size = DEFAULT_MAX_BUFFER_SIZE; + zstream->istream.max_buffer_size = input->real_stream->max_buffer_size; zstream->istream.read = i_stream_zlib_read; zstream->istream.seek = i_stream_zlib_seek; zstream->istream.stat = i_stream_zlib_stat; zstream->istream.sync = i_stream_zlib_sync; - /* if it's a file, set the flags properly */ - if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) { - zstream->istream.istream.blocking = TRUE; - zstream->istream.istream.seekable = TRUE; - } + zstream->istream.istream.readable_fd = FALSE; + zstream->istream.istream.blocking = input->blocking; + zstream->istream.istream.seekable = input->seekable; + + return i_stream_create(&zstream->istream, input, + i_stream_get_fd(input)); +} - zstream->istream.istream.readable_fd = FALSE; - return i_stream_create(&zstream->istream, NULL, fd); +struct istream *i_stream_create_gz(struct istream *input) +{ + return i_stream_create_zlib(input, TRUE); +} + +struct istream *i_stream_create_deflate(struct istream *input) +{ + return i_stream_create_zlib(input, FALSE); } #endif
--- a/src/plugins/zlib/istream-zlib.h Sat Feb 13 00:42:59 2010 +0200 +++ b/src/plugins/zlib/istream-zlib.h Sat Feb 13 01:15:12 2010 +0200 @@ -1,7 +1,8 @@ #ifndef ISTREAM_ZLIB_H #define ISTREAM_ZLIB_H -struct istream *i_stream_create_zlib(int fd); -struct istream *i_stream_create_bzlib(int fd); +struct istream *i_stream_create_gz(struct istream *input); +struct istream *i_stream_create_deflate(struct istream *input); +struct istream *i_stream_create_bz2(struct istream *input); #endif
--- a/src/plugins/zlib/zlib-plugin.c Sat Feb 13 00:42:59 2010 +0200 +++ b/src/plugins/zlib/zlib-plugin.c Sat Feb 13 01:15:12 2010 +0200 @@ -32,11 +32,13 @@ # define o_stream_create_bzlib NULL #endif +#define MAX_INBUF_SIZE (1024*1024) + struct zlib_handler { const char *name; const char *ext; bool (*is_compressed)(struct istream *input); - struct istream *(*create_istream)(int fd); + struct istream *(*create_istream)(struct istream *input); struct ostream *(*create_ostream)(struct ostream *output, int level); }; @@ -95,9 +97,9 @@ static struct zlib_handler zlib_handlers[] = { { "gz", ".gz", is_compressed_zlib, - i_stream_create_zlib, o_stream_create_gz }, + i_stream_create_gz, o_stream_create_gz }, { "bz2", ".bz2", is_compressed_bzlib, - i_stream_create_bzlib, o_stream_create_bz2 } + i_stream_create_bz2, o_stream_create_bz2 } }; static struct zlib_handler *zlib_find_zlib_handler(const char *name) @@ -145,7 +147,6 @@ union mail_module_context *zmail = ZLIB_MAIL_CONTEXT(mail); struct istream *input; struct zlib_handler *handler; - int fd; if (imail->data.stream != NULL) { return zmail->super.get_stream(_mail, hdr_size, body_size, @@ -162,22 +163,12 @@ mail_storage_set_critical(_mail->box->storage, "zlib plugin: Detected %s compression " "but support not compiled in", handler->ext); - fd = -1; - } else { - fd = dup(i_stream_get_fd(imail->data.stream)); - if (fd == -1) { - mail_storage_set_critical(_mail->box->storage, - "zlib plugin: dup() failed: %m"); - } + return -1; } - imail->data.destroying_stream = TRUE; - i_stream_unref(&imail->data.stream); - i_assert(!imail->data.destroying_stream); - - if (fd == -1) - return -1; - imail->data.stream = handler->create_istream(fd); + input = imail->data.stream; + imail->data.stream = handler->create_istream(input); + i_stream_unref(&input); } return index_mail_init_stream(imail, hdr_size, body_size, stream_r); } @@ -329,6 +320,7 @@ static int zlib_mailbox_open_input(struct mailbox *box) { struct zlib_handler *handler; + struct istream *input; int fd; handler = zlib_get_zlib_handler_ext(box->name); @@ -344,7 +336,9 @@ "open(%s) failed: %m", box->path); return -1; } - box->input = handler->create_istream(fd); + input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE); + box->input = handler->create_istream(input); + i_stream_unref(&input); box->flags |= MAILBOX_FLAG_READONLY; } return 0;