diff options
Diffstat (limited to 'src/lib/ostream-failure-at.c')
-rw-r--r-- | src/lib/ostream-failure-at.c | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/src/lib/ostream-failure-at.c b/src/lib/ostream-failure-at.c new file mode 100644 index 0000000..87bd7b8 --- /dev/null +++ b/src/lib/ostream-failure-at.c @@ -0,0 +1,123 @@ +/* Copyright (c) 2015-2018 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 = + container_of(stream, struct failure_at_ostream, + ostream.iostream); + + i_free(fstream->error_string); + o_stream_unref(&fstream->ostream.parent); +} + +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 = + container_of(stream, struct failure_at_ostream, ostream); + unsigned int i; + struct const_iovec *iov_dup; + unsigned int iov_dup_count; + uoff_t bytes_until_failure, blocking_bytes_count = 0; + ssize_t ret; + + if (stream->ostream.blocking) { + /* blocking ostream must return either a full success or a + failure. if the current write would go past failure_offset, + return a failure now before writing anything. */ + for (i = 0; i < iov_count; i++) + blocking_bytes_count += iov[i].iov_len; + if (blocking_bytes_count > 0) { + /* if we're exactly at the failure offset after this + write, fail it only on the next write. */ + blocking_bytes_count--; + } + } + + if (fstream->failure_offset <= stream->ostream.offset + blocking_bytes_count) { + 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 = + container_of(stream, struct failure_at_ostream, ostream); + + 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_parent(stream); +} + +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)); +} |