From: Timo Sirainen Date: Tue, 16 Jun 2015 13:21:56 +0000 (+0300) Subject: lib: Added i_stream_create_failure_at() to inject EIO at given offset in istream. X-Git-Tag: 2.2.19.rc1~329 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=289bb39856d74484c1622d096922acf126dd90fc;p=thirdparty%2Fdovecot%2Fcore.git lib: Added i_stream_create_failure_at() to inject EIO at given offset in istream. --- diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index a6f47e0759..026a0e4fe8 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -65,6 +65,7 @@ liblib_la_SOURCES = \ istream-concat.c \ istream-crlf.c \ istream-data.c \ + istream-failure-at.c \ istream-file.c \ istream-hash.c \ istream-jsonstr.c \ @@ -203,6 +204,7 @@ headers = \ istream-chain.h \ istream-concat.h \ istream-crlf.h \ + istream-failure-at.h \ istream-file-private.h \ istream-hash.h \ istream-jsonstr.h \ @@ -306,6 +308,7 @@ test_lib_SOURCES = \ test-istream-base64-encoder.c \ test-istream-concat.c \ test-istream-crlf.c \ + test-istream-failure-at.c \ test-istream-seekable.c \ test-istream-tee.c \ test-istream-unix.c \ diff --git a/src/lib/istream-failure-at.c b/src/lib/istream-failure-at.c new file mode 100644 index 0000000000..f87909e032 --- /dev/null +++ b/src/lib/istream-failure-at.c @@ -0,0 +1,88 @@ +/* Copyright (c) 2015 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; + char *error_string; + uoff_t failure_offset; +}; + +static void i_stream_failure_at_destroy(struct iostream_private *stream) +{ + struct failure_at_istream *fstream = + (struct failure_at_istream *)stream; + + i_free(fstream->error_string); +} + +static ssize_t +i_stream_failure_at_read(struct istream_private *stream) +{ + struct failure_at_istream *fstream = (struct failure_at_istream *)stream; + 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 = EIO; + 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)-1) { + /* failure at EOF */ + stream->istream.stream_errno = errno = EIO; + 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, + 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.blocking = input->blocking; + fstream->istream.istream.seekable = input->seekable; + + fstream->error_string = i_strdup(error_string); + fstream->failure_offset = failure_offset; + return i_stream_create(&fstream->istream, input, + i_stream_get_fd(input)); +} + +struct istream * +i_stream_create_failure_at_eof(struct istream *input, const char *error_string) +{ + return i_stream_create_failure_at(input, (uoff_t)-1, error_string); +} diff --git a/src/lib/istream-failure-at.h b/src/lib/istream-failure-at.h new file mode 100644 index 0000000000..560ea26b36 --- /dev/null +++ b/src/lib/istream-failure-at.h @@ -0,0 +1,10 @@ +#ifndef ISTREAM_FAILURE_AT_H +#define ISTREAM_FAILURE_AT_H + +struct istream * +i_stream_create_failure_at(struct istream *input, uoff_t failure_offset, + const char *error_string); +struct istream * +i_stream_create_failure_at_eof(struct istream *input, const char *error_string); + +#endif diff --git a/src/lib/test-istream-failure-at.c b/src/lib/test-istream-failure-at.c new file mode 100644 index 0000000000..895c3a588e --- /dev/null +++ b/src/lib/test-istream-failure-at.c @@ -0,0 +1,46 @@ +/* Copyright (c) 2015 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "istream.h" +#include "istream-failure-at.h" + +#define TEST_DATA_LENGTH 128 +#define TEST_ERRMSG "test-istream-failure-at error triggered" + +void test_istream_failure_at(void) +{ + struct istream *input, *data_input; + unsigned char test_data[TEST_DATA_LENGTH]; + unsigned int i; + ssize_t ret; + + test_begin("istream failure at"); + for (i = 0; i < sizeof(test_data); i++) + test_data[i] = i; + data_input = i_stream_create_from_data(test_data, sizeof(test_data)); + for (i = 0; i < TEST_DATA_LENGTH; i++) { + i_stream_seek(data_input, 0); + input = i_stream_create_failure_at(data_input, i, TEST_ERRMSG); + while ((ret = i_stream_read(input)) > 0) + i_stream_skip(input, ret); + test_assert_idx(ret == -1 && input->v_offset == i && + input->stream_errno == EIO && + strcmp(i_stream_get_error(input), TEST_ERRMSG) == 0, i); + i_stream_destroy(&input); + } + /* shouldn't fail */ + i_stream_seek(data_input, 0); + input = i_stream_create_failure_at(data_input, TEST_DATA_LENGTH, TEST_ERRMSG); + while ((ret = i_stream_read(input)) > 0) + i_stream_skip(input, ret); + test_assert(ret == -1 && input->stream_errno == 0); + /* fail at EOF */ + i_stream_seek(data_input, 0); + input = i_stream_create_failure_at_eof(data_input, TEST_ERRMSG); + while ((ret = i_stream_read(input)) > 0) + i_stream_skip(input, ret); + test_assert_idx(ret == -1 && input->v_offset == TEST_DATA_LENGTH && + input->stream_errno == EIO && + strcmp(i_stream_get_error(input), TEST_ERRMSG) == 0, i); + test_end(); +} diff --git a/src/lib/test-lib.c b/src/lib/test-lib.c index 70caa87f0e..38d74c88c3 100644 --- a/src/lib/test-lib.c +++ b/src/lib/test-lib.c @@ -26,6 +26,7 @@ int main(void) test_istream_base64_encoder, test_istream_concat, test_istream_crlf, + test_istream_failure_at, test_istream_seekable, test_istream_tee, test_istream_unix, diff --git a/src/lib/test-lib.h b/src/lib/test-lib.h index e6ed093568..d4dad7f468 100644 --- a/src/lib/test-lib.h +++ b/src/lib/test-lib.h @@ -27,6 +27,7 @@ void test_istream_base64_decoder(void); void test_istream_base64_encoder(void); void test_istream_concat(void); void test_istream_crlf(void); +void test_istream_failure_at(void); void test_istream_seekable(void); void test_istream_tee(void); void test_istream_unix(void);