]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: Don't silently ignore partial writes for o_stream_nsend()
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Mon, 16 May 2016 16:26:26 +0000 (19:26 +0300)
committerGitLab <gitlab@git.dovecot.net>
Wed, 18 May 2016 12:21:30 +0000 (15:21 +0300)
Normally o_stream_nsend() should be used only for blocking streams or
streams with infinite (or "enough") buffer space.

src/lib/ostream-private.h
src/lib/ostream.c
src/lib/ostream.h

index a2e39c3b3a4dbd8cc8b7776b757c769475f17ca6..0ca9aa56b9f70049f6f35b0d2762206d60db83cf 100644 (file)
@@ -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 *
index 6eef92d29417445ac2a8cc7c45e1d80947dc5b63..3733d8a45d776288a2257c35457d4298d61a8c04 100644 (file)
@@ -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;
 }
 
index 08503e42e6d8043acfec9f9f7166105b29182732..bb10c82c6c0e9a1d50d7b790873ce92e25d07892 100644 (file)
@@ -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. */