istream-tee.c revision 0a70a2e71658d168f75d1f075661d7936701343c
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2006-2008 Dovecot authors, see the included COPYING file */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "lib.h"
49e358eebea107aad9919dcc4bd88cee8519ba2eTimo Sirainen#include "istream-internal.h"
49e358eebea107aad9919dcc4bd88cee8519ba2eTimo Sirainen#include "istream-tee.h"
49e358eebea107aad9919dcc4bd88cee8519ba2eTimo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainenstruct tee_istream {
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen struct istream *input;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen struct tee_child_istream *children;
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainen
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainen uoff_t max_read_offset;
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainen};
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainen
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainenstruct tee_child_istream {
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainen struct istream_private istream;
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainen
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainen struct tee_istream *tee;
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainen struct tee_child_istream *next;
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainen};
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainenstatic void tee_streams_update_buffer(struct tee_istream *tee)
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen{
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen struct tee_child_istream *tstream = tee->children;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen const unsigned char *data;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen size_t size, old_used;
da2aa032ccfa8e7e4a4380ef738014549f4d2c2dTimo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen data = i_stream_get_data(tee->input, &size);
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen for (; tstream != NULL; tstream = tstream->next) {
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen if (tstream->istream.istream.closed) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen tstream->istream.skip = tstream->istream.pos = 0;
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen continue;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen old_used = tstream->istream.pos - tstream->istream.skip;
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen tstream->istream.buffer = data;
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi tstream->istream.skip = tstream->istream.istream.v_offset -
43834f87bf431198f986e86052a4f6e558fdb07dTimo Sirainen tee->input->v_offset;
43834f87bf431198f986e86052a4f6e558fdb07dTimo Sirainen i_assert(tstream->istream.skip + old_used <= size);
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen tstream->istream.pos = tstream->istream.skip + old_used;
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen }
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen}
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainenstatic void tee_streams_skip(struct tee_istream *tee)
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen{
a9a928e40e3b691924c8e5e444e3e1a4320aa3bdStephan Bosch struct tee_child_istream *tstream = tee->children;
a9a928e40e3b691924c8e5e444e3e1a4320aa3bdStephan Bosch size_t min_skip;
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen
212a34c06ff45952c008ae9eec387ced783de6cfPhil Carmody min_skip = (size_t)-1;
212a34c06ff45952c008ae9eec387ced783de6cfPhil Carmody for (; tstream != NULL; tstream = tstream->next) {
212a34c06ff45952c008ae9eec387ced783de6cfPhil Carmody if (tstream->istream.skip < min_skip &&
212a34c06ff45952c008ae9eec387ced783de6cfPhil Carmody !tstream->istream.istream.closed)
a9a928e40e3b691924c8e5e444e3e1a4320aa3bdStephan Bosch min_skip = tstream->istream.skip;
a9a928e40e3b691924c8e5e444e3e1a4320aa3bdStephan Bosch }
a9a928e40e3b691924c8e5e444e3e1a4320aa3bdStephan Bosch
a9a928e40e3b691924c8e5e444e3e1a4320aa3bdStephan Bosch if (min_skip > 0 && min_skip != (size_t)-1) {
a9a928e40e3b691924c8e5e444e3e1a4320aa3bdStephan Bosch i_stream_skip(tee->input, min_skip);
a9a928e40e3b691924c8e5e444e3e1a4320aa3bdStephan Bosch tee_streams_update_buffer(tee);
a9a928e40e3b691924c8e5e444e3e1a4320aa3bdStephan Bosch }
a9a928e40e3b691924c8e5e444e3e1a4320aa3bdStephan Bosch}
a9a928e40e3b691924c8e5e444e3e1a4320aa3bdStephan Bosch
10c96a244935de4add8213ba0b894178dfb889a5Timo Sirainenstatic void i_stream_tee_close(struct iostream_private *stream)
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen{
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen struct tee_child_istream *tstream = (struct tee_child_istream *)stream;
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen tee_streams_skip(tstream->tee);
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen}
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic void i_stream_tee_destroy(struct iostream_private *stream)
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen{
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen struct tee_child_istream *tstream = (struct tee_child_istream *)stream;
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen struct tee_istream *tee = tstream->tee;
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen struct tee_child_istream **p;
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen if (tstream->istream.istream.v_offset > tee->max_read_offset)
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen tee->max_read_offset = tstream->istream.istream.v_offset;
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen for (p = &tee->children; *p != NULL; p = &(*p)->next) {
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen if (*p == tstream) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen *p = tstream->next;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen break;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen }
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen }
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen if (tee->children == NULL) {
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen i_assert(tee->input->v_offset <= tee->max_read_offset);
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen i_stream_skip(tee->input,
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen tee->max_read_offset - tee->input->v_offset);
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen i_stream_unref(&tee->input);
27a44fcfd8d19bffe0f267f20a2b5d3fe7600fddTimo Sirainen i_free(tee);
27a44fcfd8d19bffe0f267f20a2b5d3fe7600fddTimo Sirainen } else {
c28f6aa0b70af4811c9ace9114fe827c2f503455Timo Sirainen tee_streams_skip(tstream->tee);
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen }
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic void
c0435c854a0e7246373b9752d163095cc4fbe985Timo Siraineni_stream_tee_set_max_buffer_size(struct iostream_private *stream,
46ce4d9273e6df12ef1912bbdb1c8b84b104f394Timo Sirainen size_t max_size)
46ce4d9273e6df12ef1912bbdb1c8b84b104f394Timo Sirainen{
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen struct tee_child_istream *tstream = (struct tee_child_istream *)stream;
5af5137f6dc0c9f358b7813e941e26f7bd735b3aTimo Sirainen
5af5137f6dc0c9f358b7813e941e26f7bd735b3aTimo Sirainen tstream->istream.max_buffer_size = max_size;
5af5137f6dc0c9f358b7813e941e26f7bd735b3aTimo Sirainen i_stream_set_max_buffer_size(tstream->tee->input, max_size);
5af5137f6dc0c9f358b7813e941e26f7bd735b3aTimo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainenstatic ssize_t i_stream_tee_read(struct istream_private *stream)
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen{
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen struct tee_child_istream *tstream = (struct tee_child_istream *)stream;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen struct istream *input = tstream->tee->input;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen const unsigned char *data;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen size_t size;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen uoff_t last_high_offset;
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen ssize_t ret;
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen data = i_stream_get_data(input, &size);
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen last_high_offset = stream->istream.v_offset +
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen (tstream->istream.pos - tstream->istream.skip);
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen i_assert(last_high_offset <= input->v_offset + size);
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen if (last_high_offset == input->v_offset + size) {
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen tee_streams_skip(tstream->tee);
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen ret = i_stream_read(input);
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen if (ret <= 0) {
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen data = i_stream_get_data(input, &size);
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen if (ret == -2 && stream->skip != 0) {
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen /* someone else is holding the data,
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen wait for it */
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen return 0;
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen }
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen stream->istream.stream_errno = input->stream_errno;
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen stream->istream.eof = input->eof;
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen return ret;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen tee_streams_update_buffer(tstream->tee);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen data = i_stream_get_data(input, &size);
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen } else if (stream->buffer == NULL) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen tee_streams_update_buffer(tstream->tee);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen } else {
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen stream->buffer = data;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainen
87a6b7df39d6822a5a8289a62f32deabff9b75e4Timo Sirainen i_assert(stream->buffer == data);
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen ret = size - stream->pos;
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen stream->pos = size;
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen return ret;
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen}
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainenstatic void ATTR_NORETURN
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Siraineni_stream_tee_seek(struct istream_private *stream ATTR_UNUSED,
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen uoff_t v_offset ATTR_UNUSED, bool mark ATTR_UNUSED)
7d207b1e77a7b5e3fda640e353acfc86d261fedfTimo Sirainen{
7d207b1e77a7b5e3fda640e353acfc86d261fedfTimo Sirainen i_panic("tee-istream: seeking unsupported currently");
7d207b1e77a7b5e3fda640e353acfc86d261fedfTimo Sirainen}
7d207b1e77a7b5e3fda640e353acfc86d261fedfTimo Sirainen
7d207b1e77a7b5e3fda640e353acfc86d261fedfTimo Sirainenstatic const struct stat *
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Siraineni_stream_tee_stat(struct istream_private *stream, bool exact)
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen{
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen struct tee_child_istream *tstream = (struct tee_child_istream *)stream;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen return i_stream_stat(tstream->tee->input, exact);
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen}
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainenstatic void i_stream_tee_sync(struct istream_private *stream)
923115fd382904fa13bb09bf307bf2835b52df60Timo Sirainen{
923115fd382904fa13bb09bf307bf2835b52df60Timo Sirainen struct tee_child_istream *tstream = (struct tee_child_istream *)stream;
923115fd382904fa13bb09bf307bf2835b52df60Timo Sirainen size_t size;
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen tee_streams_skip(tstream->tee);
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen (void)i_stream_get_data(tstream->tee->input, &size);
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen if (size != 0) {
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen i_panic("tee-istream: i_stream_sync() called "
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen "with data still buffered");
a0b6b441fc679e562e79be0fb2819ffc24ab5b74Timo Sirainen }
a0b6b441fc679e562e79be0fb2819ffc24ab5b74Timo Sirainen i_stream_sync(tstream->tee->input);
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen}
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainenstruct tee_istream *tee_i_stream_create(struct istream *input)
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen{
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen struct tee_istream *tee;
6657aee0bb6c603b4ee5111388b93c1a8a9ad680Martti Rannanjärvi
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen tee = i_new(struct tee_istream, 1);
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen tee->input = input;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_stream_ref(input);
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen return tee;
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
699fdc186f982f70d990820796eaa0f12133e27cTimo Sirainenstruct istream *tee_i_stream_create_child(struct tee_istream *tee)
699fdc186f982f70d990820796eaa0f12133e27cTimo Sirainen{
699fdc186f982f70d990820796eaa0f12133e27cTimo Sirainen struct tee_child_istream *tstream;
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen tstream = i_new(struct tee_child_istream, 1);
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody tstream->tee = tee;
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody tstream->istream.max_buffer_size =
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody tee->input->real_stream->max_buffer_size;
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody tstream->istream.iostream.close = i_stream_tee_close;
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody tstream->istream.iostream.destroy = i_stream_tee_destroy;
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody tstream->istream.iostream.set_max_buffer_size =
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody i_stream_tee_set_max_buffer_size;
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody
573424407a2d3c1453638a643583a7cf10c129e1Phil Carmody tstream->istream.read = i_stream_tee_read;
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody tstream->istream.seek = i_stream_tee_seek;
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody tstream->istream.stat = i_stream_tee_stat;
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody tstream->istream.sync = i_stream_tee_sync;
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody tstream->next = tee->children;
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody tee->children = tstream;
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody
09142ea11662746ea07475b1a4f69a6a406fb996Phil Carmody return i_stream_create(&tstream->istream, NULL,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_stream_get_fd(tee->input));
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen}
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen