bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen#include "lib.h"
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen#include "buffer.h"
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen#include "str.h"
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen#include "memarea.h"
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen#include "read-full.h"
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen#include "write-full.h"
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen#include "safe-mkstemp.h"
0536ccb51d41e3078c3a9fa33e509fb4b2420f95Timo Sirainen#include "istream-private.h"
4499995f7029bafd85094694b6a14752ea34c9b3Timo Sirainen#include "istream-concat.h"
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen#include "istream-seekable.h"
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen#include <unistd.h>
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen#define BUF_INITIAL_SIZE (1024*32)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainenstruct seekable_istream {
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen struct istream_private istream;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainen char *temp_path;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen uoff_t write_peak;
d10cb4d7a80571af21f776c65604442bf09b1765Timo Sirainen uoff_t size;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen size_t buffer_peak;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainen int (*fd_callback)(const char **path_r, void *context);
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainen void *context;
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen struct istream **input, *cur_input;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen struct istream *fd_input;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen unsigned int cur_idx;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen int fd;
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen bool free_context;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen};
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainenstatic void i_stream_seekable_close(struct iostream_private *stream,
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen bool close_parent ATTR_UNUSED)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen{
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen sstream->fd = -1;
9a84b90d894a741ae6e090de104d31382a41d0aaJosef 'Jeff' Sipek i_stream_close(sstream->fd_input);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen}
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
a6f281d078ed03d555802c1a8e15fefce80132dcTimo Sirainenstatic void unref_streams(struct seekable_istream *sstream)
a6f281d078ed03d555802c1a8e15fefce80132dcTimo Sirainen{
a6f281d078ed03d555802c1a8e15fefce80132dcTimo Sirainen unsigned int i;
a6f281d078ed03d555802c1a8e15fefce80132dcTimo Sirainen
a6f281d078ed03d555802c1a8e15fefce80132dcTimo Sirainen for (i = 0; sstream->input[i] != NULL; i++)
a6f281d078ed03d555802c1a8e15fefce80132dcTimo Sirainen i_stream_unref(&sstream->input[i]);
a6f281d078ed03d555802c1a8e15fefce80132dcTimo Sirainen}
a6f281d078ed03d555802c1a8e15fefce80132dcTimo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainenstatic void i_stream_seekable_destroy(struct iostream_private *stream)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen{
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen i_stream_free_buffer(&sstream->istream);
204ee6ed414f5e4eeb6f6c10763b55daf56f11acJosef 'Jeff' Sipek i_stream_unref(&sstream->fd_input);
a6f281d078ed03d555802c1a8e15fefce80132dcTimo Sirainen unref_streams(sstream);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen if (sstream->free_context)
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen i_free(sstream->context);
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainen i_free(sstream->temp_path);
c1d4780bc0c9017e8e5d366b81e4fad31174c0adTimo Sirainen i_free(sstream->input);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen}
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainenstatic void
a94936bafd127680184da114c6a177b37ff656e5Timo Siraineni_stream_seekable_set_max_buffer_size(struct iostream_private *stream,
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen size_t max_size)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen{
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen unsigned int i;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
597dba3488c648ffb375ee4a552bd52ac4346979Timo Sirainen sstream->istream.max_buffer_size = max_size;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen if (sstream->fd_input != NULL)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen i_stream_set_max_buffer_size(sstream->fd_input, max_size);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen for (i = 0; sstream->input[i] != NULL; i++)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen i_stream_set_max_buffer_size(sstream->input[i], max_size);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen}
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainenstatic int copy_to_temp_file(struct seekable_istream *sstream)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen{
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen struct istream_private *stream = &sstream->istream;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen const char *path;
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen const unsigned char *buffer;
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen size_t size;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen int fd;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainen fd = sstream->fd_callback(&path, sstream->context);
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainen if (fd == -1)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen return -1;
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* copy our currently read buffer to it */
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen i_assert(stream->pos <= sstream->buffer_peak);
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen if (write_full(fd, stream->buffer, sstream->buffer_peak) < 0) {
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen if (!ENOSPACE(errno))
9e406b04bb5bed7d73aeed375c40c6a3fea1a2cbTimo Sirainen i_error("istream-seekable: write_full(%s) failed: %m", path);
4307c886579381dbb1897ea1388ae6978c96f560Timo Sirainen i_close_fd(&fd);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen return -1;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen }
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen sstream->temp_path = i_strdup(path);
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen sstream->write_peak = sstream->buffer_peak;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen sstream->fd = fd;
159de4e2ce3f541a9aa745d158b31203a40711e1Timo Sirainen sstream->fd_input = i_stream_create_fd_autoclose(&fd,
159de4e2ce3f541a9aa745d158b31203a40711e1Timo Sirainen I_MAX(stream->pos, sstream->istream.max_buffer_size));
ac6bba612af5207c24f6f02497d64b0ea03e7bbdTimo Sirainen i_stream_set_name(sstream->fd_input, t_strdup_printf(
ac6bba612af5207c24f6f02497d64b0ea03e7bbdTimo Sirainen "(seekable temp-istream for: %s)", i_stream_get_name(&stream->istream)));
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen /* read back the data we just had in our buffer */
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen for (;;) {
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen buffer = i_stream_get_data(sstream->fd_input, &size);
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen if (size >= stream->pos)
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen break;
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen
7f74811b78f8915e73dffc88bb49009e98b6846dTimo Sirainen ssize_t ret;
bcd286622779a93f809b11993db0550f8c7cc9b5Timo Sirainen if ((ret = i_stream_read_memarea(sstream->fd_input)) <= 0) {
7f74811b78f8915e73dffc88bb49009e98b6846dTimo Sirainen i_assert(ret != 0);
159de4e2ce3f541a9aa745d158b31203a40711e1Timo Sirainen i_assert(ret != -2);
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen i_error("istream-seekable: Couldn't read back "
7f74811b78f8915e73dffc88bb49009e98b6846dTimo Sirainen "in-memory input %s: %s",
7f74811b78f8915e73dffc88bb49009e98b6846dTimo Sirainen i_stream_get_name(&stream->istream),
7f74811b78f8915e73dffc88bb49009e98b6846dTimo Sirainen i_stream_get_error(sstream->fd_input));
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen i_stream_destroy(&sstream->fd_input);
3fe5eaeb32a8bbc31ce0673793b1c37f72d00d47Timo Sirainen i_close_fd(&sstream->fd);
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen return -1;
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen }
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen }
159de4e2ce3f541a9aa745d158b31203a40711e1Timo Sirainen /* Set the max buffer size only after we've already read everything
159de4e2ce3f541a9aa745d158b31203a40711e1Timo Sirainen into memory. For example with istream-data it's possible that
159de4e2ce3f541a9aa745d158b31203a40711e1Timo Sirainen more data exists in buffer than max_buffer_size. */
159de4e2ce3f541a9aa745d158b31203a40711e1Timo Sirainen i_stream_set_max_buffer_size(sstream->fd_input,
159de4e2ce3f541a9aa745d158b31203a40711e1Timo Sirainen sstream->istream.max_buffer_size);
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen stream->buffer = buffer;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen i_stream_free_buffer(&sstream->istream);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen return 0;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen}
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainenstatic ssize_t read_more(struct seekable_istream *sstream)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen{
5069adb2f5b3609fff9a0a705c6edeae56e0030aTimo Sirainen size_t size;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen ssize_t ret;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen if (sstream->cur_input == NULL) {
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen sstream->istream.istream.eof = TRUE;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen return -1;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen }
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
bcd286622779a93f809b11993db0550f8c7cc9b5Timo Sirainen while ((ret = i_stream_read_memarea(sstream->cur_input)) == -1) {
8d56f3334e22619abf56833d290bb1f49ac6722cTimo Sirainen if (sstream->cur_input->stream_errno != 0) {
2c42748505ef4aed83ff59b34e50ed5606900c86Timo Sirainen io_stream_set_error(&sstream->istream.iostream,
2c42748505ef4aed83ff59b34e50ed5606900c86Timo Sirainen "read(%s) failed: %s",
2c42748505ef4aed83ff59b34e50ed5606900c86Timo Sirainen i_stream_get_name(sstream->cur_input),
2c42748505ef4aed83ff59b34e50ed5606900c86Timo Sirainen i_stream_get_error(sstream->cur_input));
b8c009e1639efa53eb9e70720f28ce137d493e5eTimo Sirainen sstream->istream.istream.eof = TRUE;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen sstream->istream.istream.stream_errno =
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen sstream->cur_input->stream_errno;
156736910057b280cb9999d4c6c7221c4c80f5c2Timo Sirainen return -1;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen }
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* go to next stream */
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen sstream->cur_input = sstream->input[sstream->cur_idx++];
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen if (sstream->cur_input == NULL) {
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* last one, EOF */
d10cb4d7a80571af21f776c65604442bf09b1765Timo Sirainen sstream->size = sstream->istream.istream.v_offset;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen sstream->istream.istream.eof = TRUE;
81b1d14891415fef0c2f37ef1ef3680cdcc600f1Timo Sirainen unref_streams(sstream);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen return -1;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen }
5069adb2f5b3609fff9a0a705c6edeae56e0030aTimo Sirainen
5069adb2f5b3609fff9a0a705c6edeae56e0030aTimo Sirainen /* see if stream has pending data */
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen size = i_stream_get_data_size(sstream->cur_input);
5069adb2f5b3609fff9a0a705c6edeae56e0030aTimo Sirainen if (size != 0)
5069adb2f5b3609fff9a0a705c6edeae56e0030aTimo Sirainen return size;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen }
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen return ret;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen}
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainenstatic bool read_from_buffer(struct seekable_istream *sstream, ssize_t *ret_r)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen{
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen struct istream_private *stream = &sstream->istream;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen const unsigned char *data;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen size_t size, avail_size;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen if (stream->pos < sstream->buffer_peak) {
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen /* This could be the first read() or we could have already
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen seeked backwards. */
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen i_assert(stream->pos == 0 && stream->skip == 0);
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen stream->skip = stream->istream.v_offset;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen stream->pos = sstream->buffer_peak;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen size = stream->pos - stream->skip;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen } else {
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* need to read more */
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen i_assert(stream->pos == sstream->buffer_peak);
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen size = sstream->cur_input == NULL ? 0 :
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen i_stream_get_data_size(sstream->cur_input);
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen if (size == 0) {
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen /* read more to buffer */
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen *ret_r = read_more(sstream);
8d56f3334e22619abf56833d290bb1f49ac6722cTimo Sirainen if (*ret_r == 0 || *ret_r == -1)
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen return TRUE;
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen }
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* we should have more now. */
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen data = i_stream_get_data(sstream->cur_input, &size);
5297aa3ceddf3a4ecc09f49c832bc424eff8f906Timo Sirainen i_assert(size > 0);
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen /* change skip to 0 temporarily so i_stream_try_alloc() won't try to
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen compress the buffer. */
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen size_t old_skip = stream->skip;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen stream->skip = 0;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen bool have_space = i_stream_try_alloc(stream, size, &avail_size);
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen stream->skip = old_skip;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen if (!have_space)
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen return FALSE;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen if (size > avail_size)
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen size = avail_size;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen memcpy(stream->w_buffer + stream->pos, data, size);
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen stream->pos += size;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen sstream->buffer_peak += size;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen i_stream_skip(sstream->cur_input, size);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen }
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen *ret_r = size;
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen i_assert(*ret_r > 0);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen return TRUE;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen}
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainenstatic int i_stream_seekable_write_failed(struct seekable_istream *sstream)
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen{
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen struct istream_private *stream = &sstream->istream;
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen void *data;
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen i_assert(sstream->fd != -1);
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen stream->max_buffer_size = (size_t)-1;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen data = i_stream_alloc(stream, sstream->write_peak);
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen if (pread_full(sstream->fd, data, sstream->write_peak, 0) < 0) {
9e406b04bb5bed7d73aeed375c40c6a3fea1a2cbTimo Sirainen i_error("istream-seekable: read(%s) failed: %m", sstream->temp_path);
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen memarea_unref(&stream->memarea);
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen return -1;
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen }
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen i_stream_destroy(&sstream->fd_input);
4307c886579381dbb1897ea1388ae6978c96f560Timo Sirainen i_close_fd(&sstream->fd);
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen i_free_and_null(sstream->temp_path);
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen return 0;
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen}
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainenstatic ssize_t i_stream_seekable_read(struct istream_private *stream)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen{
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen const unsigned char *data;
7efee0bb408b0d5253e41997857bdda57855cdc7Timo Sirainen size_t size, pos;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen ssize_t ret;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen if (sstream->fd == -1) {
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen if (read_from_buffer(sstream, &ret))
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen return ret;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* copy everything to temp file and use it as the stream */
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen if (copy_to_temp_file(sstream) < 0) {
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen stream->max_buffer_size = (size_t)-1;
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen if (!read_from_buffer(sstream, &ret))
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen i_unreached();
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen return ret;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen }
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen i_assert(sstream->fd != -1);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen }
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen stream->buffer = CONST_PTR_OFFSET(stream->buffer, stream->skip);
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen stream->pos -= stream->skip;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen stream->skip = 0;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen i_assert(stream->istream.v_offset + stream->pos <= sstream->write_peak);
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen if (stream->istream.v_offset + stream->pos == sstream->write_peak) {
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* need to read more */
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen if (sstream->cur_input == NULL ||
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen i_stream_get_data_size(sstream->cur_input) == 0) {
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen ret = read_more(sstream);
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen if (ret == -1 || ret == 0)
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen return ret;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen }
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* save to our file */
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen data = i_stream_get_data(sstream->cur_input, &size);
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen ret = write(sstream->fd, data, size);
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen if (ret <= 0) {
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen if (ret < 0 && !ENOSPACE(errno)) {
9e406b04bb5bed7d73aeed375c40c6a3fea1a2cbTimo Sirainen i_error("istream-seekable: write_full(%s) failed: %m",
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen sstream->temp_path);
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen }
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen if (i_stream_seekable_write_failed(sstream) < 0)
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen return -1;
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen if (!read_from_buffer(sstream, &ret))
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen i_unreached();
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen return ret;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen }
7efee0bb408b0d5253e41997857bdda57855cdc7Timo Sirainen i_stream_sync(sstream->fd_input);
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen i_stream_skip(sstream->cur_input, ret);
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen sstream->write_peak += ret;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen }
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen i_stream_seek(sstream->fd_input, stream->istream.v_offset);
bcd286622779a93f809b11993db0550f8c7cc9b5Timo Sirainen ret = i_stream_read_memarea(sstream->fd_input);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen if (ret <= 0) {
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen stream->istream.eof = sstream->fd_input->eof;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen stream->istream.stream_errno =
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen sstream->fd_input->stream_errno;
1b9aae1cb53708b5a3d861b4db6ba96ac2eb35b4Timo Sirainen } else {
1b9aae1cb53708b5a3d861b4db6ba96ac2eb35b4Timo Sirainen ret = -2;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen }
7efee0bb408b0d5253e41997857bdda57855cdc7Timo Sirainen
7efee0bb408b0d5253e41997857bdda57855cdc7Timo Sirainen stream->buffer = i_stream_get_data(sstream->fd_input, &pos);
7efee0bb408b0d5253e41997857bdda57855cdc7Timo Sirainen stream->pos -= stream->skip;
7efee0bb408b0d5253e41997857bdda57855cdc7Timo Sirainen stream->skip = 0;
7efee0bb408b0d5253e41997857bdda57855cdc7Timo Sirainen
4da70fe8c9cb6e57b36103d78ab1e9c8654f76d9Timo Sirainen ret = pos > stream->pos ? (ssize_t)(pos - stream->pos) : ret;
7efee0bb408b0d5253e41997857bdda57855cdc7Timo Sirainen stream->pos = pos;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen return ret;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen}
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainenstatic int
a94936bafd127680184da114c6a177b37ff656e5Timo Siraineni_stream_seekable_stat(struct istream_private *stream, bool exact)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen{
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen const struct stat *st;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen uoff_t old_offset;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen ssize_t ret;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
d10cb4d7a80571af21f776c65604442bf09b1765Timo Sirainen if (sstream->size != (uoff_t)-1) {
d10cb4d7a80571af21f776c65604442bf09b1765Timo Sirainen /* we've already reached EOF and know the size */
d10cb4d7a80571af21f776c65604442bf09b1765Timo Sirainen stream->statbuf.st_size = sstream->size;
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen return 0;
d10cb4d7a80571af21f776c65604442bf09b1765Timo Sirainen }
d10cb4d7a80571af21f776c65604442bf09b1765Timo Sirainen
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen /* we want to know the full size of the file, so read until
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen we're finished */
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen old_offset = stream->istream.v_offset;
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen do {
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen i_stream_skip(&stream->istream,
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen stream->pos - stream->skip);
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen } while ((ret = i_stream_seekable_read(stream)) > 0);
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen if (ret == 0) {
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen i_panic("i_stream_stat() used for non-blocking "
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen "seekable stream %s offset %"PRIuUOFF_T,
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen i_stream_get_name(sstream->cur_input),
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen sstream->cur_input->v_offset);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen }
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen i_stream_skip(&stream->istream, stream->pos - stream->skip);
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen i_stream_seek(&stream->istream, old_offset);
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen unref_streams(sstream);
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen
2a8b891366a3fc69524c2bb07f68d42c16223a56Timo Sirainen if (stream->istream.stream_errno != 0)
2a8b891366a3fc69524c2bb07f68d42c16223a56Timo Sirainen return -1;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
8255a22cccf3b0ccf38206c594941820ac1c9e00Timo Sirainen if (sstream->fd_input != NULL) {
8255a22cccf3b0ccf38206c594941820ac1c9e00Timo Sirainen /* using a file backed buffer, we can use real fstat() */
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen if (i_stream_stat(sstream->fd_input, exact, &st) < 0)
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen return -1;
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen stream->statbuf = *st;
8255a22cccf3b0ccf38206c594941820ac1c9e00Timo Sirainen } else {
8255a22cccf3b0ccf38206c594941820ac1c9e00Timo Sirainen /* buffer is completely in memory */
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen i_assert(sstream->fd == -1);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen stream->statbuf.st_size = stream->pos;
8255a22cccf3b0ccf38206c594941820ac1c9e00Timo Sirainen }
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen return 0;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen}
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainenstatic void i_stream_seekable_seek(struct istream_private *stream,
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen uoff_t v_offset, bool mark)
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen{
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen if (v_offset <= stream->istream.v_offset) {
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen /* seeking backwards */
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen stream->istream.v_offset = v_offset;
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen stream->skip = stream->pos = 0;
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen } else {
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen /* we can't skip over data we haven't yet read and written to
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen our buffer/temp file */
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen i_stream_default_seek_nonseekable(stream, v_offset, mark);
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen }
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen}
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainenstatic struct istream_snapshot *
448ab3576e0def2135c6f4681bd412dd59425d44Timo Siraineni_stream_seekable_snapshot(struct istream_private *stream,
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen struct istream_snapshot *prev_snapshot)
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen{
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen if (sstream->fd == -1) {
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen /* still in memory */
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen if (stream->memarea == NULL)
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen return prev_snapshot;
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen return i_stream_default_snapshot(stream, prev_snapshot);
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen } else {
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen /* using the fd_input stream */
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen return sstream->fd_input->real_stream->
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen snapshot(sstream->fd_input->real_stream, prev_snapshot);
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen }
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen}
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainenstruct istream *
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Siraineni_streams_merge(struct istream *input[], size_t max_buffer_size,
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen int (*fd_callback)(const char **path_r, void *context),
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen void *context) ATTR_NULL(4)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen{
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen struct seekable_istream *sstream;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen const unsigned char *data;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen unsigned int count;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen size_t size;
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen bool blocking = TRUE;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
d907b51d224e965a876eada6bc49455773e416e9Timo Sirainen i_assert(max_buffer_size > 0);
d907b51d224e965a876eada6bc49455773e416e9Timo Sirainen
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen /* if any of the streams isn't blocking, set ourself also nonblocking */
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen for (count = 0; input[count] != NULL; count++) {
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen if (!input[count]->blocking)
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen blocking = FALSE;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen i_stream_ref(input[count]);
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen }
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen i_assert(count != 0);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen sstream = i_new(struct seekable_istream, 1);
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainen sstream->fd_callback = fd_callback;
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainen sstream->context = context;
597dba3488c648ffb375ee4a552bd52ac4346979Timo Sirainen sstream->istream.max_buffer_size = max_buffer_size;
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen sstream->fd = -1;
d10cb4d7a80571af21f776c65604442bf09b1765Timo Sirainen sstream->size = (uoff_t)-1;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen sstream->input = i_new(struct istream *, count + 1);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen memcpy(sstream->input, input, sizeof(*input) * count);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen sstream->cur_input = sstream->input[0];
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen sstream->istream.iostream.close = i_stream_seekable_close;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen sstream->istream.iostream.destroy = i_stream_seekable_destroy;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen sstream->istream.iostream.set_max_buffer_size =
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen i_stream_seekable_set_max_buffer_size;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen sstream->istream.read = i_stream_seekable_read;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen sstream->istream.stat = i_stream_seekable_stat;
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen sstream->istream.seek = i_stream_seekable_seek;
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen sstream->istream.snapshot = i_stream_seekable_snapshot;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
da2aa032ccfa8e7e4a4380ef738014549f4d2c2dTimo Sirainen sstream->istream.istream.readable_fd = FALSE;
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen sstream->istream.istream.blocking = blocking;
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen sstream->istream.istream.seekable = TRUE;
2974dca6be5120e49279f06c8aa952e5fac56048Timo Sirainen (void)i_stream_create(&sstream->istream, NULL, -1, 0);
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen /* initialize our buffer from first stream's pending data */
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen data = i_stream_get_data(sstream->cur_input, &size);
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen if (size > 0) {
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen memcpy(i_stream_alloc(&sstream->istream, size), data, size);
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen sstream->buffer_peak = size;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen i_stream_skip(sstream->cur_input, size);
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen }
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen return &sstream->istream.istream;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen}
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainenstatic bool inputs_are_seekable(struct istream *input[])
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen{
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen unsigned int count;
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen for (count = 0; input[count] != NULL; count++) {
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen if (!input[count]->seekable)
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen return FALSE;
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen }
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen return TRUE;
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen}
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainenstruct istream *
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Siraineni_stream_create_seekable(struct istream *input[],
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen size_t max_buffer_size,
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen int (*fd_callback)(const char **path_r, void *context),
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen void *context)
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen{
d907b51d224e965a876eada6bc49455773e416e9Timo Sirainen i_assert(max_buffer_size > 0);
d907b51d224e965a876eada6bc49455773e416e9Timo Sirainen
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen /* If all input streams are seekable, use concat istream instead */
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen if (inputs_are_seekable(input))
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen return i_stream_create_concat(input);
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen return i_streams_merge(input, max_buffer_size, fd_callback, context);
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen}
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainenstatic int seekable_fd_callback(const char **path_r, void *context)
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen{
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen char *temp_path_prefix = context;
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen string_t *path;
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen int fd;
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen path = t_str_new(128);
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen str_append(path, temp_path_prefix);
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1);
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen if (fd == -1) {
9e406b04bb5bed7d73aeed375c40c6a3fea1a2cbTimo Sirainen i_error("istream-seekable: safe_mkstemp(%s) failed: %m", str_c(path));
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen return -1;
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen }
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen /* we just want the fd, unlink it */
46b823ac3bce2c0f9f0fc73911e48d3a77b04fbeTimo Sirainen if (i_unlink(str_c(path)) < 0) {
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen /* shouldn't happen.. */
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen i_close_fd(&fd);
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen return -1;
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen }
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen *path_r = str_c(path);
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen return fd;
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen}
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainenstruct istream *
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Siraineni_stream_create_seekable_path(struct istream *input[],
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen size_t max_buffer_size,
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen const char *temp_path_prefix)
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen{
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen struct seekable_istream *sstream;
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen struct istream *stream;
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen
17466c748183b19f7775374ce6afbd7bf62d479dTimo Sirainen i_assert(temp_path_prefix != NULL);
d907b51d224e965a876eada6bc49455773e416e9Timo Sirainen i_assert(max_buffer_size > 0);
d907b51d224e965a876eada6bc49455773e416e9Timo Sirainen
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen if (inputs_are_seekable(input))
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen return i_stream_create_concat(input);
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen stream = i_stream_create_seekable(input, max_buffer_size,
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen seekable_fd_callback,
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen i_strdup(temp_path_prefix));
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen sstream = (struct seekable_istream *)stream->real_stream;
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen sstream->free_context = TRUE;
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen return stream;
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen}