istream-tee.c revision 74f810327aca91b3375d3fc963bce8076785b1cb
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2006-2007 Dovecot authors, see the included COPYING file */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen#include "lib.h"
46552a931924c2d743f045e95b08c3ce6beda91aTimo Sirainen#include "istream-internal.h"
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen#include "istream-tee.h"
c5f932968281763df360b9c97cef60f5f80d5e3dTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstruct tee_istream {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct istream *input;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct tee_child_istream *children;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen uoff_t max_read_offset;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen};
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainenstruct tee_child_istream {
f29756821a4c6b12b73e4a2a3e1c230117a43773Timo Sirainen struct istream_private istream;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct tee_istream *tee;
53dfcefa9440a49d703e49193819a79be99c9ba6Timo Sirainen struct tee_child_istream *next;
f0d93763f210ecdb85a115fdd0210a16cfc5ff5cTimo Sirainen};
53dfcefa9440a49d703e49193819a79be99c9ba6Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void tee_streams_update_buffer(struct tee_istream *tee)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct tee_child_istream *tstream = tee->children;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen const unsigned char *data;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size_t size, old_used;
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch data = i_stream_get_data(tee->input, &size);
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch for (; tstream != NULL; tstream = tstream->next) {
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch if (tstream->istream.istream.closed) {
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch tstream->istream.skip = tstream->istream.pos = 0;
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch continue;
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch }
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch old_used = tstream->istream.pos - tstream->istream.skip;
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch tstream->istream.buffer = data;
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch tstream->istream.skip = tstream->istream.istream.v_offset -
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch tee->input->v_offset;
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch i_assert(tstream->istream.skip + old_used <= size);
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch tstream->istream.pos = tstream->istream.skip + old_used;
8ac6623677005256bf99ab33a2ed98c69c1d656cStephan Bosch }
8ac6623677005256bf99ab33a2ed98c69c1d656cStephan Bosch}
04eb0abcf8f8b0c014499b5c5bae89484553613fStephan Bosch
04eb0abcf8f8b0c014499b5c5bae89484553613fStephan Boschstatic void tee_streams_skip(struct tee_istream *tee)
04eb0abcf8f8b0c014499b5c5bae89484553613fStephan Bosch{
04eb0abcf8f8b0c014499b5c5bae89484553613fStephan Bosch struct tee_child_istream *tstream = tee->children;
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch size_t min_skip;
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch min_skip = (size_t)-1;
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch for (; tstream != NULL; tstream = tstream->next) {
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch if (tstream->istream.skip < min_skip &&
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch !tstream->istream.istream.closed)
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch min_skip = tstream->istream.skip;
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch }
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch if (min_skip > 0 && min_skip != (size_t)-1) {
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch i_stream_skip(tee->input, min_skip);
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch tee_streams_update_buffer(tee);
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch }
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch}
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Boschstatic void i_stream_tee_close(struct iostream_private *stream)
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch{
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen struct tee_child_istream *tstream = (struct tee_child_istream *)stream;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen tee_streams_skip(tstream->tee);
2e78f05b11df23ec2731afaf8f19d5b5240cb29fTimo Sirainen}
2e78f05b11df23ec2731afaf8f19d5b5240cb29fTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void i_stream_tee_destroy(struct iostream_private *stream)
d1e7425048c61d71f41f737ba947687198842dc2Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct tee_child_istream *tstream = (struct tee_child_istream *)stream;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct tee_istream *tee = tstream->tee;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct tee_child_istream **p;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (tstream->istream.istream.v_offset > tee->max_read_offset)
660b99a7059824676b2b8d6f79b8e15d47df25a2Timo Sirainen tee->max_read_offset = tstream->istream.istream.v_offset;
660b99a7059824676b2b8d6f79b8e15d47df25a2Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen for (p = &tee->children; *p != NULL; p = &(*p)->next) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (*p == tstream) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen *p = tstream->next;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen break;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen }
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen }
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen if (tee->children == NULL) {
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen i_assert(tee->input->v_offset <= tee->max_read_offset);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen i_stream_skip(tee->input,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen tee->max_read_offset - tee->input->v_offset);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_stream_unref(&tee->input);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_free(tee);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen tee_streams_skip(tstream->tee);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen}
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainenstatic void
e248fe370c4047cee921a91b48edc37944ab0526Timo Siraineni_stream_tee_set_max_buffer_size(struct iostream_private *stream,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size_t max_size)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen struct tee_child_istream *tstream = (struct tee_child_istream *)stream;
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen i_stream_set_max_buffer_size(tstream->tee->input, max_size);
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen}
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainenstatic ssize_t i_stream_tee_read(struct istream_private *stream)
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen{
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen struct tee_child_istream *tstream = (struct tee_child_istream *)stream;
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen struct istream *input = tstream->tee->input;
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen const unsigned char *data;
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen size_t size;
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen uoff_t last_high_offset;
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen ssize_t ret;
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen data = i_stream_get_data(input, &size);
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen last_high_offset = stream->istream.v_offset +
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen (tstream->istream.pos - tstream->istream.skip);
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen i_assert(last_high_offset <= input->v_offset + size);
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen if (last_high_offset == input->v_offset + size) {
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen tee_streams_skip(tstream->tee);
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen ret = i_stream_read(input);
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen if (ret <= 0) {
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen data = i_stream_get_data(input, &size);
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen if (ret == -2 && stream->skip != 0) {
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen /* someone else is holding the data,
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen wait for it */
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen return 0;
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen }
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen stream->istream.eof = input->eof;
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen return ret;
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen tee_streams_update_buffer(tstream->tee);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen data = i_stream_get_data(input, &size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else if (stream->buffer == NULL) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen tee_streams_update_buffer(tstream->tee);
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen } else {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen stream->buffer = data;
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_assert(stream->buffer == data);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = size - stream->pos;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen stream->pos = size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void ATTR_NORETURN
e248fe370c4047cee921a91b48edc37944ab0526Timo Siraineni_stream_tee_seek(struct istream_private *stream ATTR_UNUSED,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen uoff_t v_offset ATTR_UNUSED, bool mark ATTR_UNUSED)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch i_panic("tee-istream: seeking unsupported currently");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
f32d0295c90ed810889504cdfa5e1a25a415f65fStephan Bosch
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Boschstatic const struct stat *
e248fe370c4047cee921a91b48edc37944ab0526Timo Siraineni_stream_tee_stat(struct istream_private *stream, bool exact)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct tee_child_istream *tstream = (struct tee_child_istream *)stream;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return i_stream_stat(tstream->tee->input, exact);
e7dd5065d21c569e5e6ddd713bd345dadd1cf51dTimo Sirainen}
e7dd5065d21c569e5e6ddd713bd345dadd1cf51dTimo Sirainen
d694a52bce62c52080c3f87a56dcf77030fd2712Timo Sirainenstatic void i_stream_tee_sync(struct istream_private *stream)
d694a52bce62c52080c3f87a56dcf77030fd2712Timo Sirainen{
009217abb57a24a4076092e8e4e165545747839eStephan Bosch struct tee_child_istream *tstream = (struct tee_child_istream *)stream;
009217abb57a24a4076092e8e4e165545747839eStephan Bosch size_t size;
009217abb57a24a4076092e8e4e165545747839eStephan Bosch
009217abb57a24a4076092e8e4e165545747839eStephan Bosch tee_streams_skip(tstream->tee);
009217abb57a24a4076092e8e4e165545747839eStephan Bosch (void)i_stream_get_data(tstream->tee->input, &size);
009217abb57a24a4076092e8e4e165545747839eStephan Bosch if (size != 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_panic("tee-istream: i_stream_sync() called "
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "with data still buffered");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch i_stream_sync(tstream->tee->input);
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch}
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Boschstruct tee_istream *tee_i_stream_create(struct istream *input)
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch{
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch struct tee_istream *tee;
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch tee = i_new(struct tee_istream, 1);
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch tee->input = input;
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch i_stream_ref(input);
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch return tee;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen}
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainen
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainenstruct istream *tee_i_stream_create_child(struct tee_istream *tee)
6d24551e169c0808695db35d7a228f1970a84c75Timo Sirainen{
6d24551e169c0808695db35d7a228f1970a84c75Timo Sirainen struct tee_child_istream *tstream;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen tstream = i_new(struct tee_child_istream, 1);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen tstream->tee = tee;
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen tstream->istream.iostream.close = i_stream_tee_close;
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen tstream->istream.iostream.destroy = i_stream_tee_destroy;
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen tstream->istream.iostream.set_max_buffer_size =
5a250816ffc4cc5db203f9410ea99b6601c7b91aTimo Sirainen i_stream_tee_set_max_buffer_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen tstream->istream.read = i_stream_tee_read;
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen tstream->istream.seek = i_stream_tee_seek;
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen tstream->istream.stat = i_stream_tee_stat;
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen tstream->istream.sync = i_stream_tee_sync;
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch tstream->next = tee->children;
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch tee->children = tstream;
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch return i_stream_create(&tstream->istream,
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch i_stream_get_fd(tee->input), 0);
8ce3071e80b9973230048ecadfcb073fb82cc69fTimo Sirainen}
8ce3071e80b9973230048ecadfcb073fb82cc69fTimo Sirainen