istream-seekable.c revision e2ce8d4a6ac5d82a906178148453e7613fab9ba0
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2005-2013 Dovecot authors, see the included COPYING file */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "lib.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "buffer.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "str.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "read-full.h"
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "write-full.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "safe-mkstemp.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "istream-private.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "istream-concat.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "istream-seekable.h"
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include <unistd.h>
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#define BUF_INITIAL_SIZE (1024*32)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
25d624dd86700c82cd28427f3d3bebe7c8f7f459Timo Sirainenstruct seekable_istream {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct istream_private istream;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen char *temp_path;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen uoff_t write_peak;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen uoff_t size;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen int (*fd_callback)(const char **path_r, void *context);
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen void *context;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen buffer_t *membuf;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct istream **input, *cur_input;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct istream *fd_input;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen unsigned int cur_idx;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen int fd;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen bool free_context;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen};
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic void i_stream_seekable_close(struct iostream_private *stream,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen bool close_parent ATTR_UNUSED)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->fd = -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (sstream->fd_input != NULL)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_stream_close(sstream->fd_input);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic void unref_streams(struct seekable_istream *sstream)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
bd63b5b860658b01b1f46f26d406e1e4a9dc019aTimo Sirainen unsigned int i;
15a07b47846c47a81d69a14d649564e222d6f742Timo Sirainen
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen for (i = 0; sstream->input[i] != NULL; i++)
c1faff067b29fb48426cb84260adba563e93189aTimo Sirainen i_stream_unref(&sstream->input[i]);
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic void i_stream_seekable_destroy(struct iostream_private *stream)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen if (sstream->membuf != NULL)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen buffer_free(&sstream->membuf);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (sstream->fd_input != NULL)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen i_stream_unref(&sstream->fd_input);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen unref_streams(sstream);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (sstream->free_context)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen i_free(sstream->context);
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen i_free(sstream->temp_path);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen i_free(sstream->input);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic void
02a54da28f376dd66d7939d8546a196a0045b486Timo Siraineni_stream_seekable_set_max_buffer_size(struct iostream_private *stream,
4145cbac82bfc0c8bfeceeca0ef841700117930cTimo Sirainen size_t max_size)
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen{
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen unsigned int i;
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen sstream->istream.max_buffer_size = max_size;
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen if (sstream->fd_input != NULL)
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen i_stream_set_max_buffer_size(sstream->fd_input, max_size);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen for (i = 0; sstream->input[i] != NULL; i++)
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen i_stream_set_max_buffer_size(sstream->input[i], max_size);
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen}
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int copy_to_temp_file(struct seekable_istream *sstream)
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct istream_private *stream = &sstream->istream;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *path;
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen const unsigned char *buffer;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen size_t size;
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen int fd;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen fd = sstream->fd_callback(&path, sstream->context);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (fd == -1)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* copy our currently read buffer to it */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (write_full(fd, sstream->membuf->data, sstream->membuf->used) < 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (!ENOSPACE(errno))
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_error("write_full(%s) failed: %m", path);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_close_fd(&fd);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->temp_path = i_strdup(path);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->write_peak = sstream->membuf->used;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->fd = fd;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->fd_input =
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_stream_create_fd(fd, sstream->istream.max_buffer_size, TRUE);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
411f318ed3a25fa66c1b932e10df43841e2725c9Timo Sirainen /* read back the data we just had in our buffer */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_stream_seek(sstream->fd_input, stream->istream.v_offset);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen for (;;) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen buffer = i_stream_get_data(sstream->fd_input, &size);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (size >= stream->pos)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen break;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (i_stream_read(sstream->fd_input) <= 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_error("istream-seekable: Couldn't read back "
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen "in-memory input %s",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_stream_get_name(&stream->istream));
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_stream_destroy(&sstream->fd_input);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen stream->buffer = buffer;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen stream->pos = size;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen buffer_free(&sstream->membuf);
411f318ed3a25fa66c1b932e10df43841e2725c9Timo Sirainen return 0;
411f318ed3a25fa66c1b932e10df43841e2725c9Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic ssize_t read_more(struct seekable_istream *sstream)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen size_t size;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen ssize_t ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (sstream->cur_input == NULL) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->istream.istream.eof = TRUE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
3a9eb305fd4aad5502cb7e64625874385ab5bc19Timo Sirainen }
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen while ((ret = i_stream_read(sstream->cur_input)) == -1) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (sstream->cur_input->stream_errno != 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->istream.istream.stream_errno =
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->cur_input->stream_errno;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* go to next stream */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->cur_input = sstream->input[sstream->cur_idx++];
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (sstream->cur_input == NULL) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* last one, EOF */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->size = sstream->istream.istream.v_offset;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->istream.istream.eof = TRUE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen unref_streams(sstream);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* see if stream has pending data */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen size = i_stream_get_data_size(sstream->cur_input);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (size != 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return size;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic bool read_from_buffer(struct seekable_istream *sstream, ssize_t *ret_r)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct istream_private *stream = &sstream->istream;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const unsigned char *data;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen size_t size, pos, offset;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen i_assert(stream->skip == 0);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen if (stream->istream.v_offset + stream->pos >= sstream->membuf->used) {
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen /* need to read more */
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen if (sstream->membuf->used >= stream->max_buffer_size)
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen return FALSE;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen size = sstream->cur_input == NULL ? 0 :
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen i_stream_get_data_size(sstream->cur_input);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen if (size == 0) {
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen /* read more to buffer */
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen *ret_r = read_more(sstream);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen if (*ret_r == 0 || *ret_r == -1)
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen return TRUE;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen }
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen /* we should have more now. */
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen data = i_stream_get_data(sstream->cur_input, &size);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen i_assert(size > 0);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen buffer_append(sstream->membuf, data, size);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen i_stream_skip(sstream->cur_input, size);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen }
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen offset = stream->istream.v_offset;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen stream->buffer = CONST_PTR_OFFSET(sstream->membuf->data, offset);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen pos = sstream->membuf->used - offset;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen *ret_r = pos - stream->pos;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen i_assert(*ret_r > 0);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen stream->pos = pos;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen return TRUE;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen}
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainenstatic int i_stream_seekable_write_failed(struct seekable_istream *sstream)
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen{
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen struct istream_private *stream = &sstream->istream;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen void *data;
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen i_assert(sstream->membuf == NULL);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->membuf =
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen buffer_create_dynamic(default_pool, sstream->write_peak);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen data = buffer_append_space_unsafe(sstream->membuf, sstream->write_peak);
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen if (pread_full(sstream->fd, data, sstream->write_peak, 0) < 0) {
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen i_error("read(%s) failed: %m", sstream->temp_path);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen buffer_free(&sstream->membuf);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen }
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen i_stream_destroy(&sstream->fd_input);
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen i_close_fd(&sstream->fd);
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen stream->max_buffer_size = (size_t)-1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_free_and_null(sstream->temp_path);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen}
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainenstatic ssize_t i_stream_seekable_read(struct istream_private *stream)
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen{
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen const unsigned char *data;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen size_t size, pos;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen ssize_t ret;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen stream->buffer = CONST_PTR_OFFSET(stream->buffer, stream->skip);
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen stream->pos -= stream->skip;
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen stream->skip = 0;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen if (sstream->membuf != NULL) {
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen if (read_from_buffer(sstream, &ret))
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen return ret;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen /* copy everything to temp file and use it as the stream */
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen if (copy_to_temp_file(sstream) < 0) {
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen stream->max_buffer_size = (size_t)-1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (!read_from_buffer(sstream, &ret))
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen i_unreached();
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen return ret;
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_assert(sstream->membuf == NULL);
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_assert(stream->istream.v_offset + stream->pos <= sstream->write_peak);
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen if (stream->istream.v_offset + stream->pos == sstream->write_peak) {
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen /* need to read more */
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen ret = read_more(sstream);
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen if (ret <= 0)
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen return ret;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen /* save to our file */
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen data = i_stream_get_data(sstream->cur_input, &size);
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen ret = write(sstream->fd, data, size);
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen if (ret <= 0) {
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen if (ret < 0 && !ENOSPACE(errno)) {
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen i_error("write_full(%s) failed: %m",
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen sstream->temp_path);
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen }
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen if (i_stream_seekable_write_failed(sstream) < 0)
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (!read_from_buffer(sstream, &ret))
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_unreached();
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_stream_sync(sstream->fd_input);
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen i_stream_skip(sstream->cur_input, ret);
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen sstream->write_peak += ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_stream_seek(sstream->fd_input, stream->istream.v_offset);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen ret = i_stream_read(sstream->fd_input);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (ret <= 0) {
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen stream->istream.eof = sstream->fd_input->eof;
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen stream->istream.stream_errno =
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->fd_input->stream_errno;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen ret = -2;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen stream->buffer = i_stream_get_data(sstream->fd_input, &pos);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen stream->pos -= stream->skip;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen stream->skip = 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen ret = pos > stream->pos ? (ssize_t)(pos - stream->pos) : ret;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen stream->pos = pos;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen return ret;
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen}
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int
02a54da28f376dd66d7939d8546a196a0045b486Timo Siraineni_stream_seekable_stat(struct istream_private *stream, bool exact)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const struct stat *st;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen uoff_t old_offset;
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen ssize_t ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (sstream->size != (uoff_t)-1) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* we've already reached EOF and know the size */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen stream->statbuf.st_size = sstream->size;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
ec5fec7eab19e134a2607b7e224b3e14a1771ee0Timo Sirainen if (sstream->membuf != NULL) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* we want to know the full size of the file, so read until
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen we're finished */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen old_offset = stream->istream.v_offset;
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen do {
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen i_stream_skip(&stream->istream,
ec5fec7eab19e134a2607b7e224b3e14a1771ee0Timo Sirainen stream->pos - stream->skip);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } while ((ret = i_stream_seekable_read(stream)) > 0);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (ret == 0) {
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen i_panic("i_stream_stat() used for non-blocking "
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen "seekable stream %s offset %"PRIuUOFF_T,
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen i_stream_get_name(sstream->cur_input),
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen sstream->cur_input->v_offset);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_stream_skip(&stream->istream, stream->pos - stream->skip);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_stream_seek(&stream->istream, old_offset);
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen unref_streams(sstream);
4145cbac82bfc0c8bfeceeca0ef841700117930cTimo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (stream->istream.stream_errno != 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen if (sstream->fd_input != NULL) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* using a file backed buffer, we can use real fstat() */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (i_stream_stat(sstream->fd_input, exact, &st) < 0)
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen return -1;
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen stream->statbuf = *st;
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen } else {
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen /* buffer is completely in memory */
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen i_assert(sstream->membuf != NULL);
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen
e593e507ee5ea3869271a631874c5c4b5c7a294dTimo Sirainen stream->statbuf.st_size = sstream->membuf->used;
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen }
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen return 0;
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstruct istream *
02a54da28f376dd66d7939d8546a196a0045b486Timo Siraineni_streams_merge(struct istream *input[], size_t max_buffer_size,
ec5fec7eab19e134a2607b7e224b3e14a1771ee0Timo Sirainen int (*fd_callback)(const char **path_r, void *context),
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen void *context) ATTR_NULL(4)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct seekable_istream *sstream;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const unsigned char *data;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen unsigned int count;
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen size_t size;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen bool blocking = TRUE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* if any of the streams isn't blocking, set ourself also nonblocking */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen for (count = 0; input[count] != NULL; count++) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (!input[count]->blocking)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen blocking = FALSE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_stream_ref(input[count]);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_assert(count != 0);
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen sstream = i_new(struct seekable_istream, 1);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->fd_callback = fd_callback;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->context = context;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->membuf = buffer_create_dynamic(default_pool, BUF_INITIAL_SIZE);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->istream.max_buffer_size = max_buffer_size;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->fd = -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->size = (uoff_t)-1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->input = i_new(struct istream *, count + 1);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen memcpy(sstream->input, input, sizeof(*input) * count);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->cur_input = sstream->input[0];
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* initialize our buffer from first stream's pending data */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen data = i_stream_get_data(sstream->cur_input, &size);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen buffer_append(sstream->membuf, data, size);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_stream_skip(sstream->cur_input, size);
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->istream.iostream.close = i_stream_seekable_close;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->istream.iostream.destroy = i_stream_seekable_destroy;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->istream.iostream.set_max_buffer_size =
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_stream_seekable_set_max_buffer_size;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->istream.read = i_stream_seekable_read;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sstream->istream.stat = i_stream_seekable_stat;
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen sstream->istream.istream.readable_fd = FALSE;
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen sstream->istream.istream.blocking = blocking;
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen sstream->istream.istream.seekable = TRUE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return i_stream_create(&sstream->istream, NULL, -1);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic bool inputs_are_seekable(struct istream *input[])
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen unsigned int count;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen for (count = 0; input[count] != NULL; count++) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (!input[count]->seekable)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return FALSE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return TRUE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainenstruct istream *
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Siraineni_stream_create_seekable(struct istream *input[],
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen size_t max_buffer_size,
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen int (*fd_callback)(const char **path_r, void *context),
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen void *context)
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen{
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen /* If all input streams are seekable, use concat istream instead */
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen if (inputs_are_seekable(input))
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen return i_stream_create_concat(input);
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen return i_streams_merge(input, max_buffer_size, fd_callback, context);
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen}
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainenstatic int seekable_fd_callback(const char **path_r, void *context)
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen char *temp_path_prefix = context;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen string_t *path;
31a574fda352ef4f71dbff9c30e15e4744e132c0Timo Sirainen int fd;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen path = t_str_new(128);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen str_append(path, temp_path_prefix);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (fd == -1) {
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen i_error("safe_mkstemp(%s) failed: %m", str_c(path));
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen return -1;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen }
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen /* we just want the fd, unlink it */
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (unlink(str_c(path)) < 0) {
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen /* shouldn't happen.. */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_error("unlink(%s) failed: %m", str_c(path));
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen i_close_fd(&fd);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen return -1;
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen *path_r = str_c(path);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen return fd;
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen}
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainenstruct istream *
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Siraineni_stream_create_seekable_path(struct istream *input[],
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen size_t max_buffer_size,
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen const char *temp_path_prefix)
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen{
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen struct seekable_istream *sstream;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen struct istream *stream;
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen if (inputs_are_seekable(input))
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen return i_stream_create_concat(input);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen stream = i_stream_create_seekable(input, max_buffer_size,
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen seekable_fd_callback,
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen i_strdup(temp_path_prefix));
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen sstream = (struct seekable_istream *)stream->real_stream;
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen sstream->free_context = TRUE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return stream;
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen