istream-tee.c revision 74f810327aca91b3375d3fc963bce8076785b1cb
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch/* Copyright (c) 2006-2007 Dovecot authors, see the included COPYING file */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch#include "lib.h"
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch#include "istream-internal.h"
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch#include "istream-tee.h"
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstruct tee_istream {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch struct istream *input;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch struct tee_child_istream *children;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch uoff_t max_read_offset;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch};
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstruct tee_child_istream {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch struct istream_private istream;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch struct tee_istream *tee;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch struct tee_child_istream *next;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch};
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstatic void tee_streams_update_buffer(struct tee_istream *tee)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch struct tee_child_istream *tstream = tee->children;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch const unsigned char *data;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch size_t size, old_used;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch data = i_stream_get_data(tee->input, &size);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch for (; tstream != NULL; tstream = tstream->next) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (tstream->istream.istream.closed) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tstream->istream.skip = tstream->istream.pos = 0;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch continue;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch old_used = tstream->istream.pos - tstream->istream.skip;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tstream->istream.buffer = data;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tstream->istream.skip = tstream->istream.istream.v_offset -
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tee->input->v_offset;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch i_assert(tstream->istream.skip + old_used <= size);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tstream->istream.pos = tstream->istream.skip + old_used;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstatic void tee_streams_skip(struct tee_istream *tee)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch struct tee_child_istream *tstream = tee->children;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch size_t min_skip;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch min_skip = (size_t)-1;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch for (; tstream != NULL; tstream = tstream->next) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (tstream->istream.skip < min_skip &&
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch !tstream->istream.istream.closed)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch min_skip = tstream->istream.skip;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (min_skip > 0 && min_skip != (size_t)-1) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch i_stream_skip(tee->input, min_skip);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tee_streams_update_buffer(tee);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstatic void i_stream_tee_close(struct iostream_private *stream)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch struct tee_child_istream *tstream = (struct tee_child_istream *)stream;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tee_streams_skip(tstream->tee);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstatic void i_stream_tee_destroy(struct iostream_private *stream)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch struct tee_child_istream *tstream = (struct tee_child_istream *)stream;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch struct tee_istream *tee = tstream->tee;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch struct tee_child_istream **p;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (tstream->istream.istream.v_offset > tee->max_read_offset)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tee->max_read_offset = tstream->istream.istream.v_offset;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch for (p = &tee->children; *p != NULL; p = &(*p)->next) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (*p == tstream) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch *p = tstream->next;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch break;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (tee->children == NULL) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch i_assert(tee->input->v_offset <= tee->max_read_offset);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch i_stream_skip(tee->input,
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tee->max_read_offset - tee->input->v_offset);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch i_stream_unref(&tee->input);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch i_free(tee);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch } else {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tee_streams_skip(tstream->tee);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstatic void
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschi_stream_tee_set_max_buffer_size(struct iostream_private *stream,
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch size_t max_size)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch struct tee_child_istream *tstream = (struct tee_child_istream *)stream;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch i_stream_set_max_buffer_size(tstream->tee->input, max_size);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstatic ssize_t i_stream_tee_read(struct istream_private *stream)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch struct tee_child_istream *tstream = (struct tee_child_istream *)stream;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch struct istream *input = tstream->tee->input;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch const unsigned char *data;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch size_t size;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch uoff_t last_high_offset;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch ssize_t ret;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch data = i_stream_get_data(input, &size);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch last_high_offset = stream->istream.v_offset +
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch (tstream->istream.pos - tstream->istream.skip);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch i_assert(last_high_offset <= input->v_offset + size);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (last_high_offset == input->v_offset + size) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tee_streams_skip(tstream->tee);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch ret = i_stream_read(input);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (ret <= 0) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch data = i_stream_get_data(input, &size);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (ret == -2 && stream->skip != 0) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch /* someone else is holding the data,
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch wait for it */
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return 0;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch stream->istream.eof = input->eof;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return ret;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tee_streams_update_buffer(tstream->tee);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch data = i_stream_get_data(input, &size);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch } else if (stream->buffer == NULL) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tee_streams_update_buffer(tstream->tee);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch } else {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch stream->buffer = data;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch i_assert(stream->buffer == data);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch ret = size - stream->pos;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch stream->pos = size;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return ret;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstatic void ATTR_NORETURN
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschi_stream_tee_seek(struct istream_private *stream ATTR_UNUSED,
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch uoff_t v_offset ATTR_UNUSED, bool mark ATTR_UNUSED)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch i_panic("tee-istream: seeking unsupported currently");
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstatic const struct stat *
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschi_stream_tee_stat(struct istream_private *stream, bool exact)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch struct tee_child_istream *tstream = (struct tee_child_istream *)stream;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return i_stream_stat(tstream->tee->input, exact);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstatic void i_stream_tee_sync(struct istream_private *stream)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch struct tee_child_istream *tstream = (struct tee_child_istream *)stream;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch size_t size;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tee_streams_skip(tstream->tee);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch (void)i_stream_get_data(tstream->tee->input, &size);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch if (size != 0) {
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch i_panic("tee-istream: i_stream_sync() called "
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch "with data still buffered");
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch }
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch i_stream_sync(tstream->tee->input);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstruct tee_istream *tee_i_stream_create(struct istream *input)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch struct tee_istream *tee;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tee = i_new(struct tee_istream, 1);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tee->input = input;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch i_stream_ref(input);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return tee;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Boschstruct istream *tee_i_stream_create_child(struct tee_istream *tee)
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch{
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch struct tee_child_istream *tstream;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tstream = i_new(struct tee_child_istream, 1);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tstream->tee = tee;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tstream->istream.iostream.close = i_stream_tee_close;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tstream->istream.iostream.destroy = i_stream_tee_destroy;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tstream->istream.iostream.set_max_buffer_size =
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch i_stream_tee_set_max_buffer_size;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tstream->istream.read = i_stream_tee_read;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tstream->istream.seek = i_stream_tee_seek;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tstream->istream.stat = i_stream_tee_stat;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tstream->istream.sync = i_stream_tee_sync;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tstream->next = tee->children;
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch tee->children = tstream;
639bb36b12b9f9bb54c8bb1be50eac623622f8a0Timo Sirainen
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch return i_stream_create(&tstream->istream,
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch i_stream_get_fd(tee->input), 0);
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch}
8fe8f97e688779add9cd042a9db4ddb7b117cce2Stephan Bosch