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

index a6f47e075923929b12c8e2edfc0e9473355b1eaa..026a0e4fe83f282b903602f1e1b53b519c652fd5 100644 (file)
@@ -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 (file)
index 0000000..f87909e
--- /dev/null
@@ -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 (file)
index 0000000..560ea26
--- /dev/null
@@ -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 (file)
index 0000000..895c3a5
--- /dev/null
@@ -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();
+}
index 70caa87f0ed28acddbeb5073f30904d9192f625b..38d74c88c3eeb270f4c4b83025af51829e818a3a 100644 (file)
@@ -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,
index e6ed09356853612a73998b2a74679680cce88e5f..d4dad7f46805d1358ca60a58eed221a542c94e87 100644 (file)
@@ -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);