istream-seekable.c revision c1d4780bc0c9017e8e5d366b81e4fad31174c0ad
5a580c3a38ced62d4bcc95b8ac7c4f2935b5d294Timo Sirainen/* Copyright (c) 2005-2011 Dovecot authors, see the included COPYING file */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen#include "lib.h"
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen#include "buffer.h"
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen#include "close-keep-errno.h"
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen#include "read-full.h"
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen#include "write-full.h"
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch#include "istream-private.h"
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch#include "istream-concat.h"
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch#include "istream-seekable.h"
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen#include <unistd.h>
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen#define BUF_INITIAL_SIZE (1024*32)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenstruct seekable_istream {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen struct istream_private istream;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen char *temp_path;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen uoff_t write_peak;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen int (*fd_callback)(const char **path_r, void *context);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen void *context;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen buffer_t *buffer;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen struct istream **input, *cur_input;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen struct istream *fd_input;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen unsigned int cur_idx;
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen int fd;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen};
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenstatic void i_stream_seekable_close(struct iostream_private *stream)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen{
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen unsigned int i;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen sstream->fd = -1;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen if (sstream->fd_input != NULL)
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen i_stream_close(sstream->fd_input);
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen for (i = 0; sstream->input[i] != NULL; i++)
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen i_stream_close(sstream->input[i]);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen}
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainenstatic void i_stream_seekable_destroy(struct iostream_private *stream)
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen{
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen unsigned int i;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (sstream->buffer != NULL)
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch buffer_free(&sstream->buffer);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch if (sstream->fd_input != NULL)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_stream_unref(&sstream->fd_input);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen for (i = 0; sstream->input[i] != NULL; i++)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_stream_unref(&sstream->input[i]);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_free(sstream->temp_path);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch i_free(sstream->input);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen}
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenstatic void
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Boschi_stream_seekable_set_max_buffer_size(struct iostream_private *stream,
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch size_t max_size)
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch{
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen unsigned int i;
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch sstream->istream.max_buffer_size = max_size;
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch if (sstream->fd_input != NULL)
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch i_stream_set_max_buffer_size(sstream->fd_input, max_size);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch for (i = 0; sstream->input[i] != NULL; i++)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_stream_set_max_buffer_size(sstream->input[i], max_size);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen}
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Boschstatic int copy_to_temp_file(struct seekable_istream *sstream)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen{
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen struct istream_private *stream = &sstream->istream;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen const char *path;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen const unsigned char *buffer;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen size_t size;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen int fd;
df1713bd29d29a3e3f3ebfdf05f929525825a7d3Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen fd = sstream->fd_callback(&path, sstream->context);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (fd == -1)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return -1;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* copy our currently read buffer to it */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (write_full(fd, sstream->buffer->data, sstream->buffer->used) < 0) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (!ENOSPACE(errno))
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_error("write_full(%s) failed: %m", path);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen close_keep_errno(fd);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return -1;
df1713bd29d29a3e3f3ebfdf05f929525825a7d3Timo Sirainen }
df1713bd29d29a3e3f3ebfdf05f929525825a7d3Timo Sirainen sstream->temp_path = i_strdup(path);
df1713bd29d29a3e3f3ebfdf05f929525825a7d3Timo Sirainen sstream->write_peak = sstream->buffer->used;
cd2fc7dd28c3a2e3f82e8480eaf3ba7c4abc3614Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen sstream->fd = fd;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen sstream->fd_input =
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_stream_create_fd(fd, sstream->istream.max_buffer_size, TRUE);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* read back the data we just had in our buffer */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_stream_seek(sstream->fd_input, stream->istream.v_offset);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch for (;;) {
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch buffer = i_stream_get_data(sstream->fd_input, &size);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (size >= stream->pos)
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch break;
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch if (i_stream_read(sstream->fd_input) <= 0) {
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch i_error("istream-seekable: Couldn't read back "
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen "in-memory input %s",
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch i_stream_get_name(&stream->istream));
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch i_stream_destroy(&sstream->fd_input);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch return -1;
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch }
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch }
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen stream->buffer = buffer;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen stream->pos = size;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen buffer_free(&sstream->buffer);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch return 0;
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch}
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Boschstatic ssize_t read_more(struct seekable_istream *sstream)
53ec1ff2231d477db3103c51987fa9cb6033bc16Timo Sirainen{
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen size_t size;
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch ssize_t ret;
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch if (sstream->cur_input == NULL) {
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch sstream->istream.istream.eof = TRUE;
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch return -1;
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch }
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch while ((ret = i_stream_read(sstream->cur_input)) < 0) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (!sstream->cur_input->eof) {
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch /* full / error */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen sstream->istream.istream.stream_errno =
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen sstream->cur_input->stream_errno;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return -1;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* go to next stream */
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch sstream->cur_input = sstream->input[sstream->cur_idx++];
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch if (sstream->cur_input == NULL) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* last one, EOF */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen sstream->istream.istream.eof = TRUE;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return -1;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch
dafbec2c6b4275233a78cb137f41dd8041aa1c46Timo Sirainen /* see if stream has pending data */
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch (void)i_stream_get_data(sstream->cur_input, &size);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch if (size != 0)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return size;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return ret;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen}
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenstatic bool read_from_buffer(struct seekable_istream *sstream, ssize_t *ret_r)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen{
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen struct istream_private *stream = &sstream->istream;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen const unsigned char *data;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen size_t size, pos, offset;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_assert(stream->skip == 0);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (stream->istream.v_offset + stream->pos >= sstream->buffer->used) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* need to read more */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (sstream->buffer->used >= stream->max_buffer_size)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return FALSE;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (sstream->cur_input == NULL)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen size = 0;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen else
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen (void)i_stream_get_data(sstream->cur_input, &size);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (size == 0) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* read more to buffer */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen *ret_r = read_more(sstream);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (*ret_r <= 0)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return TRUE;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* we should have more now. */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen data = i_stream_get_data(sstream->cur_input, &size);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_assert(size > 0);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen buffer_append(sstream->buffer, data, size);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_stream_skip(sstream->cur_input, size);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen offset = stream->istream.v_offset;
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen stream->buffer = CONST_PTR_OFFSET(sstream->buffer->data, offset);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen pos = sstream->buffer->used - offset;
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen *ret_r = pos - stream->pos;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen i_assert(*ret_r > 0);
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen stream->pos = pos;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen return TRUE;
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen}
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenstatic int i_stream_seekable_write_failed(struct seekable_istream *sstream)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen{
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen struct istream_private *stream = &sstream->istream;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen void *data;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_assert(sstream->buffer == NULL);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen sstream->buffer =
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen buffer_create_dynamic(default_pool, sstream->write_peak);
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen data = buffer_append_space_unsafe(sstream->buffer, sstream->write_peak);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen if (pread_full(sstream->fd, data, sstream->write_peak, 0) < 0) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_error("read(%s) failed: %m", sstream->temp_path);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen buffer_free(&sstream->buffer);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return -1;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_stream_destroy(&sstream->fd_input);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen (void)close(sstream->fd);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen sstream->fd = -1;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen stream->max_buffer_size = (size_t)-1;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen i_free_and_null(sstream->temp_path);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen return 0;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen}
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainenstatic ssize_t i_stream_seekable_read(struct istream_private *stream)
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen{
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen const unsigned char *data;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen size_t size, pos;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen ssize_t ret;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen stream->buffer = CONST_PTR_OFFSET(stream->buffer, stream->skip);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen stream->pos -= stream->skip;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen stream->skip = 0;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen if (sstream->buffer != NULL) {
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen if (read_from_buffer(sstream, &ret))
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen return ret;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen /* copy everything to temp file and use it as the stream */
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen if (copy_to_temp_file(sstream) < 0) {
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen stream->max_buffer_size = (size_t)-1;
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen if (!read_from_buffer(sstream, &ret))
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen i_unreached();
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen return ret;
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen }
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen i_assert(sstream->buffer == NULL);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen }
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen i_assert(stream->istream.v_offset + stream->pos <= sstream->write_peak);
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen if (stream->istream.v_offset + stream->pos == sstream->write_peak) {
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen /* need to read more */
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen ret = read_more(sstream);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen if (ret <= 0)
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen return ret;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen /* save to our file */
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen data = i_stream_get_data(sstream->cur_input, &size);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen ret = write(sstream->fd, data, size);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen if (ret <= 0) {
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen if (ret < 0 && !ENOSPACE(errno)) {
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen i_error("write_full(%s) failed: %m",
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen sstream->temp_path);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen }
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen if (i_stream_seekable_write_failed(sstream) < 0)
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen return -1;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen if (!read_from_buffer(sstream, &ret))
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen i_unreached();
86bde2c1838d1ce967fa2b394bb952004a4adcb7Timo Sirainen return ret;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen }
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen i_stream_sync(sstream->fd_input);
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen i_stream_skip(sstream->cur_input, ret);
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen sstream->write_peak += ret;
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen }
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen i_stream_seek(sstream->fd_input, stream->istream.v_offset);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen ret = i_stream_read(sstream->fd_input);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (ret <= 0) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen stream->istream.eof = sstream->fd_input->eof;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen stream->istream.stream_errno =
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen sstream->fd_input->stream_errno;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen } else {
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen ret = -2;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen }
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen stream->buffer = i_stream_get_data(sstream->fd_input, &pos);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen stream->pos -= stream->skip;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen stream->skip = 0;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen ret = pos > stream->pos ? (ssize_t)(pos - stream->pos) : ret;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen stream->pos = pos;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen return ret;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen}
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainenstatic void i_stream_seekable_seek(struct istream_private *stream,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen uoff_t v_offset, bool mark ATTR_UNUSED)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen{
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen stream->istream.v_offset = v_offset;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen stream->skip = stream->pos = 0;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen}
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainenstatic const struct stat *
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Siraineni_stream_seekable_stat(struct istream_private *stream, bool exact)
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen{
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen uoff_t old_offset;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen ssize_t ret;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen if (sstream->buffer != NULL) {
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen /* we want to know the full size of the file, so read until
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen we're finished */
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen old_offset = stream->istream.v_offset;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen do {
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen i_stream_skip(&stream->istream,
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen stream->pos - stream->skip);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen } while ((ret = i_stream_seekable_read(stream)) > 0);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen if (ret == 0) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_panic("i_stream_stat() used for non-blocking "
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen "seekable stream");
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
acc4e0a41f1c8ef0559a19c280afc1b97b9e0818Timo Sirainen i_stream_skip(&stream->istream, stream->pos - stream->skip);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_stream_seek(&stream->istream, old_offset);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (sstream->fd_input != NULL) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* using a file backed buffer, we can use real fstat() */
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen return i_stream_stat(sstream->fd_input, exact);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen } else {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* buffer is completely in memory */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_assert(sstream->buffer != NULL);
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen stream->statbuf.st_size = sstream->buffer->used;
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen return &stream->statbuf;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen}
acc4e0a41f1c8ef0559a19c280afc1b97b9e0818Timo Sirainen
acc4e0a41f1c8ef0559a19c280afc1b97b9e0818Timo Sirainenstruct istream *
acc4e0a41f1c8ef0559a19c280afc1b97b9e0818Timo Siraineni_stream_create_seekable(struct istream *input[],
acc4e0a41f1c8ef0559a19c280afc1b97b9e0818Timo Sirainen size_t max_buffer_size,
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen int (*fd_callback)(const char **path_r, void *context),
acc4e0a41f1c8ef0559a19c280afc1b97b9e0818Timo Sirainen void *context)
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen{
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen struct seekable_istream *sstream;
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen const unsigned char *data;
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen unsigned int count;
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen size_t size;
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen bool blocking = TRUE;
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen /* If all input streams are seekable, use concat istream instead */
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen for (count = 0; input[count] != NULL; count++) {
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen if (!input[count]->seekable)
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen break;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (input[count] == NULL)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return i_stream_create_concat(input);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch /* if any of the streams isn't blocking, set ourself also nonblocking */
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch for (count = 0; input[count] != NULL; count++) {
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch if (!input[count]->blocking)
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch blocking = FALSE;
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch i_stream_ref(input[count]);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch }
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch i_assert(count != 0);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch sstream = i_new(struct seekable_istream, 1);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch sstream->fd_callback = fd_callback;
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch sstream->context = context;
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch sstream->buffer = buffer_create_dynamic(default_pool, BUF_INITIAL_SIZE);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch sstream->istream.max_buffer_size = max_buffer_size;
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch sstream->fd = -1;
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch sstream->input = i_new(struct istream *, count + 1);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch memcpy(sstream->input, input, sizeof(*input) * count);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch sstream->cur_input = sstream->input[0];
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch /* initialize our buffer from first stream's pending data */
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch data = i_stream_get_data(sstream->cur_input, &size);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch buffer_append(sstream->buffer, data, size);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch i_stream_skip(sstream->cur_input, size);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch sstream->istream.iostream.close = i_stream_seekable_close;
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch sstream->istream.iostream.destroy = i_stream_seekable_destroy;
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch sstream->istream.iostream.set_max_buffer_size =
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch i_stream_seekable_set_max_buffer_size;
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch sstream->istream.read = i_stream_seekable_read;
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch sstream->istream.seek = i_stream_seekable_seek;
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch sstream->istream.stat = i_stream_seekable_stat;
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch sstream->istream.istream.readable_fd = FALSE;
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch sstream->istream.istream.blocking = blocking;
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch sstream->istream.istream.seekable = TRUE;
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch return i_stream_create(&sstream->istream, NULL, -1);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch}
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch