From: Timo Sirainen Date: Mon, 16 May 2016 16:26:26 +0000 (+0300) Subject: lib: Don't silently ignore partial writes for o_stream_nsend() X-Git-Tag: 2.3.0.rc1~3721 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9de176ef7f3d28ff486c2a8805110b84389e4f19;p=thirdparty%2Fdovecot%2Fcore.git lib: Don't silently ignore partial writes for o_stream_nsend() Normally o_stream_nsend() should be used only for blocking streams or streams with infinite (or "enough") buffer space. --- diff --git a/src/lib/ostream-private.h b/src/lib/ostream-private.h index a2e39c3b3a..0ca9aa56b9 100644 --- a/src/lib/ostream-private.h +++ b/src/lib/ostream-private.h @@ -40,6 +40,7 @@ struct ostream_private { unsigned int closing:1; unsigned int last_errors_not_checked:1; unsigned int error_handling_disabled:1; + unsigned int noverflow:1; }; struct ostream * diff --git a/src/lib/ostream.c b/src/lib/ostream.c index 6eef92d294..3733d8a45d 100644 --- a/src/lib/ostream.c +++ b/src/lib/ostream.c @@ -230,18 +230,16 @@ ssize_t o_stream_send(struct ostream *stream, const void *data, size_t size) return o_stream_sendv(stream, &iov, 1); } -ssize_t o_stream_sendv(struct ostream *stream, const struct const_iovec *iov, - unsigned int iov_count) +static ssize_t +o_stream_sendv_int(struct ostream *stream, const struct const_iovec *iov, + unsigned int iov_count, bool *overflow_r) { struct ostream_private *_stream = stream->real_stream; unsigned int i; size_t total_size; ssize_t ret; - if (unlikely(stream->closed || stream->stream_errno != 0)) { - errno = stream->stream_errno; - return -1; - } + *overflow_r = FALSE; for (i = 0, total_size = 0; i < iov_count; i++) total_size += iov[i].iov_len; @@ -255,11 +253,24 @@ ssize_t o_stream_sendv(struct ostream *stream, const struct const_iovec *iov, errno = stream->stream_errno; } else { stream->overflow = TRUE; + *overflow_r = TRUE; } } return ret; } +ssize_t o_stream_sendv(struct ostream *stream, const struct const_iovec *iov, + unsigned int iov_count) +{ + bool overflow; + + if (unlikely(stream->closed || stream->stream_errno != 0)) { + errno = stream->stream_errno; + return -1; + } + return o_stream_sendv_int(stream, iov, iov_count, &overflow); +} + ssize_t o_stream_send_str(struct ostream *stream, const char *str) { return o_stream_send(stream, str, strlen(str)); @@ -279,9 +290,14 @@ void o_stream_nsend(struct ostream *stream, const void *data, size_t size) void o_stream_nsendv(struct ostream *stream, const struct const_iovec *iov, unsigned int iov_count) { - if (unlikely(stream->closed || stream->stream_errno != 0)) + bool overflow; + + if (unlikely(stream->closed || stream->stream_errno != 0 || + stream->real_stream->noverflow)) return; - (void)o_stream_sendv(stream, iov, iov_count); + (void)o_stream_sendv_int(stream, iov, iov_count, &overflow); + if (overflow) + stream->real_stream->noverflow = TRUE; stream->real_stream->last_errors_not_checked = TRUE; } @@ -302,6 +318,12 @@ int o_stream_nfinish(struct ostream *stream) { o_stream_nflush(stream); o_stream_ignore_last_errors(stream); + if (stream->stream_errno == 0 && stream->real_stream->noverflow) { + io_stream_set_error(&stream->real_stream->iostream, + "Output stream buffer was full (%"PRIuSIZE_T" bytes)", + o_stream_get_max_buffer_size(stream)); + stream->stream_errno = ENOBUFS; + } return stream->stream_errno != 0 ? -1 : 0; } diff --git a/src/lib/ostream.h b/src/lib/ostream.h index 08503e42e6..bb10c82c6c 100644 --- a/src/lib/ostream.h +++ b/src/lib/ostream.h @@ -119,16 +119,19 @@ ssize_t o_stream_send(struct ostream *stream, const void *data, size_t size); ssize_t o_stream_sendv(struct ostream *stream, const struct const_iovec *iov, unsigned int iov_count); ssize_t o_stream_send_str(struct ostream *stream, const char *str); -/* Send with delayed error handling. o_stream_has_errors() or +/* Send with delayed error handling. o_stream_nfinish() or o_stream_ignore_last_errors() must be called after these functions before - the stream is destroyed. */ + the stream is destroyed. If any of the data can't be sent due to stream's + buffer getting full, all further nsends are ignores and o_stream_nfinish() + will fail. */ void o_stream_nsend(struct ostream *stream, const void *data, size_t size); void o_stream_nsendv(struct ostream *stream, const struct const_iovec *iov, unsigned int iov_count); void o_stream_nsend_str(struct ostream *stream, const char *str); void o_stream_nflush(struct ostream *stream); -/* Flushes the stream and returns -1 if stream->stream_errno is non-zero. - Marks the stream's error handling as completed. */ +/* Marks the stream's error handling as completed. Flushes the stream and + returns -1 if stream->stream_errno is non-zero. Returns failure if any of + the o_stream_nsend*() didn't write all data. */ int o_stream_nfinish(struct ostream *stream); /* Marks the stream's error handling as completed to avoid i_panic() on destroy. */