diff options
Diffstat (limited to 'src/lib/istream-limit.c')
-rw-r--r-- | src/lib/istream-limit.c | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/src/lib/istream-limit.c b/src/lib/istream-limit.c new file mode 100644 index 0000000..f66cef3 --- /dev/null +++ b/src/lib/istream-limit.c @@ -0,0 +1,144 @@ +/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "istream-private.h" + +struct limit_istream { + struct istream_private istream; + + uoff_t v_size; +}; + +static void i_stream_limit_destroy(struct iostream_private *stream) +{ + struct limit_istream *lstream = + container_of(stream, struct limit_istream, istream.iostream); + uoff_t v_offset; + + v_offset = lstream->istream.parent_start_offset + + lstream->istream.istream.v_offset; + if (lstream->istream.parent->seekable || + v_offset > lstream->istream.parent->v_offset) { + /* get to same position in parent stream */ + i_stream_seek(lstream->istream.parent, v_offset); + } +} + +static ssize_t i_stream_limit_read(struct istream_private *stream) +{ + struct limit_istream *lstream = + container_of(stream, struct limit_istream, istream); + uoff_t left; + ssize_t ret; + size_t pos; + + i_stream_seek(stream->parent, lstream->istream.parent_start_offset + + stream->istream.v_offset); + + if (stream->istream.v_offset + + (stream->pos - stream->skip) >= lstream->v_size) { + stream->istream.eof = TRUE; + return -1; + } + + stream->pos -= stream->skip; + stream->skip = 0; + + stream->buffer = i_stream_get_data(stream->parent, &pos); + if (pos > stream->pos) + ret = 0; + else do { + ret = i_stream_read_memarea(stream->parent); + stream->istream.stream_errno = stream->parent->stream_errno; + stream->istream.eof = stream->parent->eof; + stream->buffer = i_stream_get_data(stream->parent, &pos); + } while (pos <= stream->pos && ret > 0); + if (ret == -2) + return -2; + + if (lstream->v_size != UOFF_T_MAX) { + left = lstream->v_size - stream->istream.v_offset; + if (pos >= left) { + pos = left; + stream->istream.eof = TRUE; + } + } + + ret = pos > stream->pos ? (ssize_t)(pos - stream->pos) : + (ret == 0 ? 0 : -1); + stream->pos = pos; + i_assert(ret != -1 || stream->istream.eof || + stream->istream.stream_errno != 0); + return ret; +} + +static int +i_stream_limit_stat(struct istream_private *stream, bool exact) +{ + struct limit_istream *lstream = + container_of(stream, struct limit_istream, istream); + const struct stat *st; + + if (i_stream_stat(stream->parent, exact, &st) < 0) { + stream->istream.stream_errno = stream->parent->stream_errno; + return -1; + } + + stream->statbuf = *st; + if (lstream->v_size != UOFF_T_MAX) + stream->statbuf.st_size = lstream->v_size; + return 0; +} + +static int i_stream_limit_get_size(struct istream_private *stream, + bool exact, uoff_t *size_r) +{ + struct limit_istream *lstream = + container_of(stream, struct limit_istream, istream); + const struct stat *st; + + if (lstream->v_size != UOFF_T_MAX) { + *size_r = lstream->v_size; + return 1; + } + + if (i_stream_stat(&stream->istream, exact, &st) < 0) + return -1; + if (st->st_size == -1) + return 0; + + *size_r = st->st_size; + return 1; +} + +struct istream *i_stream_create_limit(struct istream *input, uoff_t v_size) +{ + struct limit_istream *lstream; + + lstream = i_new(struct limit_istream, 1); + lstream->v_size = v_size; + lstream->istream.max_buffer_size = input->real_stream->max_buffer_size; + + lstream->istream.iostream.destroy = i_stream_limit_destroy; + lstream->istream.read = i_stream_limit_read; + lstream->istream.stat = i_stream_limit_stat; + lstream->istream.get_size = i_stream_limit_get_size; + + lstream->istream.istream.readable_fd = input->readable_fd; + lstream->istream.istream.blocking = input->blocking; + lstream->istream.istream.seekable = input->seekable; + return i_stream_create(&lstream->istream, input, + i_stream_get_fd(input), 0); +} + +struct istream *i_stream_create_range(struct istream *input, + uoff_t v_offset, uoff_t v_size) +{ + uoff_t orig_offset = input->v_offset; + struct istream *ret; + + input->v_offset = v_offset; + ret = i_stream_create_limit(input, v_size); + input->v_offset = orig_offset; + return ret; +} |