From: Timo Sirainen Date: Tue, 5 Jun 2018 10:34:47 +0000 (+0300) Subject: lib: i_stream_default_stat() - Get seekable streams' sizes by reading the stream X-Git-Tag: 2.3.2.rc1~57 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=bd7afb3d1136b98ea93ee20b72b503571c4102f8;p=thirdparty%2Fdovecot%2Fcore.git lib: i_stream_default_stat() - Get seekable streams' sizes by reading the stream After the stream size is found, seek back to original offset. --- diff --git a/src/lib/istream-private.h b/src/lib/istream-private.h index 02b35f3e8d..b9329f7337 100644 --- a/src/lib/istream-private.h +++ b/src/lib/istream-private.h @@ -45,6 +45,9 @@ struct istream_private { struct istream *parent; /* for filter streams */ uoff_t parent_start_offset; + /* Initially (uoff_t)-1. Otherwise it's the exact known stream size, + which can be used by stat() / get_size(). */ + uoff_t cached_stream_size; /* parent stream's expected offset is kept here. i_stream_read() always seeks parent stream to here before calling read(). */ diff --git a/src/lib/istream.c b/src/lib/istream.c index 7517b64218..0a8f3fd064 100644 --- a/src/lib/istream.c +++ b/src/lib/istream.c @@ -1066,6 +1066,28 @@ bool i_stream_nonseekable_try_seek(struct istream_private *stream, return TRUE; } +static int +seekable_i_stream_get_size(struct istream_private *stream) +{ + if (stream->cached_stream_size == (uoff_t)-1) { + uoff_t old_offset = stream->istream.v_offset; + ssize_t ret; + + do { + i_stream_skip(&stream->istream, + i_stream_get_data_size(&stream->istream)); + } while ((ret = i_stream_read(&stream->istream)) > 0); + i_assert(ret == -1); + if (stream->istream.stream_errno != 0) + return -1; + + stream->cached_stream_size = stream->istream.v_offset; + i_stream_seek(&stream->istream, old_offset); + } + stream->statbuf.st_size = stream->cached_stream_size; + return 0; +} + static int i_stream_default_stat(struct istream_private *stream, bool exact) { @@ -1082,6 +1104,15 @@ i_stream_default_stat(struct istream_private *stream, bool exact) if (exact && !stream->stream_size_passthrough) { /* exact size is not known, even if parent returned something */ stream->statbuf.st_size = -1; + if (stream->istream.seekable) { + if (seekable_i_stream_get_size(stream) < 0) + return -1; + } + } else { + /* When exact=FALSE always return the parent stat's size, even + if we know the exact value. This is necessary because + otherwise e.g. mbox code can see two different values and + think that the mbox file keeps changing. */ } return 0; } @@ -1159,6 +1190,7 @@ i_stream_create(struct istream_private *_stream, struct istream *parent, int fd, _stream->statbuf.st_atime = _stream->statbuf.st_mtime = _stream->statbuf.st_ctime = ioloop_time; + _stream->cached_stream_size = (uoff_t)-1; io_stream_init(&_stream->iostream);