istream-seekable.c revision 3e55775bc7a37ebc05e06c04cafb32eee9888e87
45312f52ff3a3d4c137447be4c7556500c2f8bf2Timo Sirainen/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen int (*fd_callback)(const char **path_r, void *context);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen unsigned int cur_idx;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainenstatic void i_stream_seekable_close(struct iostream_private *stream,
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainenstatic void unref_streams(struct seekable_istream *sstream)
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen unsigned int i;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainenstatic void i_stream_seekable_destroy(struct iostream_private *stream)
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
beffc30d933c5e134c45cc871852a8427eba7e70Timo Siraineni_stream_seekable_set_max_buffer_size(struct iostream_private *stream,
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen unsigned int i;
4ce6338bf945cccfff9e4ce7cc6aa2246851b84aTimo Sirainen i_stream_set_max_buffer_size(sstream->fd_input, max_size);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen i_stream_set_max_buffer_size(sstream->input[i], max_size);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainenstatic int copy_to_temp_file(struct seekable_istream *sstream)
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen struct istream_private *stream = &sstream->istream;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen const unsigned char *buffer;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen fd = sstream->fd_callback(&path, sstream->context);
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen /* copy our currently read buffer to it */
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen i_assert(stream->pos <= sstream->buffer_peak);
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen if (write_full(fd, stream->buffer, sstream->buffer_peak) < 0) {
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen i_error("istream-seekable: write_full(%s) failed: %m", path);
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen sstream->fd_input = i_stream_create_fd_autoclose(&fd,
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen I_MAX(stream->pos, sstream->istream.max_buffer_size));
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen i_stream_set_name(sstream->fd_input, t_strdup_printf(
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen "(seekable temp-istream for: %s)", i_stream_get_name(&stream->istream)));
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen /* read back the data we just had in our buffer */
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen buffer = i_stream_get_data(sstream->fd_input, &size);
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen if ((ret = i_stream_read(sstream->fd_input)) <= 0) {
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen i_error("istream-seekable: Couldn't read back "
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen "in-memory input %s: %s",
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen /* Set the max buffer size only after we've already read everything
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen into memory. For example with istream-data it's possible that
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen more data exists in buffer than max_buffer_size. */
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen i_stream_set_max_buffer_size(sstream->fd_input,
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainenstatic ssize_t read_more(struct seekable_istream *sstream)
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen while ((ret = i_stream_read(sstream->cur_input)) == -1) {
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen io_stream_set_error(&sstream->istream.iostream,
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen "read(%s) failed: %s",
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen /* go to next stream */
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen sstream->cur_input = sstream->input[sstream->cur_idx++];
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen /* last one, EOF */
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen sstream->size = sstream->istream.istream.v_offset;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen /* see if stream has pending data */
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen size = i_stream_get_data_size(sstream->cur_input);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainenstatic bool read_from_buffer(struct seekable_istream *sstream, ssize_t *ret_r)
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen struct istream_private *stream = &sstream->istream;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen const unsigned char *data;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen /* This could be the first read() or we could have already
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen seeked backwards. */
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen i_assert(stream->pos == 0 && stream->skip == 0);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen /* need to read more */
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen i_assert(stream->pos == sstream->buffer_peak);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen /* read more to buffer */
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen /* we should have more now. */
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen data = i_stream_get_data(sstream->cur_input, &size);
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen /* change skip to 0 temporarily so i_stream_try_alloc() won't try to
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen compress the buffer. */
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen bool have_space = i_stream_try_alloc(stream, size, &avail_size);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen memcpy(stream->w_buffer + stream->pos, data, size);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainenstatic int i_stream_seekable_write_failed(struct seekable_istream *sstream)
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen struct istream_private *stream = &sstream->istream;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen data = i_stream_alloc(stream, sstream->write_peak);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen if (pread_full(sstream->fd, data, sstream->write_peak, 0) < 0) {
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen i_error("istream-seekable: read(%s) failed: %m", sstream->temp_path);
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainenstatic ssize_t i_stream_seekable_read(struct istream_private *stream)
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen const unsigned char *data;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen /* copy everything to temp file and use it as the stream */
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen stream->buffer = CONST_PTR_OFFSET(stream->buffer, stream->skip);
da2aa032ccfa8e7e4a4380ef738014549f4d2c2dTimo Sirainen i_assert(stream->istream.v_offset + stream->pos <= sstream->write_peak);
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen if (stream->istream.v_offset + stream->pos == sstream->write_peak) {
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen /* need to read more */
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen i_stream_get_data_size(sstream->cur_input) == 0) {
return ret;
if (ret <= 0) {
i_unreached();
return ret;
if (ret <= 0) {
return ret;
if (ret == 0) {
struct istream *
const unsigned char *data;
unsigned int count;
if (size > 0) {
unsigned int count;
return FALSE;
return TRUE;
struct istream *
void *context)
int fd;
return fd;
struct istream *
const char *temp_path_prefix)
return stream;