--- /dev/null
+/* 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));
+}
--- /dev/null
+/* 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();
+}