Skip to content

Commit

Permalink
iostream-temp: If write() to temp file fails at any time, move it bac…
Browse files Browse the repository at this point in the history
…k to memory.

Similarly to if the write() to temp fails during the initial move attempt.
This way even if write() fails due to out of disk space, it's not visible
to caller. An error message is logged in any case.
  • Loading branch information
sirainen authored and GitLab committed Sep 15, 2016
1 parent 061046c commit d97f081
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 2 deletions.
44 changes: 42 additions & 2 deletions src/lib/iostream-temp.c
Expand Up @@ -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)
Expand All @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions src/lib/iostream-temp.h
Expand Up @@ -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
26 changes: 26 additions & 0 deletions src/lib/test-iostream-temp.c
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
}

0 comments on commit d97f081

Please sign in to comment.