Skip to content

Commit

Permalink
lib: Don't silently ignore partial writes for o_stream_nsend()
Browse files Browse the repository at this point in the history
Normally o_stream_nsend() should be used only for blocking streams or
streams with infinite (or "enough") buffer space.
  • Loading branch information
sirainen authored and GitLab committed May 18, 2016
1 parent 61d89b0 commit 9de176e
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 12 deletions.
1 change: 1 addition & 0 deletions src/lib/ostream-private.h
Expand Up @@ -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 *
Expand Down
38 changes: 30 additions & 8 deletions src/lib/ostream.c
Expand Up @@ -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;
Expand All @@ -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));
Expand All @@ -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;
}

Expand All @@ -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;
}

Expand Down
11 changes: 7 additions & 4 deletions src/lib/ostream.h
Expand Up @@ -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. */
Expand Down

0 comments on commit 9de176e

Please sign in to comment.