]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
zlib: Reimplemented gz/bz2 input streams by using the uncompression functions directly.
authorTimo Sirainen <tss@iki.fi>
Fri, 12 Feb 2010 23:15:12 +0000 (01:15 +0200)
committerTimo Sirainen <tss@iki.fi>
Fri, 12 Feb 2010 23:15:12 +0000 (01:15 +0200)
--HG--
branch : HEAD

src/plugins/zlib/istream-bzlib.c
src/plugins/zlib/istream-zlib.c
src/plugins/zlib/istream-zlib.h
src/plugins/zlib/zlib-plugin.c

index 60cf138246831928ef5b53e2597c7063cadca9e8..b057f6862bb607758ee8e015851a8f1f5e3a66cc 100644 (file)
-/* Copyright (c) 2005-2010 Dovecot authors, see the included COPYING file */
+/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
-#include "istream-internal.h"
-#include "istream-zlib.h"
 
 #ifdef HAVE_BZLIB
-#include <stdio.h>
+
+#include "istream-internal.h"
+#include "istream-zlib.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;
+
+       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);
+
+               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;
 
-#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
+       zstream->istream.istream.readable_fd = FALSE;
+       zstream->istream.istream.blocking = input->blocking;
+       zstream->istream.istream.seekable = input->seekable;
 
-#define i_stream_create_zlib i_stream_create_bzlib
-#include "istream-zlib.c"
+       return i_stream_create(&zstream->istream, input,
+                              i_stream_get_fd(input));
+}
 #endif
index ac4eca02027946abfb3e2c6cba90a0b7a6d20137..b3e7605e42cc362bf957ac0f889ea840b873dde0 100644 (file)
-/* Copyright (c) 2005-2010 Dovecot authors, see the included COPYING file */
-
-#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>
-
-#  ifdef HAVE_ZLIB
-#    define BUILD_SOURCE
-#    define HAVE_GZSEEK
-#  endif
-#endif
+/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+
+#ifdef HAVE_ZLIB
 
-#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 "crc32.h"
+#include "istream-internal.h"
+#include "istream-zlib.h"
+#include <zlib.h>
 
-#include <unistd.h>
+#define CHUNK_SIZE (1024*64)
+
+#define GZ_HEADER_MIN_SIZE 10
+#define GZ_TRAILER_SIZE 8
+
+#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;
+
+       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 (stream->pos == stream->buffer_size) {
+       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 @@ static ssize_t i_stream_zlib_read(struct istream_private *stream)
                }
        }
 
-       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);
+               zstream->zs.next_in = (void *)data;
+               zstream->zs.avail_in = size;
+       }
 
-       if (ret == 0) {
-               /* EOF */
-               stream->istream.eof = TRUE;
+       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;
+               }
+               break;
+       default:
+               i_fatal("inflate() failed with %d", ret);
+       }
+       if (size == 0) {
+               /* read more input */
+               return i_stream_zlib_read(stream);
        }
+       return 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;
-               }
-               if (errno == EAGAIN) {
-                       i_assert(!stream->istream.blocking);
-                       ret = 0;
-               } else {
-                       i_assert(errno != 0);
-                       stream->istream.stream_errno = errno;
-                       return -1;
-               }
+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;
 
-       zstream->seek_offset += ret;
-       stream->pos += ret;
-       i_assert(ret > 0);
-       return ret;
+       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 @@ i_stream_zlib_seek(struct istream_private *stream, uoff_t v_offset, bool mark)
        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 @@ i_stream_zlib_seek(struct istream_private *stream, uoff_t v_offset, bool mark)
                /* 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 @@ static const struct stat *
 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 @@ static void i_stream_zlib_sync(struct istream_private *stream)
 {
        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;
-       return i_stream_create(&zstream->istream, NULL, fd);
+       zstream->istream.istream.blocking = input->blocking;
+       zstream->istream.istream.seekable = input->seekable;
+
+       return i_stream_create(&zstream->istream, input,
+                              i_stream_get_fd(input));
+}
+
+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
index 0f4ed03b4c2d14382d0717e84add3e4e63c1b68e..60f7976719aaccd3478c25368114bf8d9e5b09da 100644 (file)
@@ -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
index e45197f66e51d01f756f80d928ae02dedd8a1f10..0bede3aa1daf8c0d8466c912d69b502c684e76ec 100644 (file)
 #  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 bool is_compressed_bzlib(struct istream *input)
 
 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 @@ static int zlib_maildir_get_stream(struct mail *_mail,
        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 @@ static int zlib_maildir_get_stream(struct mail *_mail,
                        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 void zlib_maildir_alloc_init(struct mailbox *box)
 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 @@ static int zlib_mailbox_open_input(struct mailbox *box)
                                "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;