]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: i_stream_default_stat() - Get seekable streams' sizes by reading the stream
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Tue, 5 Jun 2018 10:34:47 +0000 (13:34 +0300)
committerTimo Sirainen <timo.sirainen@dovecot.fi>
Wed, 6 Jun 2018 13:25:09 +0000 (16:25 +0300)
After the stream size is found, seek back to original offset.

src/lib/istream-private.h
src/lib/istream.c

index 02b35f3e8d2e9e5f7f36d8b073cfc6ff771e44d4..b9329f7337ab6a17f9f367c44483c26d8d74adf4 100644 (file)
@@ -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(). */
index 7517b642188a3b7cd97f97108238c180cca9807c..0a8f3fd064c90d663c00ec03964fb18bb93560de 100644 (file)
@@ -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);