istream-concat.c revision a91f2c465f026ca4ebb9e6c8e92800175c0dece6
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2007-2012 Dovecot authors, see the included COPYING file */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen#include "buffer.h"
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen#include "istream-private.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "istream-concat.h"
5666a3d6a7ea89362b8d9e8b39b15424cd9d6388Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenstruct concat_istream {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen struct istream_private istream;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen struct istream **input, *cur_input;
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen uoff_t *input_size;
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen
3ed2d0f6b5e67e2663d44489d9da3176823789a8Timo Sirainen unsigned int cur_idx, unknown_size_idx;
65f8fb656051f1059f7b5a2da9c5555adcc30439Timo Sirainen size_t prev_stream_left, prev_skip;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen};
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenstatic void i_stream_concat_close(struct iostream_private *stream)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen{
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen struct concat_istream *cstream = (struct concat_istream *)stream;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen unsigned int i;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen for (i = 0; cstream->input[i] != NULL; i++)
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen i_stream_close(cstream->input[i]);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainenstatic void i_stream_concat_destroy(struct iostream_private *stream)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen{
2dd39e478269d6fb0bb26d12b394aa30ee965e38Timo Sirainen struct concat_istream *cstream = (struct concat_istream *)stream;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int i;
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen for (i = 0; cstream->input[i] != NULL; i++)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_stream_unref(&cstream->input[i]);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen i_free(cstream->input);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen i_free(cstream->input_size);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen i_free(cstream->istream.w_buffer);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainenstatic void
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Siraineni_stream_concat_set_max_buffer_size(struct iostream_private *stream,
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen size_t max_size)
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen{
3e564425db51f3921ce4de11859777135fdedd15Timo Sirainen struct concat_istream *cstream = (struct concat_istream *)stream;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen unsigned int i;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen cstream->istream.max_buffer_size = max_size;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen for (i = 0; cstream->input[i] != NULL; i++)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen i_stream_set_max_buffer_size(cstream->input[i], max_size);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen}
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainenstatic void i_stream_concat_read_next(struct concat_istream *cstream)
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen{
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen const unsigned char *data;
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen size_t data_size, size;
e5fd6dfd0a492e4708d4dbb7971d7fc5d7b8fd85Timo Sirainen
e5fd6dfd0a492e4708d4dbb7971d7fc5d7b8fd85Timo Sirainen i_assert(cstream->cur_input->eof);
4ba9a1d3facc515b3feb5238a16bcf91f76fac61Timo Sirainen
4ba9a1d3facc515b3feb5238a16bcf91f76fac61Timo Sirainen data = i_stream_get_data(cstream->cur_input, &data_size);
dfaefeabae939803ceb8c503101e86b5496541d1Timo Sirainen cstream->cur_idx++;
dfaefeabae939803ceb8c503101e86b5496541d1Timo Sirainen cstream->cur_input = cstream->input[cstream->cur_idx];
dfaefeabae939803ceb8c503101e86b5496541d1Timo Sirainen i_stream_seek(cstream->cur_input, 0);
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen if (cstream->prev_stream_left > 0 || cstream->istream.pos == 0) {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen cstream->prev_stream_left += data_size;
57a8c6a95e4bce3eeaba36985adb81c07dd683ffTimo Sirainen i_assert(cstream->prev_stream_left ==
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen cstream->istream.pos - cstream->istream.skip);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return;
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* we already verified that the data size is less than the
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen maximum buffer size */
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen cstream->istream.pos = 0;
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainen if (data_size > 0) {
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainen if (!i_stream_try_alloc(&cstream->istream, data_size, &size))
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen i_unreached();
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainen i_assert(size >= data_size);
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainen }
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainen
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen cstream->prev_stream_left = data_size;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen memcpy(cstream->istream.w_buffer, data, data_size);
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen cstream->istream.skip = 0;
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen cstream->istream.pos = data_size;
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen}
f1743785713e7632459d623d5df2108f4b93accbTimo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenstatic ssize_t i_stream_concat_read(struct istream_private *stream)
70ead6466f9baa8294e71fc2fba0a4f54f488b5eTimo Sirainen{
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct concat_istream *cstream = (struct concat_istream *)stream;
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen const unsigned char *data;
ccc895c0358108d2304239063e940b7d75f364abTimo Sirainen size_t size, data_size, cur_data_pos, new_pos, bytes_skipped;
8d630c15a8ed6f85553467c3a231a273defca5f6Timo Sirainen size_t new_bytes_count;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ssize_t ret;
ee116df08d0fdab703483e18fe8076b2ef9fd9d7Timo Sirainen bool last_stream;
c5ab90cfad9cc3e33bcb1baeb30ffc82a7b7053aTimo Sirainen
c5ab90cfad9cc3e33bcb1baeb30ffc82a7b7053aTimo Sirainen if (cstream->cur_input == NULL) {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen stream->istream.stream_errno = EINVAL;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_assert(stream->skip >= cstream->prev_skip);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen bytes_skipped = stream->skip - cstream->prev_skip;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (cstream->prev_stream_left == 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* no need to worry about buffers, skip everything */
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen i_assert(cstream->prev_skip == 0);
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen } else if (bytes_skipped < cstream->prev_stream_left) {
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen /* we're still skipping inside buffer */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen cstream->prev_stream_left -= bytes_skipped;
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen bytes_skipped = 0;
c29216637957d4b3126c6929ac5ba98138256ce1Timo Sirainen } else {
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen /* done with the buffer */
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen bytes_skipped -= cstream->prev_stream_left;
e03d986a74128f5ba30fcfda9f6e36578f5d8decTimo Sirainen cstream->prev_stream_left = 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen i_stream_skip(cstream->cur_input, bytes_skipped);
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen stream->pos -= bytes_skipped;
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen stream->skip -= bytes_skipped;
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen i_assert(stream->pos >= stream->skip + cstream->prev_stream_left);
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen cur_data_pos = stream->pos - (stream->skip + cstream->prev_stream_left);
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen
d3280fe317a4598c0868cc440e7a1191c06d0db3Timo Sirainen data = i_stream_get_data(cstream->cur_input, &data_size);
d3280fe317a4598c0868cc440e7a1191c06d0db3Timo Sirainen if (data_size <= cur_data_pos) {
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen /* need to read more */
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen i_assert(cur_data_pos == data_size);
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen ret = i_stream_read(cstream->cur_input);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (ret == -2 || ret == 0)
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen return ret;
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen if (ret == -1 && cstream->cur_input->stream_errno != 0) {
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen stream->istream.stream_errno =
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen cstream->cur_input->stream_errno;
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen return -1;
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen }
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen /* we either read something or we're at EOF */
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen last_stream = cstream->input[cstream->cur_idx+1] == NULL;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen if (ret == -1 && !last_stream) {
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen if (stream->pos >= stream->max_buffer_size)
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen return -2;
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen i_stream_concat_read_next(cstream);
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen cstream->prev_skip = stream->skip;
2584e86cc2d8c31ba30a4109cf4ba09d1e37e28aTimo Sirainen return i_stream_concat_read(stream);
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen }
4b41116563110d00330896a568eff1078c382827Timo Sirainen
4b41116563110d00330896a568eff1078c382827Timo Sirainen stream->istream.eof = cstream->cur_input->eof && last_stream;
4b41116563110d00330896a568eff1078c382827Timo Sirainen i_assert(ret != -1 || stream->istream.eof);
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen data = i_stream_get_data(cstream->cur_input, &data_size);
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen }
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen if (cstream->prev_stream_left == 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* we can point directly to the current stream's buffers */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen stream->buffer = data;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen stream->pos -= stream->skip;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen stream->skip = 0;
b2c1349cf07410aefab0f5b17153af9e5cfcf48fTimo Sirainen new_pos = data_size;
96308127e006bb3b1108093bcf4cc1fd9481cb7aTimo Sirainen } else if (data_size == cur_data_pos) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* nothing new read */
dbe64f3893616a4005c8946be75d2dc8f6823d72Timo Sirainen i_assert(ret == 0 || ret == -1);
8a13b020f90e080570658b18c042e7e352c8b14fTimo Sirainen stream->buffer = stream->w_buffer;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen new_pos = stream->pos;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen } else {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen /* we still have some of the previous stream left. merge the
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen new data with it. */
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen i_assert(data_size > cur_data_pos);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen new_bytes_count = data_size - cur_data_pos;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (!i_stream_try_alloc(stream, new_bytes_count, &size)) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen stream->buffer = stream->w_buffer;
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen return -2;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen stream->buffer = stream->w_buffer;
f3bb2fbe87425dc89a839908985af496f7f65702Timo Sirainen
f3bb2fbe87425dc89a839908985af496f7f65702Timo Sirainen if (new_bytes_count > size)
a3ee5ce6ecc8e228ee69300fdd562d7ac8be89a7Timo Sirainen new_bytes_count = size;
bd1b2615928a1e8be190cb0405754f0aec8cac2fTimo Sirainen memcpy(stream->w_buffer + stream->pos,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen data + cur_data_pos, new_bytes_count);
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen new_pos = stream->pos + new_bytes_count;
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen }
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen ret = new_pos > stream->pos ? (ssize_t)(new_pos - stream->pos) :
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen (ret == 0 ? 0 : -1);
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen stream->pos = new_pos;
bd4d0a1a7c0626452b8d82f37e3ec07267ac9896Timo Sirainen cstream->prev_skip = stream->skip;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return ret;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic unsigned int
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenfind_v_offset(struct concat_istream *cstream, uoff_t *v_offset)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen{
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen const struct stat *st;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen unsigned int i;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen for (i = 0; cstream->input[i] != NULL; i++) {
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen if (*v_offset == 0) {
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen /* seek to beginning of this stream */
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen break;
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen }
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen if (i == cstream->unknown_size_idx) {
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen /* we'll need to figure out this stream's size */
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen st = i_stream_stat(cstream->input[i], TRUE);
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen if (st == NULL) {
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen i_error("istream-concat: "
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen "Failed to get size of stream %s",
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen i_stream_get_name(cstream->input[i]));
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen cstream->istream.istream.stream_errno =
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen cstream->input[i]->stream_errno;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return (unsigned int)-1;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen }
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen /* @UNSAFE */
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen cstream->input_size[i] = st->st_size;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen cstream->unknown_size_idx = i + 1;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen }
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen if (*v_offset < cstream->input_size[i])
ca98d6a1bbe73499da758a36bfab2963375c8d06Timo Sirainen break;
ca98d6a1bbe73499da758a36bfab2963375c8d06Timo Sirainen *v_offset -= cstream->input_size[i];
ca98d6a1bbe73499da758a36bfab2963375c8d06Timo Sirainen }
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen return i;
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen}
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen
ca98d6a1bbe73499da758a36bfab2963375c8d06Timo Sirainenstatic void i_stream_concat_seek(struct istream_private *stream,
ca98d6a1bbe73499da758a36bfab2963375c8d06Timo Sirainen uoff_t v_offset, bool mark ATTR_UNUSED)
ca98d6a1bbe73499da758a36bfab2963375c8d06Timo Sirainen{
ca98d6a1bbe73499da758a36bfab2963375c8d06Timo Sirainen struct concat_istream *cstream = (struct concat_istream *)stream;
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen stream->istream.v_offset = v_offset;
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen stream->skip = stream->pos = 0;
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen cstream->prev_stream_left = 0;
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen cstream->prev_skip = 0;
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen cstream->cur_idx = find_v_offset(cstream, &v_offset);
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen if (cstream->cur_idx == (unsigned int)-1) {
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen /* failed */
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen cstream->cur_input = NULL;
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen stream->istream.stream_errno = EINVAL;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen return;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen }
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen cstream->cur_input = cstream->input[cstream->cur_idx];
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (cstream->cur_input != NULL)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen i_stream_seek(cstream->cur_input, v_offset);
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen}
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainenstatic const struct stat *
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Siraineni_stream_concat_stat(struct istream_private *stream, bool exact ATTR_UNUSED)
4bbd396aa6198c84f3f7763b6e8a63a26e97e141Timo Sirainen{
4bbd396aa6198c84f3f7763b6e8a63a26e97e141Timo Sirainen struct concat_istream *cstream = (struct concat_istream *)stream;
7baab0b0b60df7ce9093d0881cd322dff1e79491Timo Sirainen uoff_t v_offset = (uoff_t)-1;
7baab0b0b60df7ce9093d0881cd322dff1e79491Timo Sirainen unsigned int i;
3f91e60401495a4046c73992fabaa5e77200a451Timo Sirainen
3f91e60401495a4046c73992fabaa5e77200a451Timo Sirainen /* make sure we have all sizes */
6b0d8106ae51ffc6ce45636b34d2e21cbefca7fdTimo Sirainen (void)find_v_offset(cstream, &v_offset);
6b0d8106ae51ffc6ce45636b34d2e21cbefca7fdTimo Sirainen
eb64c3586d854cddd693f0b811d897399076a441Timo Sirainen stream->statbuf.st_size = 0;
eb64c3586d854cddd693f0b811d897399076a441Timo Sirainen for (i = 0; i < cstream->unknown_size_idx; i++)
eb64c3586d854cddd693f0b811d897399076a441Timo Sirainen stream->statbuf.st_size += cstream->input_size[i];
eb64c3586d854cddd693f0b811d897399076a441Timo Sirainen return &stream->statbuf;
7f735cb86b2d8abd8f230089065eacfc24e9e5d6Timo Sirainen}
7f735cb86b2d8abd8f230089065eacfc24e9e5d6Timo Sirainen
7f735cb86b2d8abd8f230089065eacfc24e9e5d6Timo Sirainenstruct istream *i_stream_create_concat(struct istream *input[])
7f735cb86b2d8abd8f230089065eacfc24e9e5d6Timo Sirainen{
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen struct concat_istream *cstream;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen unsigned int count;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen size_t max_buffer_size = I_STREAM_MIN_SIZE;
0d86aa0d47f7393c669c084b34c0537b193688adTimo Sirainen bool blocking = TRUE, seekable = TRUE;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen /* if any of the streams isn't blocking or seekable, set ourself also
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen nonblocking/nonseekable */
aa247243412a49f9bdebf7255e131dc6ece4ed46Timo Sirainen for (count = 0; input[count] != NULL; count++) {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen size_t cur_max = input[count]->real_stream->max_buffer_size;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (cur_max > max_buffer_size)
a757f31393b9d6fc7760a9dec8363404ab3ae576Timo Sirainen max_buffer_size = cur_max;
a757f31393b9d6fc7760a9dec8363404ab3ae576Timo Sirainen if (!input[count]->blocking)
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen blocking = FALSE;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (!input[count]->seekable)
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen seekable = FALSE;
c0225f7f6b43d34dc58c17d3304f0fd60ab89894Timo Sirainen i_stream_ref(input[count]);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen }
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen i_assert(count != 0);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen cstream = i_new(struct concat_istream, 1);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen cstream->input = i_new(struct istream *, count + 1);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen cstream->input_size = i_new(uoff_t, count + 1);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen memcpy(cstream->input, input, sizeof(*input) * count);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen cstream->cur_input = cstream->input[0];
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen i_stream_seek(cstream->cur_input, 0);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen cstream->istream.iostream.close = i_stream_concat_close;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen cstream->istream.iostream.destroy = i_stream_concat_destroy;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen cstream->istream.iostream.set_max_buffer_size =
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen i_stream_concat_set_max_buffer_size;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen cstream->istream.max_buffer_size = max_buffer_size;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen cstream->istream.read = i_stream_concat_read;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen cstream->istream.seek = i_stream_concat_seek;
9af6cc9ebc9986c1275ebdfa29c39e152af1557eTimo Sirainen cstream->istream.stat = i_stream_concat_stat;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen cstream->istream.istream.readable_fd = FALSE;
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen cstream->istream.istream.blocking = blocking;
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen cstream->istream.istream.seekable = seekable;
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen return i_stream_create(&cstream->istream, NULL, -1);
ad48319996942463675b53877092ab7e13a7a75aTimo Sirainen}
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen