diff options
Diffstat (limited to 'src/lib/istream-failure-at.c')
-rw-r--r-- | src/lib/istream-failure-at.c | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/src/lib/istream-failure-at.c b/src/lib/istream-failure-at.c new file mode 100644 index 0000000..7dccdd3 --- /dev/null +++ b/src/lib/istream-failure-at.c @@ -0,0 +1,97 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "istream-private.h" +#include "istream-failure-at.h" + +struct failure_at_istream { + struct istream_private istream; + int error_code; + char *error_string; + uoff_t failure_offset; +}; + +static void i_stream_failure_at_destroy(struct iostream_private *stream) +{ + struct failure_at_istream *fstream = + container_of(stream, struct failure_at_istream, + istream.iostream); + + i_free(fstream->error_string); +} + +static ssize_t +i_stream_failure_at_read(struct istream_private *stream) +{ + struct failure_at_istream *fstream = + container_of(stream, struct failure_at_istream, istream); + uoff_t new_offset; + ssize_t ret; + + i_stream_seek(stream->parent, stream->parent_start_offset + + stream->istream.v_offset); + + ret = i_stream_read_copy_from_parent(&stream->istream); + new_offset = stream->istream.v_offset + (stream->pos - stream->skip); + if (ret >= 0 && new_offset >= fstream->failure_offset) { + if (stream->istream.v_offset >= fstream->failure_offset) { + /* we already passed the wanted failure offset, + return error immediately. */ + stream->pos = stream->skip; + stream->istream.stream_errno = errno = + fstream->error_code; + io_stream_set_error(&stream->iostream, "%s", + fstream->error_string); + ret = -1; + } else { + /* return data up to the wanted failure offset and + on the next read() call return failure */ + size_t new_pos = fstream->failure_offset - + stream->istream.v_offset + stream->skip; + i_assert(new_pos >= stream->skip && + stream->pos >= new_pos); + ret -= stream->pos - new_pos; + stream->pos = new_pos; + } + } else if (ret < 0 && stream->istream.stream_errno == 0 && + fstream->failure_offset == UOFF_T_MAX) { + /* failure at EOF */ + stream->istream.stream_errno = errno = + fstream->error_code; + io_stream_set_error(&stream->iostream, "%s", + fstream->error_string); + } + return ret; +} + +struct istream * +i_stream_create_failure_at(struct istream *input, uoff_t failure_offset, + int stream_errno, const char *error_string) +{ + struct failure_at_istream *fstream; + + fstream = i_new(struct failure_at_istream, 1); + fstream->istream.max_buffer_size = input->real_stream->max_buffer_size; + fstream->istream.stream_size_passthrough = TRUE; + + fstream->istream.read = i_stream_failure_at_read; + fstream->istream.iostream.destroy = i_stream_failure_at_destroy; + + fstream->istream.istream.readable_fd = input->readable_fd; + fstream->istream.istream.blocking = input->blocking; + fstream->istream.istream.seekable = input->seekable; + + fstream->error_code = stream_errno; + fstream->error_string = i_strdup(error_string); + fstream->failure_offset = failure_offset; + return i_stream_create(&fstream->istream, input, + i_stream_get_fd(input), 0); +} + +struct istream * +i_stream_create_failure_at_eof(struct istream *input, int stream_errno, + const char *error_string) +{ + return i_stream_create_failure_at(input, UOFF_T_MAX, stream_errno, + error_string); +} |