iostream-temp.c revision d97f081a3de44852197ced772e21560c108895a6
5a580c3a38ced62d4bcc95b8ac7c4f2935b5d294Timo Sirainen/* Copyright (c) 2013-2016 Dovecot authors, see the included COPYING file */
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen
46552a931924c2d743f045e95b08c3ce6beda91aTimo Sirainen#include "lib.h"
3cb26db7f4756b71ba06c6e4950fa4f8ce7fad66Timo Sirainen#include "buffer.h"
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen#include "str.h"
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen#include "safe-mkstemp.h"
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen#include "write-full.h"
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen#include "istream-private.h"
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen#include "ostream-private.h"
45155bb1250cf5a120278f349465aded513a100fTimo Sirainen#include "iostream-temp.h"
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen#include <unistd.h>
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen#define IOSTREAM_TEMP_MAX_BUF_SIZE_DEFAULT (1024*128)
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainenstruct temp_ostream {
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen struct ostream_private ostream;
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen
3cb26db7f4756b71ba06c6e4950fa4f8ce7fad66Timo Sirainen char *temp_path_prefix;
3cb26db7f4756b71ba06c6e4950fa4f8ce7fad66Timo Sirainen enum iostream_temp_flags flags;
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen size_t max_mem_size;
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen struct istream *dupstream;
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen uoff_t dupstream_offset, dupstream_start_offset;
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen char *name;
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen buffer_t *buf;
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen int fd;
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen bool fd_tried;
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen uoff_t fd_size;
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen};
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen
9508ac436fff0e1dcea975855c139cd251deb703Timo Sirainenstatic bool o_stream_temp_dup_cancel(struct temp_ostream *tstream,
ddbdc644a15f56f4b43596f1b8c0fc196c101445Timo Sirainen enum ostream_send_istream_result *res_r);
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen
b55f914c0ade77252cfd798ea8eb9a84bda56315Timo Sirainenstatic void
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Siraineno_stream_temp_close(struct iostream_private *stream,
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen bool close_parent ATTR_UNUSED)
493123e38ca1f27b07ac30dcbc59663c5fcdcba2Timo Sirainen{
ddbdc644a15f56f4b43596f1b8c0fc196c101445Timo Sirainen struct temp_ostream *tstream = (struct temp_ostream *)stream;
ddbdc644a15f56f4b43596f1b8c0fc196c101445Timo Sirainen
b55f914c0ade77252cfd798ea8eb9a84bda56315Timo Sirainen if (tstream->fd != -1)
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen i_close_fd(&tstream->fd);
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen if (tstream->buf != NULL)
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen buffer_free(&tstream->buf);
ddbdc644a15f56f4b43596f1b8c0fc196c101445Timo Sirainen i_free(tstream->temp_path_prefix);
ddbdc644a15f56f4b43596f1b8c0fc196c101445Timo Sirainen i_free(tstream->name);
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen}
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int o_stream_temp_move_to_fd(struct temp_ostream *tstream)
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen{
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen string_t *path;
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen if (tstream->fd_tried)
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen return -1;
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen tstream->fd_tried = TRUE;
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen path = t_str_new(128);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen str_append(path, tstream->temp_path_prefix);
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen tstream->fd = safe_mkstemp_hostpid(path, 0600, (uid_t)-1, (gid_t)-1);
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen if (tstream->fd == -1) {
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen i_error("safe_mkstemp(%s) failed: %m", str_c(path));
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen return -1;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen }
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen if (i_unlink(str_c(path)) < 0) {
3bc9d91f987261e989aa653fd412b0e8095e4810Timo Sirainen i_close_fd(&tstream->fd);
3bc9d91f987261e989aa653fd412b0e8095e4810Timo Sirainen return -1;
3bc9d91f987261e989aa653fd412b0e8095e4810Timo Sirainen }
3bc9d91f987261e989aa653fd412b0e8095e4810Timo Sirainen if (write_full(tstream->fd, tstream->buf->data, tstream->buf->used) < 0) {
3bc9d91f987261e989aa653fd412b0e8095e4810Timo Sirainen i_error("write(%s) failed: %m", str_c(path));
3bc9d91f987261e989aa653fd412b0e8095e4810Timo Sirainen i_close_fd(&tstream->fd);
41783dcf1bcd7118440c9c40a691a09fb98a9460Timo Sirainen return -1;
41783dcf1bcd7118440c9c40a691a09fb98a9460Timo Sirainen }
41783dcf1bcd7118440c9c40a691a09fb98a9460Timo Sirainen /* make the fd available also to o_stream_get_fd(),
41783dcf1bcd7118440c9c40a691a09fb98a9460Timo Sirainen e.g. for unit tests */
5324117274df8564eeaebe369cb1eca76edb3165Timo Sirainen tstream->ostream.fd = tstream->fd;
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainen tstream->fd_size = tstream->buf->used;
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainen buffer_free(&tstream->buf);
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainen return 0;
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainen}
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainen
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainenint o_stream_temp_move_to_memory(struct ostream *output)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen{
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen struct temp_ostream *tstream =
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen (struct temp_ostream *)output->real_stream;
686ad6d723004b807fd558f3ef9d1f88afa7e127Timo Sirainen unsigned char buf[IO_BLOCK_SIZE];
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen uoff_t offset = 0;
686ad6d723004b807fd558f3ef9d1f88afa7e127Timo Sirainen ssize_t ret = 0;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen i_assert(tstream->buf == NULL);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen tstream->buf = buffer_create_dynamic(default_pool, 8192);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen while (offset < tstream->ostream.ostream.offset &&
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen (ret = pread(tstream->fd, buf, sizeof(buf), offset)) > 0) {
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if ((size_t)ret > tstream->ostream.ostream.offset - offset)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen ret = tstream->ostream.ostream.offset - offset;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen buffer_append(tstream->buf, buf, ret);
26681e71837ebbb3eb92455ec4e3cadefa710f82Timo Sirainen offset += ret;
26681e71837ebbb3eb92455ec4e3cadefa710f82Timo Sirainen }
26681e71837ebbb3eb92455ec4e3cadefa710f82Timo Sirainen if (ret < 0) {
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen /* not really expecting this to happen */
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen i_error("iostream-temp %s: read(%s*) failed: %m",
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen o_stream_get_name(&tstream->ostream.ostream),
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen tstream->temp_path_prefix);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen tstream->ostream.ostream.stream_errno = EIO;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen return -1;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen }
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen i_close_fd(&tstream->fd);
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen tstream->ostream.fd = -1;
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen return 0;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen}
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainenstatic ssize_t
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Siraineno_stream_temp_fd_sendv(struct temp_ostream *tstream,
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen const struct const_iovec *iov, unsigned int iov_count)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen{
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen size_t bytes = 0;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen unsigned int i;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen for (i = 0; i < iov_count; i++) {
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen if (write_full(tstream->fd, iov[i].iov_base, iov[i].iov_len) < 0) {
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen i_error("iostream-temp %s: write(%s*) failed: %m - moving to memory",
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen o_stream_get_name(&tstream->ostream.ostream),
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen tstream->temp_path_prefix);
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen if (o_stream_temp_move_to_memory(&tstream->ostream.ostream) < 0)
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen return -1;
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen for (; i < iov_count; i++) {
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen buffer_append(tstream->buf, iov[i].iov_base, iov[i].iov_len);
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen bytes += iov[i].iov_len;
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen tstream->ostream.ostream.offset += iov[i].iov_len;
3cb26db7f4756b71ba06c6e4950fa4f8ce7fad66Timo Sirainen }
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen i_assert(tstream->fd_tried);
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen return bytes;
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen bytes += iov[i].iov_len;
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen tstream->ostream.ostream.offset += iov[i].iov_len;
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen }
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen tstream->fd_size += bytes;
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen return bytes;
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen}
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen
53d564c421ca7292d7b1bd945f86894a34b75370Timo Sirainenstatic ssize_t
3cb26db7f4756b71ba06c6e4950fa4f8ce7fad66Timo Siraineno_stream_temp_sendv(struct ostream_private *stream,
3cb26db7f4756b71ba06c6e4950fa4f8ce7fad66Timo Sirainen const struct const_iovec *iov, unsigned int iov_count)
53d564c421ca7292d7b1bd945f86894a34b75370Timo Sirainen{
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen struct temp_ostream *tstream = (struct temp_ostream *)stream;
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen ssize_t ret = 0;
5324117274df8564eeaebe369cb1eca76edb3165Timo Sirainen unsigned int i;
5324117274df8564eeaebe369cb1eca76edb3165Timo Sirainen enum ostream_send_istream_result res;
5324117274df8564eeaebe369cb1eca76edb3165Timo Sirainen
53d564c421ca7292d7b1bd945f86894a34b75370Timo Sirainen
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen tstream->flags &= ~IOSTREAM_TEMP_FLAG_TRY_FD_DUP;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (tstream->dupstream != NULL) {
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen if (o_stream_temp_dup_cancel(tstream, &res))
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen return -1;
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen if (tstream->fd != -1)
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen return o_stream_temp_fd_sendv(tstream, iov, iov_count);
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen for (i = 0; i < iov_count; i++) {
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen if (tstream->buf->used + iov[i].iov_len > tstream->max_mem_size) {
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (o_stream_temp_move_to_fd(tstream) == 0) {
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen return o_stream_temp_fd_sendv(tstream, iov+i,
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen iov_count-i);
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen }
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen /* failed to move to temp fd, just keep it in memory */
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen }
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen buffer_append(tstream->buf, iov[i].iov_base, iov[i].iov_len);
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen ret += iov[i].iov_len;
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen stream->ostream.offset += iov[i].iov_len;
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen }
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen return ret;
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen}
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainenstatic bool o_stream_temp_dup_cancel(struct temp_ostream *tstream,
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen enum ostream_send_istream_result *res_r)
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen{
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen struct istream *input;
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen uoff_t size = tstream->dupstream_offset -
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen tstream->dupstream_start_offset;
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen bool ret = TRUE; /* use res_r to return error */
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen i_stream_seek(tstream->dupstream, tstream->dupstream_start_offset);
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen tstream->ostream.ostream.offset = 0;
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen input = i_stream_create_limit(tstream->dupstream, size);
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen i_stream_unref(&tstream->dupstream);
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen *res_r = io_stream_copy(&tstream->ostream.ostream, input);
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen switch (*res_r) {
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen case OSTREAM_SEND_ISTREAM_RESULT_FINISHED:
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen /* everything copied */
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen ret = FALSE;
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen break;
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT:
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT:
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen i_unreached();
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT:
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen tstream->ostream.ostream.stream_errno = input->stream_errno;
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen io_stream_set_error(&tstream->ostream.iostream,
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen "iostream-temp: read(%s) failed: %s",
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen i_stream_get_name(input),
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen i_stream_get_error(input));
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen break;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT:
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen break;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen i_stream_destroy(&input);
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen return ret;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen}
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainenstatic bool
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Siraineno_stream_temp_dup_istream(struct temp_ostream *outstream,
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen struct istream *instream,
feb665db52583259a1f42037c6e8a22852aa8889Timo Sirainen enum ostream_send_istream_result *res_r)
9bc0204ec8bda657ce2e96e6ae715e4034f1538bTimo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen uoff_t in_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen if (!instream->readable_fd || i_stream_get_fd(instream) == -1)
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen return FALSE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (i_stream_get_size(instream, TRUE, &in_size) <= 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (outstream->dupstream != NULL)
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen return o_stream_temp_dup_cancel(outstream, res_r);
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen return FALSE;
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen }
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen i_assert(instream->v_offset <= in_size);
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (outstream->dupstream == NULL) {
27d50b3aa143964143e4bef66c0bfe3c72aea233Timo Sirainen outstream->dupstream = instream;
27d50b3aa143964143e4bef66c0bfe3c72aea233Timo Sirainen outstream->dupstream_start_offset = instream->v_offset;
9c47edf0d1aa8afa6d05dde93e7aa5169059c94aTimo Sirainen i_stream_ref(outstream->dupstream);
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen } else {
9c47edf0d1aa8afa6d05dde93e7aa5169059c94aTimo Sirainen if (outstream->dupstream != instream ||
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen outstream->dupstream_offset != instream->v_offset ||
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen outstream->dupstream_offset > in_size)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen return o_stream_temp_dup_cancel(outstream, res_r);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen }
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen i_stream_seek(instream, in_size);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen /* we should be at EOF now. o_stream_send_istream() asserts if
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen eof isn't set. */
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen instream->eof = TRUE;
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen outstream->dupstream_offset = instream->v_offset;
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen outstream->ostream.ostream.offset =
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen outstream->dupstream_offset - outstream->dupstream_start_offset;
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen *res_r = OSTREAM_SEND_ISTREAM_RESULT_FINISHED;
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen return TRUE;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen}
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainenstatic enum ostream_send_istream_result
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Siraineno_stream_temp_send_istream(struct ostream_private *_outstream,
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen struct istream *instream)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen{
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen struct temp_ostream *outstream = (struct temp_ostream *)_outstream;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen enum ostream_send_istream_result res;
d229d26d263a57a77eec8fe7cba24fbfd9509966Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if ((outstream->flags & IOSTREAM_TEMP_FLAG_TRY_FD_DUP) != 0) {
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (o_stream_temp_dup_istream(outstream, instream, &res))
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen return res;
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen outstream->flags &= ~IOSTREAM_TEMP_FLAG_TRY_FD_DUP;
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen }
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen return io_stream_copy(&outstream->ostream.ostream, instream);
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen}
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainenstatic int
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Siraineno_stream_temp_write_at(struct ostream_private *stream,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const void *data, size_t size, uoff_t offset)
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen{
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen struct temp_ostream *tstream = (struct temp_ostream *)stream;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen if (tstream->fd == -1) {
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen i_assert(stream->ostream.offset == tstream->buf->used);
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen buffer_write(tstream->buf, offset, data, size);
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen stream->ostream.offset = tstream->buf->used;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (pwrite_full(tstream->fd, data, size, offset) < 0) {
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen stream->ostream.stream_errno = errno;
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen i_close_fd(&tstream->fd);
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen if (tstream->fd_size < offset + size)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen tstream->fd_size = offset + size;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen }
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen return 0;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen}
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainenstatic int o_stream_temp_seek(struct ostream_private *_stream, uoff_t offset)
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen{
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen _stream->ostream.offset = offset;
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen return 0;
8e50329e2c5e3a199674ae9f6d3dfcddab02487bTimo Sirainen}
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainenstruct ostream *iostream_temp_create(const char *temp_path_prefix,
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen enum iostream_temp_flags flags)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return iostream_temp_create_named(temp_path_prefix, flags, "");
eb188b2a2d5395b2c6125f875ec053b04a10e5fbTimo Sirainen}
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainenstruct ostream *iostream_temp_create_named(const char *temp_path_prefix,
ac45ba9c603b67cc43fa7bceffdef0a19100720bTimo Sirainen enum iostream_temp_flags flags,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *name)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return iostream_temp_create_sized(temp_path_prefix, flags, name,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen IOSTREAM_TEMP_MAX_BUF_SIZE_DEFAULT);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
6e8ad595d0603295f57bef576da8a3a00b55c5e2Timo Sirainen
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainenstruct ostream *iostream_temp_create_sized(const char *temp_path_prefix,
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen enum iostream_temp_flags flags,
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen const char *name,
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen size_t max_mem_size)
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen{
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen struct temp_ostream *tstream;
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen struct ostream *output;
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen tstream = i_new(struct temp_ostream, 1);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen tstream->ostream.ostream.blocking = TRUE;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen tstream->ostream.sendv = o_stream_temp_sendv;
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen tstream->ostream.send_istream = o_stream_temp_send_istream;
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen tstream->ostream.write_at = o_stream_temp_write_at;
00fa8dcbc66f56daa737487c9dec7166c37de79eTimo Sirainen tstream->ostream.seek = o_stream_temp_seek;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen tstream->ostream.iostream.close = o_stream_temp_close;
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen tstream->temp_path_prefix = i_strdup(temp_path_prefix);
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen tstream->flags = flags;
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen tstream->max_mem_size = max_mem_size;
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen tstream->buf = buffer_create_dynamic(default_pool, 8192);
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen tstream->fd = -1;
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen output = o_stream_create(&tstream->ostream, NULL, -1);
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen tstream->name = i_strdup(name);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen if (name[0] == '\0') {
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen o_stream_set_name(output, t_strdup_printf(
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen "(temp iostream in %s)", temp_path_prefix));
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen } else {
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen o_stream_set_name(output, t_strdup_printf(
80980955bb1bbcc1bd73623fe0912f334194ddd2Timo Sirainen "(temp iostream in %s for %s)", temp_path_prefix, name));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen return output;
3cb26db7f4756b71ba06c6e4950fa4f8ce7fad66Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void iostream_temp_buf_destroyed(buffer_t *buf)
3cb26db7f4756b71ba06c6e4950fa4f8ce7fad66Timo Sirainen{
31750e7fddc514c68c4eaf85b4f8c00000c281e0Timo Sirainen buffer_free(&buf);
737d994fb6bb0f3e87f7412e35874694013d2fc3Timo Sirainen}
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen
737d994fb6bb0f3e87f7412e35874694013d2fc3Timo Sirainenstruct istream *iostream_temp_finish(struct ostream **output,
737d994fb6bb0f3e87f7412e35874694013d2fc3Timo Sirainen size_t max_buffer_size)
737d994fb6bb0f3e87f7412e35874694013d2fc3Timo Sirainen{
737d994fb6bb0f3e87f7412e35874694013d2fc3Timo Sirainen struct temp_ostream *tstream =
737d994fb6bb0f3e87f7412e35874694013d2fc3Timo Sirainen (struct temp_ostream *)(*output)->real_stream;
737d994fb6bb0f3e87f7412e35874694013d2fc3Timo Sirainen struct istream *input, *input2;
737d994fb6bb0f3e87f7412e35874694013d2fc3Timo Sirainen uoff_t abs_offset, size;
737d994fb6bb0f3e87f7412e35874694013d2fc3Timo Sirainen const char *for_path;
737d994fb6bb0f3e87f7412e35874694013d2fc3Timo Sirainen int fd;
31750e7fddc514c68c4eaf85b4f8c00000c281e0Timo Sirainen
087eb3d719a5667631cc7ce9de6c372ddea19f4dTimo Sirainen if (tstream->name[0] == '\0')
087eb3d719a5667631cc7ce9de6c372ddea19f4dTimo Sirainen for_path = "";
087eb3d719a5667631cc7ce9de6c372ddea19f4dTimo Sirainen else
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen for_path = t_strdup_printf(" for %s", tstream->name);
087eb3d719a5667631cc7ce9de6c372ddea19f4dTimo Sirainen
087eb3d719a5667631cc7ce9de6c372ddea19f4dTimo Sirainen if (tstream->dupstream != NULL && !tstream->dupstream->closed) {
087eb3d719a5667631cc7ce9de6c372ddea19f4dTimo Sirainen abs_offset = i_stream_get_absolute_offset(tstream->dupstream) -
00fa8dcbc66f56daa737487c9dec7166c37de79eTimo Sirainen tstream->dupstream->v_offset +
087eb3d719a5667631cc7ce9de6c372ddea19f4dTimo Sirainen tstream->dupstream_start_offset;
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen size = tstream->dupstream_offset -
087eb3d719a5667631cc7ce9de6c372ddea19f4dTimo Sirainen tstream->dupstream_start_offset;
9c47edf0d1aa8afa6d05dde93e7aa5169059c94aTimo Sirainen fd = dup(i_stream_get_fd(tstream->dupstream));
9c47edf0d1aa8afa6d05dde93e7aa5169059c94aTimo Sirainen if (fd == -1)
9c47edf0d1aa8afa6d05dde93e7aa5169059c94aTimo Sirainen input = i_stream_create_error_str(errno, "dup() failed: %m");
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen else {
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen input2 = i_stream_create_fd_autoclose(&fd, max_buffer_size);
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen i_stream_seek(input2, abs_offset);
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen input = i_stream_create_limit(input2, size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_stream_unref(&input2);
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_stream_set_name(input, t_strdup_printf(
d7cd49f01fad7c87c5a0865ebf54a548275e9feeTimo Sirainen "(Temp file in %s%s, from %s)", tstream->temp_path_prefix,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen for_path, i_stream_get_name(tstream->dupstream)));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_stream_unref(&tstream->dupstream);
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen } else if (tstream->dupstream != NULL) {
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen /* return the original failed stream. */
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen input = tstream->dupstream;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen } else if (tstream->fd != -1) {
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen int fd = tstream->fd;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen input = i_stream_create_fd_autoclose(&tstream->fd, max_buffer_size);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen i_stream_set_name(input, t_strdup_printf(
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen "(Temp file fd %d in %s%s, %"PRIuUOFF_T" bytes)",
fd, tstream->temp_path_prefix, for_path, tstream->fd_size));
} else {
input = i_stream_create_from_data(tstream->buf->data,
tstream->buf->used);
i_stream_set_name(input, t_strdup_printf(
"(Temp buffer in %s%s, %"PRIuSIZE_T" bytes)",
tstream->temp_path_prefix, for_path, tstream->buf->used));
i_stream_add_destroy_callback(input, iostream_temp_buf_destroyed,
tstream->buf);
tstream->buf = NULL;
}
o_stream_destroy(output);
return input;
}