]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
iostream-temp: If write() to temp file fails at any time, move it back to memory.
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Wed, 14 Sep 2016 11:19:39 +0000 (14:19 +0300)
committerTimo Sirainen <timo.sirainen@dovecot.fi>
Mon, 19 Sep 2016 12:39:37 +0000 (15:39 +0300)
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.

src/lib/iostream-temp.c
src/lib/iostream-temp.h

index be3adcf05deda56bf7dd20256b5c2f959b022565..cdc4ecbc8a48c53d07f941db8bcb9f7bcaf1a257 100644 (file)
@@ -76,6 +76,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)
@@ -85,8 +115,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;
index 8f16ff0e56ae544201d0824d72db2db6d4ee4c96..bf5a45d15d29958243e44409d057dc29ac8ba1d4 100644 (file)
@@ -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