istream-seekable.c revision 2c42748505ef4aed83ff59b34e50ed5606900c86
89a126810703c666309310d0f3189e9834d70b5bTimo Sirainen/* Copyright (c) 2005-2013 Dovecot authors, see the included COPYING file */
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen#include "lib.h"
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen#include "buffer.h"
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen#include "str.h"
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen#include "read-full.h"
65988f5a8abed57e9894fec77105941e046d3490Timo Sirainen#include "write-full.h"
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen#include "safe-mkstemp.h"
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen#include "istream-private.h"
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen#include "istream-concat.h"
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen#include "istream-seekable.h"
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen#include <unistd.h>
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen#define BUF_INITIAL_SIZE (1024*32)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainenstruct seekable_istream {
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen struct istream_private istream;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen char *temp_path;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen uoff_t write_peak;
64510d2cc23a79d2142030bf5bade44baa490db3Timo Sirainen uoff_t size;
64510d2cc23a79d2142030bf5bade44baa490db3Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen int (*fd_callback)(const char **path_r, void *context);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen void *context;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen buffer_t *membuf;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen struct istream **input, *cur_input;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen struct istream *fd_input;
f335accff54f408a8bbb328f8098ad458f2ff58eTimo Sirainen unsigned int cur_idx;
f335accff54f408a8bbb328f8098ad458f2ff58eTimo Sirainen int fd;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen bool free_context;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen};
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainenstatic void i_stream_seekable_close(struct iostream_private *stream,
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen bool close_parent ATTR_UNUSED)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen{
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen sstream->fd = -1;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen if (sstream->fd_input != NULL)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen i_stream_close(sstream->fd_input);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen}
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainenstatic void unref_streams(struct seekable_istream *sstream)
345253fb28498b2e0a60f4a2a8644c65feee7e75Timo Sirainen{
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen unsigned int i;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen for (i = 0; sstream->input[i] != NULL; i++)
345253fb28498b2e0a60f4a2a8644c65feee7e75Timo Sirainen i_stream_unref(&sstream->input[i]);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen}
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainenstatic void i_stream_seekable_destroy(struct iostream_private *stream)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen{
345253fb28498b2e0a60f4a2a8644c65feee7e75Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen if (sstream->membuf != NULL)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen buffer_free(&sstream->membuf);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen if (sstream->fd_input != NULL)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen i_stream_unref(&sstream->fd_input);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen unref_streams(sstream);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen if (sstream->free_context)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen i_free(sstream->context);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen i_free(sstream->temp_path);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen i_free(sstream->input);
c69a255a68103a50fa3f04a527281a169075403fTimo Sirainen}
c69a255a68103a50fa3f04a527281a169075403fTimo Sirainen
c69a255a68103a50fa3f04a527281a169075403fTimo Sirainenstatic void
0f66f12eb4cdbf47670975044c88d8f388bf92dfTimo Siraineni_stream_seekable_set_max_buffer_size(struct iostream_private *stream,
0f66f12eb4cdbf47670975044c88d8f388bf92dfTimo Sirainen size_t max_size)
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen{
c69a255a68103a50fa3f04a527281a169075403fTimo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen unsigned int i;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen sstream->istream.max_buffer_size = max_size;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen if (sstream->fd_input != NULL)
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen i_stream_set_max_buffer_size(sstream->fd_input, max_size);
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen for (i = 0; sstream->input[i] != NULL; i++)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen i_stream_set_max_buffer_size(sstream->input[i], max_size);
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen}
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainenstatic int copy_to_temp_file(struct seekable_istream *sstream)
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen{
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen struct istream_private *stream = &sstream->istream;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen const char *path;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen const unsigned char *buffer;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen size_t size;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen int fd;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen fd = sstream->fd_callback(&path, sstream->context);
87b426af6a2365c6840b14281a98c23e903bf28eTimo Sirainen if (fd == -1)
87b426af6a2365c6840b14281a98c23e903bf28eTimo Sirainen return -1;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen /* copy our currently read buffer to it */
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen if (write_full(fd, sstream->membuf->data, sstream->membuf->used) < 0) {
87b426af6a2365c6840b14281a98c23e903bf28eTimo Sirainen if (!ENOSPACE(errno))
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen i_error("write_full(%s) failed: %m", path);
87b426af6a2365c6840b14281a98c23e903bf28eTimo Sirainen i_close_fd(&fd);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen return -1;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen }
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen sstream->temp_path = i_strdup(path);
87b426af6a2365c6840b14281a98c23e903bf28eTimo Sirainen sstream->write_peak = sstream->membuf->used;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen sstream->fd = fd;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen sstream->fd_input =
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen i_stream_create_fd(fd, sstream->istream.max_buffer_size, TRUE);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen /* read back the data we just had in our buffer */
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen i_stream_seek(sstream->fd_input, stream->istream.v_offset);
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen for (;;) {
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen buffer = i_stream_get_data(sstream->fd_input, &size);
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen if (size >= stream->pos)
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen break;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen if (i_stream_read(sstream->fd_input) <= 0) {
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen i_error("istream-seekable: Couldn't read back "
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen "in-memory input %s",
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen i_stream_get_name(&stream->istream));
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen i_stream_destroy(&sstream->fd_input);
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen return -1;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen }
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen }
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen stream->buffer = buffer;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen stream->pos = size;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen buffer_free(&sstream->membuf);
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen return 0;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen}
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainenstatic ssize_t read_more(struct seekable_istream *sstream)
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen{
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen size_t size;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen ssize_t ret;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen if (sstream->cur_input == NULL) {
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen sstream->istream.istream.eof = TRUE;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen return -1;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen }
0ce8f754204c7eeb33805993807393f74faf2cd3Timo Sirainen
0ce8f754204c7eeb33805993807393f74faf2cd3Timo Sirainen while ((ret = i_stream_read(sstream->cur_input)) == -1) {
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen if (sstream->cur_input->stream_errno != 0) {
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen io_stream_set_error(&sstream->istream.iostream,
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen "read(%s) failed: %s",
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen i_stream_get_name(sstream->cur_input),
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen i_stream_get_error(sstream->cur_input));
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen sstream->istream.istream.stream_errno =
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen sstream->cur_input->stream_errno;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen return -1;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen }
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen /* go to next stream */
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen sstream->cur_input = sstream->input[sstream->cur_idx++];
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen if (sstream->cur_input == NULL) {
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen /* last one, EOF */
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen sstream->size = sstream->istream.istream.v_offset;
72388282bf6718c39af34cfcf51438910f9d62daTimo Sirainen sstream->istream.istream.eof = TRUE;
345253fb28498b2e0a60f4a2a8644c65feee7e75Timo Sirainen unref_streams(sstream);
0ce8f754204c7eeb33805993807393f74faf2cd3Timo Sirainen return -1;
0ce8f754204c7eeb33805993807393f74faf2cd3Timo Sirainen }
345253fb28498b2e0a60f4a2a8644c65feee7e75Timo Sirainen
65988f5a8abed57e9894fec77105941e046d3490Timo Sirainen /* see if stream has pending data */
65988f5a8abed57e9894fec77105941e046d3490Timo Sirainen size = i_stream_get_data_size(sstream->cur_input);
e1b83f64e62cc3e8967c75fcc3f9b5dbb243d3b3Timo Sirainen if (size != 0)
e1b83f64e62cc3e8967c75fcc3f9b5dbb243d3b3Timo Sirainen return size;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen }
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen return ret;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen}
0ce8f754204c7eeb33805993807393f74faf2cd3Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainenstatic bool read_from_buffer(struct seekable_istream *sstream, ssize_t *ret_r)
345253fb28498b2e0a60f4a2a8644c65feee7e75Timo Sirainen{
e1b83f64e62cc3e8967c75fcc3f9b5dbb243d3b3Timo Sirainen struct istream_private *stream = &sstream->istream;
e1b83f64e62cc3e8967c75fcc3f9b5dbb243d3b3Timo Sirainen const unsigned char *data;
e1b83f64e62cc3e8967c75fcc3f9b5dbb243d3b3Timo Sirainen size_t size, pos, offset;
e1b83f64e62cc3e8967c75fcc3f9b5dbb243d3b3Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen i_assert(stream->skip == 0);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen if (stream->istream.v_offset + stream->pos >= sstream->membuf->used) {
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen /* need to read more */
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen if (sstream->membuf->used >= stream->max_buffer_size)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen return FALSE;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen size = sstream->cur_input == NULL ? 0 :
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen i_stream_get_data_size(sstream->cur_input);
62394a19cba1a8df01cad66eaa9331a70464441eTimo Sirainen if (size == 0) {
62394a19cba1a8df01cad66eaa9331a70464441eTimo Sirainen /* read more to buffer */
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen *ret_r = read_more(sstream);
62394a19cba1a8df01cad66eaa9331a70464441eTimo Sirainen if (*ret_r == 0 || *ret_r == -1)
62394a19cba1a8df01cad66eaa9331a70464441eTimo Sirainen return TRUE;
62394a19cba1a8df01cad66eaa9331a70464441eTimo Sirainen }
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
62394a19cba1a8df01cad66eaa9331a70464441eTimo Sirainen /* we should have more now. */
62394a19cba1a8df01cad66eaa9331a70464441eTimo Sirainen data = i_stream_get_data(sstream->cur_input, &size);
62394a19cba1a8df01cad66eaa9331a70464441eTimo Sirainen i_assert(size > 0);
62394a19cba1a8df01cad66eaa9331a70464441eTimo Sirainen buffer_append(sstream->membuf, data, size);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen i_stream_skip(sstream->cur_input, size);
62394a19cba1a8df01cad66eaa9331a70464441eTimo Sirainen }
62394a19cba1a8df01cad66eaa9331a70464441eTimo Sirainen
62394a19cba1a8df01cad66eaa9331a70464441eTimo Sirainen offset = stream->istream.v_offset;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen stream->buffer = CONST_PTR_OFFSET(sstream->membuf->data, offset);
62394a19cba1a8df01cad66eaa9331a70464441eTimo Sirainen pos = sstream->membuf->used - offset;
62394a19cba1a8df01cad66eaa9331a70464441eTimo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen *ret_r = pos - stream->pos;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen i_assert(*ret_r > 0);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen stream->pos = pos;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen return TRUE;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen}
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainenstatic int i_stream_seekable_write_failed(struct seekable_istream *sstream)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen{
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen struct istream_private *stream = &sstream->istream;
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen void *data;
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen i_assert(sstream->membuf == NULL);
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen sstream->membuf =
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen buffer_create_dynamic(default_pool, sstream->write_peak);
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen data = buffer_append_space_unsafe(sstream->membuf, sstream->write_peak);
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen if (pread_full(sstream->fd, data, sstream->write_peak, 0) < 0) {
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen i_error("read(%s) failed: %m", sstream->temp_path);
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen buffer_free(&sstream->membuf);
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen return -1;
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen }
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen i_stream_destroy(&sstream->fd_input);
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen i_close_fd(&sstream->fd);
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen stream->max_buffer_size = (size_t)-1;
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen i_free_and_null(sstream->temp_path);
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen return 0;
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen}
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainenstatic ssize_t i_stream_seekable_read(struct istream_private *stream)
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen{
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen const unsigned char *data;
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen size_t size, pos;
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen ssize_t ret;
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen stream->buffer = CONST_PTR_OFFSET(stream->buffer, stream->skip);
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen stream->pos -= stream->skip;
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen stream->skip = 0;
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen if (sstream->membuf != NULL) {
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen if (read_from_buffer(sstream, &ret))
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen return ret;
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen /* copy everything to temp file and use it as the stream */
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen if (copy_to_temp_file(sstream) < 0) {
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen stream->max_buffer_size = (size_t)-1;
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen if (!read_from_buffer(sstream, &ret))
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen i_unreached();
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen return ret;
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen }
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen i_assert(sstream->membuf == NULL);
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen }
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen i_assert(stream->istream.v_offset + stream->pos <= sstream->write_peak);
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen if (stream->istream.v_offset + stream->pos == sstream->write_peak) {
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen /* need to read more */
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen ret = read_more(sstream);
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen if (ret == -1 || ret == 0)
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen return ret;
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen /* save to our file */
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen data = i_stream_get_data(sstream->cur_input, &size);
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen ret = write(sstream->fd, data, size);
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen if (ret <= 0) {
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen if (ret < 0 && !ENOSPACE(errno)) {
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen i_error("write_full(%s) failed: %m",
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen sstream->temp_path);
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen }
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen if (i_stream_seekable_write_failed(sstream) < 0)
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen return -1;
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen if (!read_from_buffer(sstream, &ret))
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen i_unreached();
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen return ret;
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen }
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen i_stream_sync(sstream->fd_input);
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen i_stream_skip(sstream->cur_input, ret);
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen sstream->write_peak += ret;
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen }
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen i_stream_seek(sstream->fd_input, stream->istream.v_offset);
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen ret = i_stream_read(sstream->fd_input);
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen if (ret <= 0) {
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen stream->istream.eof = sstream->fd_input->eof;
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen stream->istream.stream_errno =
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen sstream->fd_input->stream_errno;
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen } else {
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen ret = -2;
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen }
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen stream->buffer = i_stream_get_data(sstream->fd_input, &pos);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen stream->pos -= stream->skip;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen stream->skip = 0;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen ret = pos > stream->pos ? (ssize_t)(pos - stream->pos) : ret;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen stream->pos = pos;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen return ret;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen}
e619ecbbc00cba9e6e1e8322caa59776507fac02Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainenstatic int
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Siraineni_stream_seekable_stat(struct istream_private *stream, bool exact)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen{
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen const struct stat *st;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen uoff_t old_offset;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen ssize_t ret;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen if (sstream->size != (uoff_t)-1) {
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen /* we've already reached EOF and know the size */
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen stream->statbuf.st_size = sstream->size;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen return 0;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen }
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen if (sstream->membuf != NULL) {
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen /* we want to know the full size of the file, so read until
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen we're finished */
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen old_offset = stream->istream.v_offset;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen do {
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen i_stream_skip(&stream->istream,
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen stream->pos - stream->skip);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen } while ((ret = i_stream_seekable_read(stream)) > 0);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen if (ret == 0) {
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen i_panic("i_stream_stat() used for non-blocking "
b29403032a7ee56c309e94d92fbf1728bacb88f4Timo Sirainen "seekable stream %s offset %"PRIuUOFF_T,
64510d2cc23a79d2142030bf5bade44baa490db3Timo Sirainen i_stream_get_name(sstream->cur_input),
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen sstream->cur_input->v_offset);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen }
64510d2cc23a79d2142030bf5bade44baa490db3Timo Sirainen i_stream_skip(&stream->istream, stream->pos - stream->skip);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen i_stream_seek(&stream->istream, old_offset);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen unref_streams(sstream);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen }
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen if (stream->istream.stream_errno != 0)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen return -1;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen if (sstream->fd_input != NULL) {
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen /* using a file backed buffer, we can use real fstat() */
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen if (i_stream_stat(sstream->fd_input, exact, &st) < 0)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen return -1;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen stream->statbuf = *st;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen } else {
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen /* buffer is completely in memory */
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen i_assert(sstream->membuf != NULL);
b29403032a7ee56c309e94d92fbf1728bacb88f4Timo Sirainen
64510d2cc23a79d2142030bf5bade44baa490db3Timo Sirainen stream->statbuf.st_size = sstream->membuf->used;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen }
e619ecbbc00cba9e6e1e8322caa59776507fac02Timo Sirainen return 0;
e619ecbbc00cba9e6e1e8322caa59776507fac02Timo Sirainen}
e619ecbbc00cba9e6e1e8322caa59776507fac02Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainenstatic void i_stream_seekable_seek(struct istream_private *stream,
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen uoff_t v_offset, bool mark)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen{
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen if (v_offset <= stream->istream.v_offset) {
e619ecbbc00cba9e6e1e8322caa59776507fac02Timo Sirainen /* seeking backwards */
64510d2cc23a79d2142030bf5bade44baa490db3Timo Sirainen stream->istream.v_offset = v_offset;
64510d2cc23a79d2142030bf5bade44baa490db3Timo Sirainen stream->skip = stream->pos = 0;
64510d2cc23a79d2142030bf5bade44baa490db3Timo Sirainen } else {
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen /* we can't skip over data we haven't yet read and written to
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen our buffer/temp file */
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen i_stream_default_seek_nonseekable(stream, v_offset, mark);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen }
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen}
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainenstruct istream *
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Siraineni_streams_merge(struct istream *input[], size_t max_buffer_size,
64510d2cc23a79d2142030bf5bade44baa490db3Timo Sirainen int (*fd_callback)(const char **path_r, void *context),
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen void *context) ATTR_NULL(4)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen{
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen struct seekable_istream *sstream;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen const unsigned char *data;
b29403032a7ee56c309e94d92fbf1728bacb88f4Timo Sirainen unsigned int count;
b29403032a7ee56c309e94d92fbf1728bacb88f4Timo Sirainen size_t size;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen bool blocking = TRUE;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
919733fcead68b0e9617cfff86ae5c74d097c6cdTimo Sirainen /* if any of the streams isn't blocking, set ourself also nonblocking */
345253fb28498b2e0a60f4a2a8644c65feee7e75Timo Sirainen for (count = 0; input[count] != NULL; count++) {
e1b83f64e62cc3e8967c75fcc3f9b5dbb243d3b3Timo Sirainen if (!input[count]->blocking)
65988f5a8abed57e9894fec77105941e046d3490Timo Sirainen blocking = FALSE;
0ae99441ae9ef80f435f3eb85fad16e136036b0bTimo Sirainen i_stream_ref(input[count]);
e1b83f64e62cc3e8967c75fcc3f9b5dbb243d3b3Timo Sirainen }
e1b83f64e62cc3e8967c75fcc3f9b5dbb243d3b3Timo Sirainen i_assert(count != 0);
e1b83f64e62cc3e8967c75fcc3f9b5dbb243d3b3Timo Sirainen
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen sstream = i_new(struct seekable_istream, 1);
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen sstream->fd_callback = fd_callback;
e1b83f64e62cc3e8967c75fcc3f9b5dbb243d3b3Timo Sirainen sstream->context = context;
919733fcead68b0e9617cfff86ae5c74d097c6cdTimo Sirainen sstream->membuf = buffer_create_dynamic(default_pool, BUF_INITIAL_SIZE);
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen sstream->istream.max_buffer_size = max_buffer_size;
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen sstream->fd = -1;
1dba794aa92dc13e6afd7a50a8c33cb19d6aa235Timo Sirainen sstream->size = (uoff_t)-1;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen sstream->input = i_new(struct istream *, count + 1);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen memcpy(sstream->input, input, sizeof(*input) * count);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen sstream->cur_input = sstream->input[0];
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen /* initialize our buffer from first stream's pending data */
055f4599bba1874fa1148a8fa488517fa077619cTimo Sirainen data = i_stream_get_data(sstream->cur_input, &size);
055f4599bba1874fa1148a8fa488517fa077619cTimo Sirainen buffer_append(sstream->membuf, data, size);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen i_stream_skip(sstream->cur_input, size);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen sstream->istream.iostream.close = i_stream_seekable_close;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen sstream->istream.iostream.destroy = i_stream_seekable_destroy;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen sstream->istream.iostream.set_max_buffer_size =
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen i_stream_seekable_set_max_buffer_size;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen sstream->istream.read = i_stream_seekable_read;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen sstream->istream.stat = i_stream_seekable_stat;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen sstream->istream.seek = i_stream_seekable_seek;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen sstream->istream.istream.readable_fd = FALSE;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen sstream->istream.istream.blocking = blocking;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen sstream->istream.istream.seekable = TRUE;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen return i_stream_create(&sstream->istream, NULL, -1);
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen}
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainenstatic bool inputs_are_seekable(struct istream *input[])
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen{
f335accff54f408a8bbb328f8098ad458f2ff58eTimo Sirainen unsigned int count;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen for (count = 0; input[count] != NULL; count++) {
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen if (!input[count]->seekable)
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen return FALSE;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen }
f335accff54f408a8bbb328f8098ad458f2ff58eTimo Sirainen return TRUE;
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen}
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainenstruct istream *
2e03303e721a293d796c0287829396f5caea76eaTimo Siraineni_stream_create_seekable(struct istream *input[],
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen size_t max_buffer_size,
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen int (*fd_callback)(const char **path_r, void *context),
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen void *context)
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen{
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen /* If all input streams are seekable, use concat istream instead */
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen if (inputs_are_seekable(input))
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen return i_stream_create_concat(input);
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen return i_streams_merge(input, max_buffer_size, fd_callback, context);
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen}
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainenstatic int seekable_fd_callback(const char **path_r, void *context)
2e03303e721a293d796c0287829396f5caea76eaTimo Sirainen{
050975ee630c761ab237fce7b8f84fe189bb02d2Timo Sirainen char *temp_path_prefix = context;
string_t *path;
int fd;
path = t_str_new(128);
str_append(path, temp_path_prefix);
fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1);
if (fd == -1) {
i_error("safe_mkstemp(%s) failed: %m", str_c(path));
return -1;
}
/* we just want the fd, unlink it */
if (unlink(str_c(path)) < 0) {
/* shouldn't happen.. */
i_error("unlink(%s) failed: %m", str_c(path));
i_close_fd(&fd);
return -1;
}
*path_r = str_c(path);
return fd;
}
struct istream *
i_stream_create_seekable_path(struct istream *input[],
size_t max_buffer_size,
const char *temp_path_prefix)
{
struct seekable_istream *sstream;
struct istream *stream;
if (inputs_are_seekable(input))
return i_stream_create_concat(input);
stream = i_stream_create_seekable(input, max_buffer_size,
seekable_fd_callback,
i_strdup(temp_path_prefix));
sstream = (struct seekable_istream *)stream->real_stream;
sstream->free_context = TRUE;
return stream;
}