]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: Added o_stream_create_failure_at() to inject EIO at given offset in ostream
authorTimo Sirainen <tss@iki.fi>
Tue, 16 Jun 2015 13:22:18 +0000 (16:22 +0300)
committerTimo Sirainen <tss@iki.fi>
Tue, 16 Jun 2015 13:22:18 +0000 (16:22 +0300)
src/lib/Makefile.am
src/lib/ostream-failure-at.c [new file with mode: 0644]
src/lib/ostream-failure-at.h [new file with mode: 0644]
src/lib/test-lib.c
src/lib/test-lib.h
src/lib/test-ostream-failure-at.c [new file with mode: 0644]

index 026a0e4fe83f282b903602f1e1b53b519c652fd5..beebdf6bfeee5e6db720653034334eb81f4516fd 100644 (file)
@@ -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 (file)
index 0000000..8d753b9
--- /dev/null
@@ -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 (file)
index 0000000..61e1d72
--- /dev/null
@@ -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
index 38d74c88c3eeb270f4c4b83025af51829e818a3a..f4e081cab10a4da6000caf60f3a1fd0038465dae 100644 (file)
@@ -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,
index d4dad7f46805d1358ca60a58eed221a542c94e87..1cd3dba0c256bc3440ea6410230f7b8f56fa66e1 100644 (file)
@@ -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 (file)
index 0000000..fea58b3
--- /dev/null
@@ -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();
+}