istream-seekable.c revision 3fe5eaeb32a8bbc31ce0673793b1c37f72d00d47
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen#include "buffer.h"
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen#include "str.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "read-full.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "write-full.h"
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen#include "safe-mkstemp.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "istream-private.h"
65f8fb656051f1059f7b5a2da9c5555adcc30439Timo Sirainen#include "istream-concat.h"
65f8fb656051f1059f7b5a2da9c5555adcc30439Timo Sirainen#include "istream-seekable.h"
65f8fb656051f1059f7b5a2da9c5555adcc30439Timo Sirainen
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen#include <unistd.h>
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen
15b9759df8e4f6fb00c115353827a2aebbebfebcTimo Sirainen#define BUF_INITIAL_SIZE (1024*32)
15b9759df8e4f6fb00c115353827a2aebbebfebcTimo Sirainen
d7a56951e519c28ac9b1fd5810a455a9ba238327Timo Sirainenstruct seekable_istream {
d7a56951e519c28ac9b1fd5810a455a9ba238327Timo Sirainen struct istream_private istream;
15b9759df8e4f6fb00c115353827a2aebbebfebcTimo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen char *temp_path;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen uoff_t write_peak;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen uoff_t size;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen int (*fd_callback)(const char **path_r, void *context);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen void *context;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen buffer_t *membuf;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen struct istream **input, *cur_input;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct istream *fd_input;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen unsigned int cur_idx;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen int fd;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen bool free_context;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen};
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenstatic void i_stream_seekable_close(struct iostream_private *stream,
2dd39e478269d6fb0bb26d12b394aa30ee965e38Timo Sirainen bool close_parent ATTR_UNUSED)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen sstream->fd = -1;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (sstream->fd_input != NULL)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen i_stream_close(sstream->fd_input);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen}
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void unref_streams(struct seekable_istream *sstream)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen unsigned int i;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen for (i = 0; sstream->input[i] != NULL; i++)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_stream_unref(&sstream->input[i]);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenstatic void i_stream_seekable_destroy(struct iostream_private *stream)
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen{
3e564425db51f3921ce4de11859777135fdedd15Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen buffer_free(&sstream->membuf);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen i_stream_unref(&sstream->fd_input);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen unref_streams(sstream);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (sstream->free_context)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen i_free(sstream->context);
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen i_free(sstream->temp_path);
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen i_free(sstream->input);
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen}
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainenstatic void
563273bdac80393af63b9520cbf4d24cc0efd028Timo Siraineni_stream_seekable_set_max_buffer_size(struct iostream_private *stream,
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen size_t max_size)
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen{
57a8c6a95e4bce3eeaba36985adb81c07dd683ffTimo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen unsigned int i;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen sstream->istream.max_buffer_size = max_size;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (sstream->fd_input != NULL)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_stream_set_max_buffer_size(sstream->fd_input, max_size);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen for (i = 0; sstream->input[i] != NULL; i++)
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen i_stream_set_max_buffer_size(sstream->input[i], max_size);
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen}
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainenstatic int copy_to_temp_file(struct seekable_istream *sstream)
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen{
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen struct istream_private *stream = &sstream->istream;
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen const char *path;
f1743785713e7632459d623d5df2108f4b93accbTimo Sirainen const unsigned char *buffer;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen size_t size;
70ead6466f9baa8294e71fc2fba0a4f54f488b5eTimo Sirainen int fd;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
ccc895c0358108d2304239063e940b7d75f364abTimo Sirainen fd = sstream->fd_callback(&path, sstream->context);
8d630c15a8ed6f85553467c3a231a273defca5f6Timo Sirainen if (fd == -1)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
c5ab90cfad9cc3e33bcb1baeb30ffc82a7b7053aTimo Sirainen
c5ab90cfad9cc3e33bcb1baeb30ffc82a7b7053aTimo Sirainen /* copy our currently read buffer to it */
c5ab90cfad9cc3e33bcb1baeb30ffc82a7b7053aTimo Sirainen if (write_full(fd, sstream->membuf->data, sstream->membuf->used) < 0) {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (!ENOSPACE(errno))
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen i_error("istream-seekable: write_full(%s) failed: %m", path);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_close_fd(&fd);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen }
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen sstream->temp_path = i_strdup(path);
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen sstream->write_peak = sstream->membuf->used;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen sstream->fd = fd;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen sstream->fd_input = i_stream_create_fd_autoclose(&fd,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen I_MAX(stream->pos, sstream->istream.max_buffer_size));
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen i_stream_set_name(sstream->fd_input, t_strdup_printf(
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen "(seekable temp-istream for: %s)", i_stream_get_name(&stream->istream)));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen /* read back the data we just had in our buffer */
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen i_stream_seek(sstream->fd_input, stream->istream.v_offset);
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen for (;;) {
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen buffer = i_stream_get_data(sstream->fd_input, &size);
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen if (size >= stream->pos)
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen break;
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen ssize_t ret;
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen if ((ret = i_stream_read(sstream->fd_input)) <= 0) {
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen i_assert(ret != 0);
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen i_assert(ret != -2);
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen i_error("istream-seekable: Couldn't read back "
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen "in-memory input %s: %s",
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen i_stream_get_name(&stream->istream),
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen i_stream_get_error(sstream->fd_input));
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen i_stream_destroy(&sstream->fd_input);
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen i_close_fd(&sstream->fd);
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen return -1;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen }
4b41116563110d00330896a568eff1078c382827Timo Sirainen }
4b41116563110d00330896a568eff1078c382827Timo Sirainen /* Set the max buffer size only after we've already read everything
4b41116563110d00330896a568eff1078c382827Timo Sirainen into memory. For example with istream-data it's possible that
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen more data exists in buffer than max_buffer_size. */
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen i_stream_set_max_buffer_size(sstream->fd_input,
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen sstream->istream.max_buffer_size);
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen stream->buffer = buffer;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen stream->pos = size;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen buffer_free(&sstream->membuf);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return 0;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen}
b2c1349cf07410aefab0f5b17153af9e5cfcf48fTimo Sirainen
ccec5f82349eae44087900c0e64ed1fd5a1a6fcaTimo Sirainenstatic ssize_t read_more(struct seekable_istream *sstream)
4d2211dac61c615c5bdfd501ea54d46c89d41b0fTimo Sirainen{
4d2211dac61c615c5bdfd501ea54d46c89d41b0fTimo Sirainen size_t size;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ssize_t ret;
48270badadd82279bfe50ae3d187aea8b0b2b30eTimo Sirainen
48270badadd82279bfe50ae3d187aea8b0b2b30eTimo Sirainen if (sstream->cur_input == NULL) {
48270badadd82279bfe50ae3d187aea8b0b2b30eTimo Sirainen sstream->istream.istream.eof = TRUE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
61f5256ef248d35459b53534ae428bf6d016e1c5Timo Sirainen }
cb05ecbd96ddb5e53c1850d27434541138a3f284Timo Sirainen
cb05ecbd96ddb5e53c1850d27434541138a3f284Timo Sirainen while ((ret = i_stream_read(sstream->cur_input)) == -1) {
cb05ecbd96ddb5e53c1850d27434541138a3f284Timo Sirainen if (sstream->cur_input->stream_errno != 0) {
61f5256ef248d35459b53534ae428bf6d016e1c5Timo Sirainen io_stream_set_error(&sstream->istream.iostream,
e3796bfd2bc0fd5ba664893d346df9334a5b3af0Timo Sirainen "read(%s) failed: %s",
e3796bfd2bc0fd5ba664893d346df9334a5b3af0Timo Sirainen i_stream_get_name(sstream->cur_input),
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen i_stream_get_error(sstream->cur_input));
5afa8e2edf4f313cd56e5909f92f39c3b5b7b4d3Timo Sirainen sstream->istream.istream.eof = TRUE;
5afa8e2edf4f313cd56e5909f92f39c3b5b7b4d3Timo Sirainen sstream->istream.istream.stream_errno =
408e5be344c9131fdebe771718a5bf49f88cc51cTimo Sirainen sstream->cur_input->stream_errno;
408e5be344c9131fdebe771718a5bf49f88cc51cTimo Sirainen return -1;
408e5be344c9131fdebe771718a5bf49f88cc51cTimo Sirainen }
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen /* go to next stream */
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen sstream->cur_input = sstream->input[sstream->cur_idx++];
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen if (sstream->cur_input == NULL) {
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen /* last one, EOF */
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen sstream->size = sstream->istream.istream.v_offset;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen sstream->istream.istream.eof = TRUE;
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen unref_streams(sstream);
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen return -1;
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen }
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen /* see if stream has pending data */
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen size = i_stream_get_data_size(sstream->cur_input);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (size != 0)
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return size;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen }
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return ret;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen}
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic bool read_from_buffer(struct seekable_istream *sstream, ssize_t *ret_r)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct istream_private *stream = &sstream->istream;
f95b3d29bc56f139c18c880aa574a0ca72b0cffbTimo Sirainen const unsigned char *data;
f95b3d29bc56f139c18c880aa574a0ca72b0cffbTimo Sirainen size_t size, pos, offset;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_assert(stream->skip == 0);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen if (stream->istream.v_offset + stream->pos >= sstream->membuf->used) {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen /* need to read more */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (sstream->membuf->used >= stream->max_buffer_size)
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen return FALSE;
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen
a3ee5ce6ecc8e228ee69300fdd562d7ac8be89a7Timo Sirainen size = sstream->cur_input == NULL ? 0 :
bd1b2615928a1e8be190cb0405754f0aec8cac2fTimo Sirainen i_stream_get_data_size(sstream->cur_input);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (size == 0) {
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen /* read more to buffer */
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen *ret_r = read_more(sstream);
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen if (*ret_r == 0 || *ret_r == -1)
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen return TRUE;
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen }
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen
bd4d0a1a7c0626452b8d82f37e3ec07267ac9896Timo Sirainen /* we should have more now. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen data = i_stream_get_data(sstream->cur_input, &size);
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen i_assert(size > 0);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen buffer_append(sstream->membuf, data, size);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_stream_skip(sstream->cur_input, size);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen }
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen offset = stream->istream.v_offset;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen stream->buffer = CONST_PTR_OFFSET(sstream->membuf->data, offset);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen pos = sstream->membuf->used - offset;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen *ret_r = pos - stream->pos;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen i_assert(*ret_r > 0);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen stream->pos = pos;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return TRUE;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen}
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainenstatic int i_stream_seekable_write_failed(struct seekable_istream *sstream)
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen{
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen struct istream_private *stream = &sstream->istream;
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen void *data;
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen i_assert(sstream->membuf == NULL);
f23ede27743c1aa03eacbfc634d6a10de9110c91Timo Sirainen
f23ede27743c1aa03eacbfc634d6a10de9110c91Timo Sirainen sstream->membuf =
f23ede27743c1aa03eacbfc634d6a10de9110c91Timo Sirainen buffer_create_dynamic(default_pool, sstream->write_peak);
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen data = buffer_append_space_unsafe(sstream->membuf, sstream->write_peak);
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen
47001341950b8588c5f3a96b75864dab48e279aeTimo Sirainen if (pread_full(sstream->fd, data, sstream->write_peak, 0) < 0) {
47001341950b8588c5f3a96b75864dab48e279aeTimo Sirainen i_error("istream-seekable: read(%s) failed: %m", sstream->temp_path);
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen buffer_free(&sstream->membuf);
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen return -1;
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen }
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen i_stream_destroy(&sstream->fd_input);
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen i_close_fd(&sstream->fd);
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen stream->max_buffer_size = (size_t)-1;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen i_free_and_null(sstream->temp_path);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return 0;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen}
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainenstatic ssize_t i_stream_seekable_read(struct istream_private *stream)
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen{
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
4bbd396aa6198c84f3f7763b6e8a63a26e97e141Timo Sirainen const unsigned char *data;
4bbd396aa6198c84f3f7763b6e8a63a26e97e141Timo Sirainen size_t size, pos;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen ssize_t ret;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen stream->buffer = CONST_PTR_OFFSET(stream->buffer, stream->skip);
0d86aa0d47f7393c669c084b34c0537b193688adTimo Sirainen stream->pos -= stream->skip;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen stream->skip = 0;
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen if (sstream->membuf != NULL) {
aa247243412a49f9bdebf7255e131dc6ece4ed46Timo Sirainen if (read_from_buffer(sstream, &ret))
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return ret;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen /* copy everything to temp file and use it as the stream */
a757f31393b9d6fc7760a9dec8363404ab3ae576Timo Sirainen if (copy_to_temp_file(sstream) < 0) {
a757f31393b9d6fc7760a9dec8363404ab3ae576Timo Sirainen stream->max_buffer_size = (size_t)-1;
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen if (!read_from_buffer(sstream, &ret))
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen i_unreached();
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen return ret;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen }
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen i_assert(sstream->membuf == NULL);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen }
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen i_assert(stream->istream.v_offset + stream->pos <= sstream->write_peak);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (stream->istream.v_offset + stream->pos == sstream->write_peak) {
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen /* need to read more */
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen ret = read_more(sstream);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (ret == -1 || ret == 0)
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen return ret;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen /* save to our file */
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen data = i_stream_get_data(sstream->cur_input, &size);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen ret = write(sstream->fd, data, size);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (ret <= 0) {
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (ret < 0 && !ENOSPACE(errno)) {
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen i_error("istream-seekable: write_full(%s) failed: %m",
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen sstream->temp_path);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen }
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (i_stream_seekable_write_failed(sstream) < 0)
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return -1;
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen if (!read_from_buffer(sstream, &ret))
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen i_unreached();
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen return ret;
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen }
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen i_stream_sync(sstream->fd_input);
838e367716bbd5e44b4a1691db9cbf72af53e9f0Timo Sirainen i_stream_skip(sstream->cur_input, ret);
838e367716bbd5e44b4a1691db9cbf72af53e9f0Timo Sirainen sstream->write_peak += ret;
01404d41657a104c5ea1c12bb87f9c321e9c1ac4Timo Sirainen }
01404d41657a104c5ea1c12bb87f9c321e9c1ac4Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen i_stream_seek(sstream->fd_input, stream->istream.v_offset);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen ret = i_stream_read(sstream->fd_input);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (ret <= 0) {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen stream->istream.eof = sstream->fd_input->eof;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen stream->istream.stream_errno =
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen sstream->fd_input->stream_errno;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen } else {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen ret = -2;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen }
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen stream->buffer = i_stream_get_data(sstream->fd_input, &pos);
310767ca33e7636d40ec45dee68a2c319a5fa3c0Timo Sirainen stream->pos -= stream->skip;
310767ca33e7636d40ec45dee68a2c319a5fa3c0Timo Sirainen stream->skip = 0;
310767ca33e7636d40ec45dee68a2c319a5fa3c0Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen ret = pos > stream->pos ? (ssize_t)(pos - stream->pos) : ret;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen stream->pos = pos;
b0a901f1dbe9e05ac1c92a0974af6bce0274f31aTimo Sirainen return ret;
b0a901f1dbe9e05ac1c92a0974af6bce0274f31aTimo Sirainen}
b0a901f1dbe9e05ac1c92a0974af6bce0274f31aTimo Sirainen
1eb17e61d3d38372674aa0c55caedb0185a985f5Timo Sirainenstatic int
1eb17e61d3d38372674aa0c55caedb0185a985f5Timo Siraineni_stream_seekable_stat(struct istream_private *stream, bool exact)
1eb17e61d3d38372674aa0c55caedb0185a985f5Timo Sirainen{
b0a901f1dbe9e05ac1c92a0974af6bce0274f31aTimo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
b0a901f1dbe9e05ac1c92a0974af6bce0274f31aTimo Sirainen const struct stat *st;
b0a901f1dbe9e05ac1c92a0974af6bce0274f31aTimo Sirainen uoff_t old_offset;
b0a901f1dbe9e05ac1c92a0974af6bce0274f31aTimo Sirainen ssize_t ret;
b0a901f1dbe9e05ac1c92a0974af6bce0274f31aTimo Sirainen
b0a901f1dbe9e05ac1c92a0974af6bce0274f31aTimo Sirainen if (sstream->size != (uoff_t)-1) {
b0a901f1dbe9e05ac1c92a0974af6bce0274f31aTimo Sirainen /* we've already reached EOF and know the size */
b0a901f1dbe9e05ac1c92a0974af6bce0274f31aTimo Sirainen stream->statbuf.st_size = sstream->size;
b0a901f1dbe9e05ac1c92a0974af6bce0274f31aTimo Sirainen return 0;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen }
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* we want to know the full size of the file, so read until
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen we're finished */
e4c90f0b88e40a8f92b8f5e1f1a3ea701e5c965cTimo Sirainen old_offset = stream->istream.v_offset;
defb12ecd360df672ffb2f4dbf4d1218a0a9549cTimo Sirainen do {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_stream_skip(&stream->istream,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen stream->pos - stream->skip);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen } while ((ret = i_stream_seekable_read(stream)) > 0);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (ret == 0) {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen i_panic("i_stream_stat() used for non-blocking "
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "seekable stream %s offset %"PRIuUOFF_T,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_stream_get_name(sstream->cur_input),
194603b35061fea1ee8d171a7104b6985c610966Timo Sirainen sstream->cur_input->v_offset);
194603b35061fea1ee8d171a7104b6985c610966Timo Sirainen }
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen i_stream_skip(&stream->istream, stream->pos - stream->skip);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen i_stream_seek(&stream->istream, old_offset);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen unref_streams(sstream);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (stream->istream.stream_errno != 0)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (sstream->fd_input != NULL) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* using a file backed buffer, we can use real fstat() */
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (i_stream_stat(sstream->fd_input, exact, &st) < 0)
e63bdfedcf61e1a9ee21990140cbd0d0638da7e1Timo Sirainen return -1;
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen stream->statbuf = *st;
e63bdfedcf61e1a9ee21990140cbd0d0638da7e1Timo Sirainen } else {
e63bdfedcf61e1a9ee21990140cbd0d0638da7e1Timo Sirainen /* buffer is completely in memory */
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen i_assert(sstream->membuf != NULL);
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen stream->statbuf.st_size = sstream->membuf->used;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen }
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen return 0;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen}
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainenstatic void i_stream_seekable_seek(struct istream_private *stream,
a3ee5ce6ecc8e228ee69300fdd562d7ac8be89a7Timo Sirainen uoff_t v_offset, bool mark)
f4616f1875297fb2f583d913c0f01b075bdecd5bTimo Sirainen{
f4616f1875297fb2f583d913c0f01b075bdecd5bTimo Sirainen if (v_offset <= stream->istream.v_offset) {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen /* seeking backwards */
4321f6c969e7b8f6b243ff5bb6b8d297921676f6Timo Sirainen stream->istream.v_offset = v_offset;
4321f6c969e7b8f6b243ff5bb6b8d297921676f6Timo Sirainen stream->skip = stream->pos = 0;
d54ab8987e482a8df250615b44f41fa040c38741Timo Sirainen } else {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* we can't skip over data we haven't yet read and written to
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen our buffer/temp file */
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen i_stream_default_seek_nonseekable(stream, v_offset, mark);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen }
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen}
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainenstruct istream *
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Siraineni_streams_merge(struct istream *input[], size_t max_buffer_size,
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen int (*fd_callback)(const char **path_r, void *context),
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen void *context) ATTR_NULL(4)
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen{
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen struct seekable_istream *sstream;
b19a1420da0618a10edf67c2cfd13c8c8633057aTimo Sirainen const unsigned char *data;
e050e5c9b1688765f1fdfce9b7141f7b614383fdTimo Sirainen unsigned int count;
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen size_t size;
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen bool blocking = TRUE;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen i_assert(max_buffer_size > 0);
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen /* if any of the streams isn't blocking, set ourself also nonblocking */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen for (count = 0; input[count] != NULL; count++) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (!input[count]->blocking)
cfb86adbda733e4945db44c6be27f8fda142c0a3Timo Sirainen blocking = FALSE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_stream_ref(input[count]);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen i_assert(count != 0);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen sstream = i_new(struct seekable_istream, 1);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen sstream->fd_callback = fd_callback;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen sstream->context = context;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen sstream->membuf = buffer_create_dynamic(default_pool, BUF_INITIAL_SIZE);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen sstream->istream.max_buffer_size = max_buffer_size;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen sstream->fd = -1;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen sstream->size = (uoff_t)-1;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen sstream->input = i_new(struct istream *, count + 1);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen memcpy(sstream->input, input, sizeof(*input) * count);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen sstream->cur_input = sstream->input[0];
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* initialize our buffer from first stream's pending data */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen data = i_stream_get_data(sstream->cur_input, &size);
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen buffer_append(sstream->membuf, data, size);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_stream_skip(sstream->cur_input, size);
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen sstream->istream.iostream.close = i_stream_seekable_close;
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen sstream->istream.iostream.destroy = i_stream_seekable_destroy;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen sstream->istream.iostream.set_max_buffer_size =
b19a1420da0618a10edf67c2cfd13c8c8633057aTimo Sirainen i_stream_seekable_set_max_buffer_size;
3c52b294054995d62bd1851c5c42b975fd8c22c9Timo Sirainen
ebe6df72f1309135f02b6a4d2aef1e81a073f91cTimo Sirainen sstream->istream.read = i_stream_seekable_read;
910fa4e4204a73d3d24c03f3059dd24e727ca057Timo Sirainen sstream->istream.stat = i_stream_seekable_stat;
4bbd396aa6198c84f3f7763b6e8a63a26e97e141Timo Sirainen sstream->istream.seek = i_stream_seekable_seek;
910fa4e4204a73d3d24c03f3059dd24e727ca057Timo Sirainen
b3b4f3875850099c9292ad74d08bb385c3988f8fTimo Sirainen sstream->istream.istream.readable_fd = FALSE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen sstream->istream.istream.blocking = blocking;
sstream->istream.istream.seekable = TRUE;
return i_stream_create(&sstream->istream, NULL, -1);
}
static bool inputs_are_seekable(struct istream *input[])
{
unsigned int count;
for (count = 0; input[count] != NULL; count++) {
if (!input[count]->seekable)
return FALSE;
}
return TRUE;
}
struct istream *
i_stream_create_seekable(struct istream *input[],
size_t max_buffer_size,
int (*fd_callback)(const char **path_r, void *context),
void *context)
{
i_assert(max_buffer_size > 0);
/* If all input streams are seekable, use concat istream instead */
if (inputs_are_seekable(input))
return i_stream_create_concat(input);
return i_streams_merge(input, max_buffer_size, fd_callback, context);
}
static int seekable_fd_callback(const char **path_r, void *context)
{
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("istream-seekable: safe_mkstemp(%s) failed: %m", str_c(path));
return -1;
}
/* we just want the fd, unlink it */
if (i_unlink(str_c(path)) < 0) {
/* shouldn't happen.. */
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;
i_assert(temp_path_prefix != NULL);
i_assert(max_buffer_size > 0);
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;
}