From: Timo Sirainen Date: Tue, 16 Jun 2015 13:22:18 +0000 (+0300) Subject: lib: Added o_stream_create_failure_at() to inject EIO at given offset in ostream X-Git-Tag: 2.2.19.rc1~328 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9294b9ad2eb112258c247906b53010664f84e57b;p=thirdparty%2Fdovecot%2Fcore.git lib: Added o_stream_create_failure_at() to inject EIO at given offset in ostream --- diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 026a0e4fe8..beebdf6bfe 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -109,6 +109,7 @@ liblib_la_SOURCES = \ numpack.c \ ostream.c \ ostream-buffer.c \ + ostream-failure-at.c \ ostream-file.c \ ostream-hash.c \ ostream-rawlog.c \ @@ -237,6 +238,7 @@ headers = \ nfs-workarounds.h \ numpack.h \ ostream.h \ + ostream-failure-at.h \ ostream-hash.h \ ostream-private.h \ ostream-rawlog.h \ @@ -318,6 +320,7 @@ test_lib_SOURCES = \ test-mempool-alloconly.c \ test-net.c \ test-numpack.c \ + test-ostream-failure-at.c \ test-ostream-file.c \ test-primes.c \ test-printf-format-fix.c \ diff --git a/src/lib/ostream-failure-at.c b/src/lib/ostream-failure-at.c new file mode 100644 index 0000000000..8d753b9cfe --- /dev/null +++ b/src/lib/ostream-failure-at.c @@ -0,0 +1,108 @@ +/* Copyright (c) 2015 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "ostream-private.h" +#include "ostream-failure-at.h" + +struct failure_at_ostream { + struct ostream_private ostream; + char *error_string; + uoff_t failure_offset; + bool failed; +}; + +static void o_stream_failure_at_destroy(struct iostream_private *stream) +{ + struct failure_at_ostream *fstream = + (struct failure_at_ostream *)stream; + + i_free(fstream->error_string); +} + +static ssize_t +o_stream_failure_at_sendv(struct ostream_private *stream, + const struct const_iovec *iov, unsigned int iov_count) +{ + struct failure_at_ostream *fstream = + (struct failure_at_ostream *)stream; + unsigned int i; + struct const_iovec *iov_dup; + unsigned int iov_dup_count; + uoff_t bytes_until_failure; + ssize_t ret; + + if (fstream->failure_offset <= stream->ostream.offset) { + io_stream_set_error(&stream->iostream, "%s", + fstream->error_string); + stream->ostream.stream_errno = errno = EIO; + fstream->failed = TRUE; + return -1; + } + bytes_until_failure = fstream->failure_offset - stream->ostream.offset; + + iov_dup = i_new(struct const_iovec, iov_count); + iov_dup_count = iov_count; + for (i = 0; i < iov_count; i++) { + iov_dup[i] = iov[i]; + if (iov_dup[i].iov_len >= bytes_until_failure) { + iov_dup[i].iov_len = bytes_until_failure; + iov_dup_count = i+1; + break; + } + } + ret = o_stream_sendv(stream->parent, iov_dup, iov_dup_count); + i_free(iov_dup); + + if (ret < 0) { + o_stream_copy_error_from_parent(stream); + return -1; + } + stream->ostream.offset += ret; + return ret; +} + +static int +o_stream_failure_at_flush(struct ostream_private *stream) +{ + struct failure_at_ostream *fstream = + (struct failure_at_ostream *)stream; + + if (fstream->failed) { + io_stream_set_error(&stream->iostream, "%s", + fstream->error_string); + stream->ostream.stream_errno = errno = EIO; + return -1; + } + return o_stream_flush(stream->parent); +} + +struct ostream * +o_stream_create_failure_at(struct ostream *output, uoff_t failure_offset, + const char *error_string) +{ + struct failure_at_ostream *fstream; + + fstream = i_new(struct failure_at_ostream, 1); + fstream->ostream.sendv = o_stream_failure_at_sendv; + fstream->ostream.flush = o_stream_failure_at_flush; + fstream->ostream.iostream.destroy = o_stream_failure_at_destroy; + fstream->failure_offset = failure_offset; + fstream->error_string = i_strdup(error_string); + return o_stream_create(&fstream->ostream, output, + o_stream_get_fd(output)); +} + +struct ostream * +o_stream_create_failure_at_flush(struct ostream *output, const char *error_string) +{ + struct failure_at_ostream *fstream; + + fstream = i_new(struct failure_at_ostream, 1); + fstream->ostream.flush = o_stream_failure_at_flush; + fstream->ostream.iostream.destroy = o_stream_failure_at_destroy; + fstream->error_string = i_strdup(error_string); + fstream->failed = TRUE; + return o_stream_create(&fstream->ostream, output, + o_stream_get_fd(output)); +} diff --git a/src/lib/ostream-failure-at.h b/src/lib/ostream-failure-at.h new file mode 100644 index 0000000000..61e1d7201a --- /dev/null +++ b/src/lib/ostream-failure-at.h @@ -0,0 +1,10 @@ +#ifndef OSTREAM_FAILURE_AT_H +#define OSTREAM_FAILURE_AT_H + +struct ostream * +o_stream_create_failure_at(struct ostream *output, uoff_t failure_offset, + const char *error_string); +struct ostream * +o_stream_create_failure_at_flush(struct ostream *output, const char *error_string); + +#endif diff --git a/src/lib/test-lib.c b/src/lib/test-lib.c index 38d74c88c3..f4e081cab1 100644 --- a/src/lib/test-lib.c +++ b/src/lib/test-lib.c @@ -36,6 +36,7 @@ int main(void) test_mempool_alloconly, test_net, test_numpack, + test_ostream_failure_at, test_ostream_file, test_primes, test_printf_format_fix, diff --git a/src/lib/test-lib.h b/src/lib/test-lib.h index d4dad7f468..1cd3dba0c2 100644 --- a/src/lib/test-lib.h +++ b/src/lib/test-lib.h @@ -38,6 +38,7 @@ void test_mempool_alloconly(void); enum fatal_test_state fatal_mempool(int); void test_net(void); void test_numpack(void); +void test_ostream_failure_at(void); void test_ostream_file(void); void test_primes(void); void test_printf_format_fix(void); diff --git a/src/lib/test-ostream-failure-at.c b/src/lib/test-ostream-failure-at.c new file mode 100644 index 0000000000..fea58b30a2 --- /dev/null +++ b/src/lib/test-ostream-failure-at.c @@ -0,0 +1,52 @@ +/* Copyright (c) 2015 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "buffer.h" +#include "ostream.h" +#include "ostream-failure-at.h" + +#define TEST_DATA_LENGTH 128 +#define TEST_ERRMSG "test-ostream-failure-at error triggered" + +void test_ostream_failure_at(void) +{ + unsigned char test_data[TEST_DATA_LENGTH]; + struct ostream *output, *buf_output; + buffer_t *buf = buffer_create_dynamic(pool_datastack_create(), 256); + unsigned int i; + + test_begin("ostream failure at"); + for (i = 0; i < sizeof(test_data); i++) + test_data[i] = i; + for (i = 0; i < TEST_DATA_LENGTH; i++) { + buf_output = o_stream_create_buffer(buf); + output = o_stream_create_failure_at(buf_output, i, TEST_ERRMSG); + if (i > 0) + test_assert(o_stream_send(output, test_data, sizeof(test_data)) == i); + test_assert_idx(o_stream_send(output, test_data, sizeof(test_data)) == -1 && + output->offset == i && + output->stream_errno == EIO && + strcmp(o_stream_get_error(output), TEST_ERRMSG) == 0, i); + o_stream_destroy(&output); + o_stream_destroy(&buf_output); + } + /* shouldn't fail */ + buf_output = o_stream_create_buffer(buf); + output = o_stream_create_failure_at(buf_output, TEST_DATA_LENGTH, TEST_ERRMSG); + test_assert(o_stream_send(output, test_data, sizeof(test_data)) == TEST_DATA_LENGTH); + test_assert(o_stream_flush(output) > 0 && + output->offset == TEST_DATA_LENGTH && + output->stream_errno == 0); + o_stream_destroy(&output); + o_stream_destroy(&buf_output); + + /* fail at flush */ + buf_output = o_stream_create_buffer(buf); + output = o_stream_create_failure_at_flush(buf_output, TEST_ERRMSG); + test_assert(o_stream_send(output, test_data, sizeof(test_data)) == TEST_DATA_LENGTH); + test_assert(o_stream_flush(output) < 0 && output->stream_errno == EIO && + strcmp(o_stream_get_error(output), TEST_ERRMSG) == 0); + o_stream_destroy(&output); + o_stream_destroy(&buf_output); + test_end(); +}