bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen#include "lib.h"
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen#include "buffer.h"
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen#include "str.h"
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen#include "safe-mkstemp.h"
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen#include "write-full.h"
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen#include "istream-private.h"
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen#include "ostream-private.h"
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen#include "iostream-temp.h"
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen#include <unistd.h>
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainen#define IOSTREAM_TEMP_MAX_BUF_SIZE_DEFAULT (1024*128)
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainenstruct temp_ostream {
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen struct ostream_private ostream;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen char *temp_path_prefix;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen enum iostream_temp_flags flags;
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainen size_t max_mem_size;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen struct istream *dupstream;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen uoff_t dupstream_offset, dupstream_start_offset;
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen char *name;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen buffer_t *buf;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen int fd;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen bool fd_tried;
d5e839aea288aceaddae28a1578cebda3c9e3b58Timo Sirainen uoff_t fd_size;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen};
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainenstatic bool o_stream_temp_dup_cancel(struct temp_ostream *tstream,
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen enum ostream_send_istream_result *res_r);
25ec868bd8b5375e1c1c4c3331d761667ddfe26cTimo Sirainen
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainenstatic void
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Siraineno_stream_temp_close(struct iostream_private *stream,
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen bool close_parent ATTR_UNUSED)
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen{
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen struct temp_ostream *tstream = (struct temp_ostream *)stream;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
7b032348d7bbb93ff96188289d3dfc1899b9abb3Josef 'Jeff' Sipek i_close_fd(&tstream->fd);
6307d76096764e66bddc63d4a3e5a1aa19cc528fJosef 'Jeff' Sipek buffer_free(&tstream->buf);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen i_free(tstream->temp_path_prefix);
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen i_free(tstream->name);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen}
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainenstatic int o_stream_temp_move_to_fd(struct temp_ostream *tstream)
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen{
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen string_t *path;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen if (tstream->fd_tried)
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen return -1;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen tstream->fd_tried = TRUE;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen path = t_str_new(128);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen str_append(path, tstream->temp_path_prefix);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen tstream->fd = safe_mkstemp_hostpid(path, 0600, (uid_t)-1, (gid_t)-1);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen if (tstream->fd == -1) {
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen i_error("safe_mkstemp(%s) failed: %m", str_c(path));
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen return -1;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen }
46b823ac3bce2c0f9f0fc73911e48d3a77b04fbeTimo Sirainen if (i_unlink(str_c(path)) < 0) {
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen i_close_fd(&tstream->fd);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen return -1;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen }
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen if (write_full(tstream->fd, tstream->buf->data, tstream->buf->used) < 0) {
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen i_error("write(%s) failed: %m", str_c(path));
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen i_close_fd(&tstream->fd);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen return -1;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen }
e245fb1302121d2bc2580f61e040c2c8a558ee9eTimo Sirainen /* make the fd available also to o_stream_get_fd(),
e245fb1302121d2bc2580f61e040c2c8a558ee9eTimo Sirainen e.g. for unit tests */
e245fb1302121d2bc2580f61e040c2c8a558ee9eTimo Sirainen tstream->ostream.fd = tstream->fd;
d5e839aea288aceaddae28a1578cebda3c9e3b58Timo Sirainen tstream->fd_size = tstream->buf->used;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen buffer_free(&tstream->buf);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen return 0;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen}
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
d97f081a3de44852197ced772e21560c108895a6Timo Sirainenint o_stream_temp_move_to_memory(struct ostream *output)
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen{
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen struct temp_ostream *tstream =
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen (struct temp_ostream *)output->real_stream;
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen unsigned char buf[IO_BLOCK_SIZE];
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen uoff_t offset = 0;
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen ssize_t ret = 0;
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen i_assert(tstream->buf == NULL);
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen tstream->buf = buffer_create_dynamic(default_pool, 8192);
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen while (offset < tstream->ostream.ostream.offset &&
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen (ret = pread(tstream->fd, buf, sizeof(buf), offset)) > 0) {
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen if ((size_t)ret > tstream->ostream.ostream.offset - offset)
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen ret = tstream->ostream.ostream.offset - offset;
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen buffer_append(tstream->buf, buf, ret);
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen offset += ret;
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen }
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen if (ret < 0) {
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen /* not really expecting this to happen */
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen i_error("iostream-temp %s: read(%s*) failed: %m",
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen o_stream_get_name(&tstream->ostream.ostream),
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen tstream->temp_path_prefix);
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen tstream->ostream.ostream.stream_errno = EIO;
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen return -1;
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen }
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen i_close_fd(&tstream->fd);
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen tstream->ostream.fd = -1;
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen return 0;
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen}
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainenstatic ssize_t
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Siraineno_stream_temp_fd_sendv(struct temp_ostream *tstream,
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen const struct const_iovec *iov, unsigned int iov_count)
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen{
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen size_t bytes = 0;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen unsigned int i;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen for (i = 0; i < iov_count; i++) {
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen if (write_full(tstream->fd, iov[i].iov_base, iov[i].iov_len) < 0) {
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen i_error("iostream-temp %s: write(%s*) failed: %m - moving to memory",
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen o_stream_get_name(&tstream->ostream.ostream),
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen tstream->temp_path_prefix);
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen if (o_stream_temp_move_to_memory(&tstream->ostream.ostream) < 0)
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen return -1;
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen for (; i < iov_count; i++) {
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen buffer_append(tstream->buf, iov[i].iov_base, iov[i].iov_len);
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen bytes += iov[i].iov_len;
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen tstream->ostream.ostream.offset += iov[i].iov_len;
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen }
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen i_assert(tstream->fd_tried);
d97f081a3de44852197ced772e21560c108895a6Timo Sirainen return bytes;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen }
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen bytes += iov[i].iov_len;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen tstream->ostream.ostream.offset += iov[i].iov_len;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen }
d5e839aea288aceaddae28a1578cebda3c9e3b58Timo Sirainen tstream->fd_size += bytes;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen return bytes;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen}
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainenstatic ssize_t
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Siraineno_stream_temp_sendv(struct ostream_private *stream,
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen const struct const_iovec *iov, unsigned int iov_count)
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen{
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen struct temp_ostream *tstream = (struct temp_ostream *)stream;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen ssize_t ret = 0;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen unsigned int i;
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen enum ostream_send_istream_result res;
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen tstream->flags &= ~IOSTREAM_TEMP_FLAG_TRY_FD_DUP;
25ec868bd8b5375e1c1c4c3331d761667ddfe26cTimo Sirainen if (tstream->dupstream != NULL) {
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen if (o_stream_temp_dup_cancel(tstream, &res))
25ec868bd8b5375e1c1c4c3331d761667ddfe26cTimo Sirainen return -1;
25ec868bd8b5375e1c1c4c3331d761667ddfe26cTimo Sirainen }
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen if (tstream->fd != -1)
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen return o_stream_temp_fd_sendv(tstream, iov, iov_count);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen for (i = 0; i < iov_count; i++) {
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainen if (tstream->buf->used + iov[i].iov_len > tstream->max_mem_size) {
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen if (o_stream_temp_move_to_fd(tstream) == 0) {
adea69875046ece77dc36abd3f88a241a3f17ad9Timo Sirainen i_assert(tstream->fd != -1);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen return o_stream_temp_fd_sendv(tstream, iov+i,
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen iov_count-i);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen }
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen /* failed to move to temp fd, just keep it in memory */
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen }
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen buffer_append(tstream->buf, iov[i].iov_base, iov[i].iov_len);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen ret += iov[i].iov_len;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen stream->ostream.offset += iov[i].iov_len;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen }
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen return ret;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen}
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainenstatic bool o_stream_temp_dup_cancel(struct temp_ostream *tstream,
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen enum ostream_send_istream_result *res_r)
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen{
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen struct istream *input;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen uoff_t size = tstream->dupstream_offset -
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen tstream->dupstream_start_offset;
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen bool ret = TRUE; /* use res_r to return error */
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen i_stream_seek(tstream->dupstream, tstream->dupstream_start_offset);
c076ad69e28e7d41af83ada84e12019793ffcfa2Timo Sirainen tstream->ostream.ostream.offset = 0;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen input = i_stream_create_limit(tstream->dupstream, size);
c076ad69e28e7d41af83ada84e12019793ffcfa2Timo Sirainen i_stream_unref(&tstream->dupstream);
c076ad69e28e7d41af83ada84e12019793ffcfa2Timo Sirainen
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen *res_r = io_stream_copy(&tstream->ostream.ostream, input);
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen switch (*res_r) {
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen case OSTREAM_SEND_ISTREAM_RESULT_FINISHED:
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen /* everything copied */
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen ret = FALSE;
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen break;
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT:
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT:
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen i_unreached();
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT:
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen tstream->ostream.ostream.stream_errno = input->stream_errno;
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen io_stream_set_error(&tstream->ostream.iostream,
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen "iostream-temp: read(%s) failed: %s",
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen i_stream_get_name(input),
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen i_stream_get_error(input));
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen break;
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT:
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen break;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen }
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen i_stream_destroy(&input);
6adf683655750bcb809275cd65dc75fd12214198Timo Sirainen return ret;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen}
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainenstatic bool
378e6cb162b355d6f103526505bc00b9a78962e7Timo Siraineno_stream_temp_dup_istream(struct temp_ostream *outstream,
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen struct istream *instream,
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen enum ostream_send_istream_result *res_r)
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen{
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen uoff_t in_size;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen if (!instream->readable_fd || i_stream_get_fd(instream) == -1)
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen return FALSE;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen if (i_stream_get_size(instream, TRUE, &in_size) <= 0) {
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen if (outstream->dupstream != NULL)
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen return o_stream_temp_dup_cancel(outstream, res_r);
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen return FALSE;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen }
61d57efe9dee0dd38b0f726ef85e3c710cb655fcTimo Sirainen i_assert(instream->v_offset <= in_size);
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen if (outstream->dupstream == NULL) {
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen outstream->dupstream = instream;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen outstream->dupstream_start_offset = instream->v_offset;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen i_stream_ref(outstream->dupstream);
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen } else {
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen if (outstream->dupstream != instream ||
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen outstream->dupstream_offset != instream->v_offset ||
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen outstream->dupstream_offset > in_size)
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen return o_stream_temp_dup_cancel(outstream, res_r);
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen }
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen i_stream_seek(instream, in_size);
c076ad69e28e7d41af83ada84e12019793ffcfa2Timo Sirainen /* we should be at EOF now. o_stream_send_istream() asserts if
c076ad69e28e7d41af83ada84e12019793ffcfa2Timo Sirainen eof isn't set. */
c076ad69e28e7d41af83ada84e12019793ffcfa2Timo Sirainen instream->eof = TRUE;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen outstream->dupstream_offset = instream->v_offset;
c076ad69e28e7d41af83ada84e12019793ffcfa2Timo Sirainen outstream->ostream.ostream.offset =
c076ad69e28e7d41af83ada84e12019793ffcfa2Timo Sirainen outstream->dupstream_offset - outstream->dupstream_start_offset;
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen *res_r = OSTREAM_SEND_ISTREAM_RESULT_FINISHED;
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen return TRUE;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen}
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainenstatic enum ostream_send_istream_result
378e6cb162b355d6f103526505bc00b9a78962e7Timo Siraineno_stream_temp_send_istream(struct ostream_private *_outstream,
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen struct istream *instream)
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen{
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen struct temp_ostream *outstream = (struct temp_ostream *)_outstream;
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen enum ostream_send_istream_result res;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen if ((outstream->flags & IOSTREAM_TEMP_FLAG_TRY_FD_DUP) != 0) {
e20f5920759215f328ce12dcca071b4e7dda3d48Timo Sirainen if (o_stream_temp_dup_istream(outstream, instream, &res))
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen return res;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen outstream->flags &= ~IOSTREAM_TEMP_FLAG_TRY_FD_DUP;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen }
46631c1d903c409444b1b1c4a1d41a033c09ee37Timo Sirainen return io_stream_copy(&outstream->ostream.ostream, instream);
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen}
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainenstatic int
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Siraineno_stream_temp_write_at(struct ostream_private *stream,
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen const void *data, size_t size, uoff_t offset)
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen{
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen struct temp_ostream *tstream = (struct temp_ostream *)stream;
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen if (tstream->fd == -1) {
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen i_assert(stream->ostream.offset == tstream->buf->used);
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen buffer_write(tstream->buf, offset, data, size);
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen stream->ostream.offset = tstream->buf->used;
d5e839aea288aceaddae28a1578cebda3c9e3b58Timo Sirainen } else {
d5e839aea288aceaddae28a1578cebda3c9e3b58Timo Sirainen if (pwrite_full(tstream->fd, data, size, offset) < 0) {
d5e839aea288aceaddae28a1578cebda3c9e3b58Timo Sirainen stream->ostream.stream_errno = errno;
d5e839aea288aceaddae28a1578cebda3c9e3b58Timo Sirainen i_close_fd(&tstream->fd);
d5e839aea288aceaddae28a1578cebda3c9e3b58Timo Sirainen return -1;
d5e839aea288aceaddae28a1578cebda3c9e3b58Timo Sirainen }
d5e839aea288aceaddae28a1578cebda3c9e3b58Timo Sirainen if (tstream->fd_size < offset + size)
d5e839aea288aceaddae28a1578cebda3c9e3b58Timo Sirainen tstream->fd_size = offset + size;
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen }
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen return 0;
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen}
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen
153ed0fbca1f5f944b70937dfd71911db172ca97Timo Sirainenstatic int o_stream_temp_seek(struct ostream_private *_stream, uoff_t offset)
153ed0fbca1f5f944b70937dfd71911db172ca97Timo Sirainen{
153ed0fbca1f5f944b70937dfd71911db172ca97Timo Sirainen _stream->ostream.offset = offset;
153ed0fbca1f5f944b70937dfd71911db172ca97Timo Sirainen return 0;
153ed0fbca1f5f944b70937dfd71911db172ca97Timo Sirainen}
153ed0fbca1f5f944b70937dfd71911db172ca97Timo Sirainen
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainenstruct ostream *iostream_temp_create(const char *temp_path_prefix,
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen enum iostream_temp_flags flags)
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen{
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen return iostream_temp_create_named(temp_path_prefix, flags, "");
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen}
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainenstruct ostream *iostream_temp_create_named(const char *temp_path_prefix,
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen enum iostream_temp_flags flags,
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen const char *name)
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainen{
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainen return iostream_temp_create_sized(temp_path_prefix, flags, name,
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainen IOSTREAM_TEMP_MAX_BUF_SIZE_DEFAULT);
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainen}
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainen
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainenstruct ostream *iostream_temp_create_sized(const char *temp_path_prefix,
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainen enum iostream_temp_flags flags,
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainen const char *name,
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainen size_t max_mem_size)
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen{
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen struct temp_ostream *tstream;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen struct ostream *output;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen tstream = i_new(struct temp_ostream, 1);
da574ef2db96f258d24bc4c89a77833036d13a95Timo Sirainen tstream->ostream.ostream.blocking = TRUE;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen tstream->ostream.sendv = o_stream_temp_sendv;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen tstream->ostream.send_istream = o_stream_temp_send_istream;
fb176cdc122707cda985ab3c09c02ccf3cec0af1Timo Sirainen tstream->ostream.write_at = o_stream_temp_write_at;
153ed0fbca1f5f944b70937dfd71911db172ca97Timo Sirainen tstream->ostream.seek = o_stream_temp_seek;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen tstream->ostream.iostream.close = o_stream_temp_close;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen tstream->temp_path_prefix = i_strdup(temp_path_prefix);
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen tstream->flags = flags;
8a8a3b43987b5ade914f22765e51c9e3de8179d3Timo Sirainen tstream->max_mem_size = max_mem_size;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen tstream->buf = buffer_create_dynamic(default_pool, 8192);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen tstream->fd = -1;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen output = o_stream_create(&tstream->ostream, NULL, -1);
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen tstream->name = i_strdup(name);
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen if (name[0] == '\0') {
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen o_stream_set_name(output, t_strdup_printf(
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen "(temp iostream in %s)", temp_path_prefix));
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen } else {
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen o_stream_set_name(output, t_strdup_printf(
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen "(temp iostream in %s for %s)", temp_path_prefix, name));
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen }
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen return output;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen}
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainenstatic void iostream_temp_buf_destroyed(buffer_t *buf)
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen{
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen buffer_free(&buf);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen}
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainenstruct istream *iostream_temp_finish(struct ostream **output,
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen size_t max_buffer_size)
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen{
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen struct temp_ostream *tstream =
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen (struct temp_ostream *)(*output)->real_stream;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen struct istream *input, *input2;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen uoff_t abs_offset, size;
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen const char *for_path;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen int fd;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen if (tstream->name[0] == '\0')
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen for_path = "";
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen else
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen for_path = t_strdup_printf(" for %s", tstream->name);
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen
e44028b5df7045dd9e7f324175e73e3ff490cb5dTimo Sirainen if (tstream->dupstream != NULL && !tstream->dupstream->closed) {
d28179fd78550a58be44dcb1e3e830ab7d33172dTimo Sirainen abs_offset = i_stream_get_absolute_offset(tstream->dupstream) -
d28179fd78550a58be44dcb1e3e830ab7d33172dTimo Sirainen tstream->dupstream->v_offset +
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen tstream->dupstream_start_offset;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen size = tstream->dupstream_offset -
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen tstream->dupstream_start_offset;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen fd = dup(i_stream_get_fd(tstream->dupstream));
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen if (fd == -1)
b3f4c31f1533e25380f49f77d5bb1251bf43db2aTimo Sirainen input = i_stream_create_error_str(errno, "dup() failed: %m");
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen else {
bace943c67e6cd14ce6c994f533d82a3caad5bf1Timo Sirainen input2 = i_stream_create_fd_autoclose(&fd, max_buffer_size);
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen i_stream_seek(input2, abs_offset);
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen input = i_stream_create_limit(input2, size);
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen i_stream_unref(&input2);
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen }
c096257fbdaf4b9fcf8eb97aae94afdbb4e71ed4Timo Sirainen i_stream_set_name(input, t_strdup_printf(
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen "(Temp file in %s%s, from %s)", tstream->temp_path_prefix,
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen for_path, i_stream_get_name(tstream->dupstream)));
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen i_stream_unref(&tstream->dupstream);
e44028b5df7045dd9e7f324175e73e3ff490cb5dTimo Sirainen } else if (tstream->dupstream != NULL) {
e44028b5df7045dd9e7f324175e73e3ff490cb5dTimo Sirainen /* return the original failed stream. */
e44028b5df7045dd9e7f324175e73e3ff490cb5dTimo Sirainen input = tstream->dupstream;
baf346e71ebd7b44fcba4b48f4d39845453b778bTimo Sirainen } else if (tstream->fd != -1) {
bace943c67e6cd14ce6c994f533d82a3caad5bf1Timo Sirainen int fd = tstream->fd;
bace943c67e6cd14ce6c994f533d82a3caad5bf1Timo Sirainen input = i_stream_create_fd_autoclose(&tstream->fd, max_buffer_size);
c096257fbdaf4b9fcf8eb97aae94afdbb4e71ed4Timo Sirainen i_stream_set_name(input, t_strdup_printf(
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen "(Temp file fd %d in %s%s, %"PRIuUOFF_T" bytes)",
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen fd, tstream->temp_path_prefix, for_path, tstream->fd_size));
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen } else {
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen input = i_stream_create_from_data(tstream->buf->data,
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen tstream->buf->used);
c096257fbdaf4b9fcf8eb97aae94afdbb4e71ed4Timo Sirainen i_stream_set_name(input, t_strdup_printf(
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen "(Temp buffer in %s%s, %"PRIuSIZE_T" bytes)",
2f4f603d4cebab2cc956c72164efb02da83515c5Timo Sirainen tstream->temp_path_prefix, for_path, tstream->buf->used));
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen i_stream_add_destroy_callback(input, iostream_temp_buf_destroyed,
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen tstream->buf);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen tstream->buf = NULL;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen }
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen o_stream_destroy(output);
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen return input;
cf636afb3826f0d8e15c248aa1fc04ce72820e08Timo Sirainen}