istream-zlib.c revision 01e606cda5192c4254c090624a0b2ca92da6da8e
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#ifdef BZLIB_INCLUDE
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen# define BUILD_SOURCE
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen#else
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen# include "config.h"
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen# undef HAVE_CONFIG_H
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen# if _FILE_OFFSET_BITS == 64
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen# define _LARGEFILE64_SOURCE
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen# endif
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen# include "lib.h"
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen# include "istream-internal.h"
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen# include "istream-zlib.h"
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen# include <zlib.h>
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen# ifdef HAVE_ZLIB
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen# define BUILD_SOURCE
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen# define HAVE_GZSEEK
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen# endif
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen#endif
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen#ifdef BUILD_SOURCE
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen/* Default maximum buffer size. Seeking backwards is very expensive, so keep
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen this pretty large */
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen#define DEFAULT_MAX_BUFFER_SIZE (1024*1024)
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen#include <unistd.h>
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstruct zlib_istream {
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen struct istream_private istream;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen int fd;
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen gzFile *file;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uoff_t cached_size;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen uoff_t seek_offset;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen unsigned int marked:1;
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen};
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainenstatic void i_stream_zlib_close(struct iostream_private *stream)
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen{
c251a38df327599a62d341bf5c2282f31352faa5Timo Sirainen struct zlib_istream *zstream = (struct zlib_istream *)stream;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen if (zstream->file != NULL) {
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen gzclose(zstream->file);
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen zstream->file = NULL;
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen }
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen}
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainenstatic ssize_t i_stream_zlib_read(struct istream_private *stream)
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen{
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen struct zlib_istream *zstream = (struct zlib_istream *)stream;
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen size_t size;
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen int ret;
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen if (stream->pos == stream->buffer_size) {
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen if (!zstream->marked && stream->skip > 0) {
1175f27441385a7011629f295f42708f9a3a4ffcTimo Sirainen /* don't try to keep anything cached if we don't
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen have a seek mark. */
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen i_stream_compress(stream);
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen } else if (stream->max_buffer_size == 0 ||
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen stream->buffer_size < stream->max_buffer_size) {
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen /* buffer is full - grow it */
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen i_stream_grow_buffer(stream, I_STREAM_MIN_SIZE);
687bb904e1bb76c21a6e392f60c990486b298ea4Timo Sirainen }
687bb904e1bb76c21a6e392f60c990486b298ea4Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (stream->pos == stream->buffer_size) {
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen if (stream->skip > 0) {
7c95b03620a03a43dd72d39608cea5fc77393ad6Timo Sirainen /* lose our buffer cache */
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen i_stream_compress(stream);
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen }
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen if (stream->pos == stream->buffer_size)
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen return -2; /* buffer full */
73e7998716853b5b7621c06aea0022dccda70ad1Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen size = stream->buffer_size - stream->pos;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen ret = -1;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen i_assert(zstream->seek_offset == stream->istream.v_offset +
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen (stream->pos - stream->skip));
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen do {
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen ret = gzread(zstream->file, stream->w_buffer + stream->pos,
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen size);
985fa802913c96ce6f2e25bbc788ee39c416a7e0Timo Sirainen } while (ret < 0 && errno == EINTR && stream->istream.blocking);
985fa802913c96ce6f2e25bbc788ee39c416a7e0Timo Sirainen
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen if (ret == 0) {
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen /* EOF */
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen stream->istream.eof = TRUE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
}
if (ret < 0) {
if (errno == EAGAIN) {
i_assert(!stream->istream.blocking);
ret = 0;
} else {
i_assert(errno != 0);
stream->istream.stream_errno = errno;
return -1;
}
}
zstream->seek_offset += ret;
stream->pos += ret;
i_assert(ret > 0);
return ret;
}
static void
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;
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;
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_zlib_read(stream) >= 0);
if (stream->istream.v_offset != v_offset) {
/* some failure, we've broken it */
if (stream->istream.stream_errno != 0) {
i_error("zlib_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_zlib_stat(struct istream_private *stream, bool exact)
{
struct zlib_istream *zstream = (struct zlib_istream *) stream;
size_t size;
if (fstat(zstream->fd, &stream->statbuf) < 0) {
i_error("zlib_istream.fstat() failed: %m");
return NULL;
}
if (!exact)
return &stream->statbuf;
if (zstream->cached_size == (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);
}
stream->statbuf.st_size = zstream->cached_size;
return &stream->statbuf;
}
static void i_stream_zlib_sync(struct istream_private *stream)
{
struct zlib_istream *zstream = (struct zlib_istream *) stream;
zstream->cached_size = (uoff_t)-1;
}
struct istream *i_stream_create_zlib(int fd)
{
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->istream.iostream.close = i_stream_zlib_close;
zstream->istream.max_buffer_size = DEFAULT_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;
}
return i_stream_create(&zstream->istream, NULL, -1);
}
#endif