diff --git a/src/lib/iostream-temp.c b/src/lib/iostream-temp.c index 1b3583d5c0..bbdc764948 100644 --- a/src/lib/iostream-temp.c +++ b/src/lib/iostream-temp.c @@ -79,6 +79,36 @@ static int o_stream_temp_move_to_fd(struct temp_ostream *tstream) return 0; } +int o_stream_temp_move_to_memory(struct ostream *output) +{ + struct temp_ostream *tstream = + (struct temp_ostream *)output->real_stream; + unsigned char buf[IO_BLOCK_SIZE]; + uoff_t offset = 0; + ssize_t ret = 0; + + i_assert(tstream->buf == NULL); + tstream->buf = buffer_create_dynamic(default_pool, 8192); + while (offset < tstream->ostream.ostream.offset && + (ret = pread(tstream->fd, buf, sizeof(buf), offset)) > 0) { + if ((size_t)ret > tstream->ostream.ostream.offset - offset) + ret = tstream->ostream.ostream.offset - offset; + buffer_append(tstream->buf, buf, ret); + offset += ret; + } + if (ret < 0) { + /* not really expecting this to happen */ + i_error("iostream-temp %s: read(%s*) failed: %m", + o_stream_get_name(&tstream->ostream.ostream), + tstream->temp_path_prefix); + tstream->ostream.ostream.stream_errno = EIO; + return -1; + } + i_close_fd(&tstream->fd); + tstream->ostream.fd = -1; + return 0; +} + static ssize_t o_stream_temp_fd_sendv(struct temp_ostream *tstream, const struct const_iovec *iov, unsigned int iov_count) @@ -88,8 +118,18 @@ o_stream_temp_fd_sendv(struct temp_ostream *tstream, for (i = 0; i < iov_count; i++) { if (write_full(tstream->fd, iov[i].iov_base, iov[i].iov_len) < 0) { - tstream->ostream.ostream.stream_errno = errno; - return -1; + i_error("iostream-temp %s: write(%s*) failed: %m - moving to memory", + o_stream_get_name(&tstream->ostream.ostream), + tstream->temp_path_prefix); + if (o_stream_temp_move_to_memory(&tstream->ostream.ostream) < 0) + return -1; + for (; i < iov_count; i++) { + buffer_append(tstream->buf, iov[i].iov_base, iov[i].iov_len); + bytes += iov[i].iov_len; + tstream->ostream.ostream.offset += iov[i].iov_len; + } + i_assert(tstream->fd_tried); + return bytes; } bytes += iov[i].iov_len; tstream->ostream.ostream.offset += iov[i].iov_len; diff --git a/src/lib/iostream-temp.h b/src/lib/iostream-temp.h index 8f16ff0e56..bf5a45d15d 100644 --- a/src/lib/iostream-temp.h +++ b/src/lib/iostream-temp.h @@ -25,4 +25,7 @@ struct ostream *iostream_temp_create_sized(const char *temp_path_prefix, struct istream *iostream_temp_finish(struct ostream **output, size_t max_buffer_size); +/* For internal testing: */ +int o_stream_temp_move_to_memory(struct ostream *output); + #endif diff --git a/src/lib/test-iostream-temp.c b/src/lib/test-iostream-temp.c index 46b5e723bc..f1e0496493 100644 --- a/src/lib/test-iostream-temp.c +++ b/src/lib/test-iostream-temp.c @@ -48,6 +48,31 @@ static void test_iostream_temp_create_sized_disk(void) test_end(); } +static void test_iostream_temp_create_write_error(void) +{ + struct ostream *output; + + test_begin("iostream_temp_create_sized() write error"); + output = iostream_temp_create_sized(".", 0, "test", 1); + + test_assert(o_stream_send(output, "123", 3) == 3); + test_assert(o_stream_get_fd(output) != -1); + test_assert(output->offset == 3); + test_assert(o_stream_temp_move_to_memory(output) == 0); + test_assert(o_stream_get_fd(output) == -1); + test_assert(o_stream_send(output, "45", 2) == 2); + test_assert(output->offset == 5); + + const unsigned char *data; + size_t size; + struct istream *input = iostream_temp_finish(&output, 128); + test_assert(i_stream_read_bytes(input, &data, &size, 5) == 1 && + memcmp(data, "12345", 5) == 0); + i_stream_destroy(&input); + + test_end(); +} + static void test_iostream_temp_istream(void) { struct istream *input, *input2, *temp_input; @@ -120,5 +145,6 @@ void test_iostream_temp(void) { test_iostream_temp_create_sized_memory(); test_iostream_temp_create_sized_disk(); + test_iostream_temp_create_write_error(); test_iostream_temp_istream(); }