istream-seekable.c revision a6f281d078ed03d555802c1a8e15fefce80132dc
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen/* Copyright (c) 2005-2012 Dovecot authors, see the included COPYING file */
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen#include "lib.h"
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen#include "buffer.h"
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen#include "read-full.h"
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen#include "write-full.h"
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen#include "istream-private.h"
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen#include "istream-concat.h"
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen#include "istream-seekable.h"
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen#include <unistd.h>
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen#define BUF_INITIAL_SIZE (1024*32)
d22390f33eedbd2413debabc0662dde5241b1aa6Timo Sirainen
d22390f33eedbd2413debabc0662dde5241b1aa6Timo Sirainenstruct seekable_istream {
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen struct istream_private istream;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen char *temp_path;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen uoff_t write_peak;
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen uoff_t size;
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen int (*fd_callback)(const char **path_r, void *context);
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen void *context;
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen buffer_t *membuf;
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen struct istream **input, *cur_input;
72e9e7ad158101d46860b42c4080e894485c78c3Timo Sirainen struct istream *fd_input;
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen unsigned int cur_idx;
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen int fd;
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen};
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainenstatic void i_stream_seekable_close(struct iostream_private *stream)
72e9e7ad158101d46860b42c4080e894485c78c3Timo Sirainen{
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
4823da41d112ff9f5e8f088b0e60d1636e01ff92Timo Sirainen unsigned int i;
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen
afa6ac39d1d6df246d4e7352288c2a0388276a24Timo Sirainen sstream->fd = -1;
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen if (sstream->fd_input != NULL)
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen i_stream_close(sstream->fd_input);
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen for (i = 0; sstream->input[i] != NULL; i++)
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen i_stream_close(sstream->input[i]);
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen}
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen
72e9e7ad158101d46860b42c4080e894485c78c3Timo Sirainenstatic void unref_streams(struct seekable_istream *sstream)
4823da41d112ff9f5e8f088b0e60d1636e01ff92Timo Sirainen{
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen unsigned int i;
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen for (i = 0; sstream->input[i] != NULL; i++)
4823da41d112ff9f5e8f088b0e60d1636e01ff92Timo Sirainen i_stream_unref(&sstream->input[i]);
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen}
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainenstatic void i_stream_seekable_destroy(struct iostream_private *stream)
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen{
9945ad9e528188521d876b80f08a648072ffa207Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
4823da41d112ff9f5e8f088b0e60d1636e01ff92Timo Sirainen
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen if (sstream->membuf != NULL)
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen buffer_free(&sstream->membuf);
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen if (sstream->fd_input != NULL)
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen i_stream_unref(&sstream->fd_input);
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen unref_streams(sstream);
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_free(sstream->temp_path);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_free(sstream->input);
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic void
992a13add4eea0810e4db0f042a595dddf85536aTimo Siraineni_stream_seekable_set_max_buffer_size(struct iostream_private *stream,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen size_t max_size)
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen{
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen unsigned int i;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen sstream->istream.max_buffer_size = max_size;
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen if (sstream->fd_input != NULL)
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen i_stream_set_max_buffer_size(sstream->fd_input, max_size);
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen for (i = 0; sstream->input[i] != NULL; i++)
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen i_stream_set_max_buffer_size(sstream->input[i], max_size);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic int copy_to_temp_file(struct seekable_istream *sstream)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen struct istream_private *stream = &sstream->istream;
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen const char *path;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const unsigned char *buffer;
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen size_t size;
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen int fd;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen fd = sstream->fd_callback(&path, sstream->context);
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen if (fd == -1)
1171f0abf442638bac1827bb24a0b6b8eb682a82Timo Sirainen return -1;
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* copy our currently read buffer to it */
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen if (write_full(fd, sstream->membuf->data, sstream->membuf->used) < 0) {
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen if (!ENOSPACE(errno))
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen i_error("write_full(%s) failed: %m", path);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_close_fd(&fd);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen }
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen sstream->temp_path = i_strdup(path);
d5ac54ef50db16b50689b5c8b7bb64d344190832Timo Sirainen sstream->write_peak = sstream->membuf->used;
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen sstream->fd = fd;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen sstream->fd_input =
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen i_stream_create_fd(fd, sstream->istream.max_buffer_size, TRUE);
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen /* read back the data we just had in our buffer */
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen i_stream_seek(sstream->fd_input, stream->istream.v_offset);
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen for (;;) {
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen buffer = i_stream_get_data(sstream->fd_input, &size);
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen if (size >= stream->pos)
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen break;
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen if (i_stream_read(sstream->fd_input) <= 0) {
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen i_error("istream-seekable: Couldn't read back "
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen "in-memory input %s",
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen i_stream_get_name(&stream->istream));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_stream_destroy(&sstream->fd_input);
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen return -1;
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen }
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen }
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen stream->buffer = buffer;
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen stream->pos = size;
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen buffer_free(&sstream->membuf);
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen return 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainenstatic ssize_t read_more(struct seekable_istream *sstream)
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen{
325d4ad220bd13f6d176391d962a0e33c856a7f6Timo Sirainen size_t size;
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen ssize_t ret;
b20fb5b1df9d604a7541f5118fc5b4b466d211efTimo Sirainen
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen if (sstream->cur_input == NULL) {
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen sstream->istream.istream.eof = TRUE;
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainen return -1;
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen }
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen while ((ret = i_stream_read(sstream->cur_input)) < 0) {
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen if (!sstream->cur_input->eof) {
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen /* full / error */
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen sstream->istream.istream.stream_errno =
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen sstream->cur_input->stream_errno;
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen return -1;
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen }
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen
7c95b03620a03a43dd72d39608cea5fc77393ad6Timo Sirainen /* go to next stream */
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen sstream->cur_input = sstream->input[sstream->cur_idx++];
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen if (sstream->cur_input == NULL) {
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen /* last one, EOF */
a40d26f83af808a0ea1e212c001d682a96d870b0Timo Sirainen sstream->size = sstream->istream.istream.v_offset;
811f2e26d9782d9cb99fdf82e18ffa0a77564fe2Timo Sirainen sstream->istream.istream.eof = TRUE;
811f2e26d9782d9cb99fdf82e18ffa0a77564fe2Timo Sirainen return -1;
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen }
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen /* see if stream has pending data */
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen size = i_stream_get_data_size(sstream->cur_input);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (size != 0)
4b058f90f9e8a2c6b2eed275de4eb8cc5195a71dTimo Sirainen return size;
4b058f90f9e8a2c6b2eed275de4eb8cc5195a71dTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return ret;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic bool read_from_buffer(struct seekable_istream *sstream, ssize_t *ret_r)
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen{
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen struct istream_private *stream = &sstream->istream;
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen const unsigned char *data;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen size_t size, pos, offset;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (stream->istream.v_offset + stream->pos >= sstream->membuf->used) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* need to read more */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (sstream->membuf->used >= stream->max_buffer_size)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return FALSE;
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen size = sstream->cur_input == NULL ? 0 :
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen i_stream_get_data_size(sstream->cur_input);
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen if (size == 0) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* read more to buffer */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *ret_r = read_more(sstream);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (*ret_r <= 0)
aff7542e1d2f48b030560a4f01096a2cc3f671ceTimo Sirainen return TRUE;
aff7542e1d2f48b030560a4f01096a2cc3f671ceTimo Sirainen }
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* we should have more now. */
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen data = i_stream_get_data(sstream->cur_input, &size);
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen i_assert(size > 0);
aff7542e1d2f48b030560a4f01096a2cc3f671ceTimo Sirainen buffer_append(sstream->membuf, data, size);
aff7542e1d2f48b030560a4f01096a2cc3f671ceTimo Sirainen i_stream_skip(sstream->cur_input, size);
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen offset = stream->istream.v_offset;
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen stream->buffer = CONST_PTR_OFFSET(sstream->membuf->data, offset);
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen pos = sstream->membuf->used - offset;
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen *ret_r = pos - stream->pos;
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen i_assert(*ret_r > 0);
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen stream->pos = pos;
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen stream->skip = 0;
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen return TRUE;
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen}
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainenstatic int i_stream_seekable_write_failed(struct seekable_istream *sstream)
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen{
4b058f90f9e8a2c6b2eed275de4eb8cc5195a71dTimo Sirainen struct istream_private *stream = &sstream->istream;
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen void *data;
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen i_assert(sstream->membuf == NULL);
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen sstream->membuf =
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen buffer_create_dynamic(default_pool, sstream->write_peak);
a40d26f83af808a0ea1e212c001d682a96d870b0Timo Sirainen data = buffer_append_space_unsafe(sstream->membuf, sstream->write_peak);
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen
7416b94f38e82381abd9cee660efdcf3e7b773afTimo Sirainen if (pread_full(sstream->fd, data, sstream->write_peak, 0) < 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_error("read(%s) failed: %m", sstream->temp_path);
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen buffer_free(&sstream->membuf);
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen return -1;
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen }
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen i_stream_destroy(&sstream->fd_input);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_close_fd(&sstream->fd);
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen stream->max_buffer_size = (size_t)-1;
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen i_free_and_null(sstream->temp_path);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen return 0;
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen}
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainenstatic ssize_t i_stream_seekable_read(struct istream_private *stream)
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen{
aff7542e1d2f48b030560a4f01096a2cc3f671ceTimo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen const unsigned char *data;
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen size_t size, pos;
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen ssize_t ret;
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen if (sstream->membuf != NULL) {
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen if (read_from_buffer(sstream, &ret))
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen return ret;
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen /* copy everything to temp file and use it as the stream */
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen if (copy_to_temp_file(sstream) < 0) {
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen stream->max_buffer_size = (size_t)-1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (!read_from_buffer(sstream, &ret))
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen i_unreached();
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return ret;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen i_assert(sstream->membuf == NULL);
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen }
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen i_assert(stream->istream.v_offset + stream->pos <= sstream->write_peak);
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen if (stream->istream.v_offset + stream->pos == sstream->write_peak) {
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen /* need to read more */
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen ret = read_more(sstream);
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen if (ret <= 0)
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen return ret;
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen /* save to our file */
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen data = i_stream_get_data(sstream->cur_input, &size);
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen ret = write(sstream->fd, data, size);
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen if (ret <= 0) {
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen if (ret < 0 && !ENOSPACE(errno)) {
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen i_error("write_full(%s) failed: %m",
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen sstream->temp_path);
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen }
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen if (i_stream_seekable_write_failed(sstream) < 0)
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen return -1;
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen if (!read_from_buffer(sstream, &ret))
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen i_unreached();
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen return ret;
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen }
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen i_stream_sync(sstream->fd_input);
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen i_stream_skip(sstream->cur_input, ret);
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen sstream->write_peak += ret;
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen }
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen i_stream_seek(sstream->fd_input, stream->istream.v_offset);
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen ret = i_stream_read(sstream->fd_input);
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen if (ret <= 0) {
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen stream->istream.eof = sstream->fd_input->eof;
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen stream->istream.stream_errno =
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen sstream->fd_input->stream_errno;
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen } else {
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen ret = -2;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen
d22390f33eedbd2413debabc0662dde5241b1aa6Timo Sirainen stream->buffer = i_stream_get_data(sstream->fd_input, &pos);
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen stream->pos -= stream->skip;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen stream->skip = 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen ret = pos > stream->pos ? (ssize_t)(pos - stream->pos) : ret;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen stream->pos = pos;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return ret;
d22390f33eedbd2413debabc0662dde5241b1aa6Timo Sirainen}
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainenstatic const struct stat *
d22390f33eedbd2413debabc0662dde5241b1aa6Timo Siraineni_stream_seekable_stat(struct istream_private *stream, bool exact)
d22390f33eedbd2413debabc0662dde5241b1aa6Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct seekable_istream *sstream = (struct seekable_istream *)stream;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uoff_t old_offset;
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen ssize_t ret;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (sstream->size != (uoff_t)-1) {
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen /* we've already reached EOF and know the size */
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen stream->statbuf.st_size = sstream->size;
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen return &stream->statbuf;
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen }
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen if (sstream->membuf != NULL) {
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen /* we want to know the full size of the file, so read until
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen we're finished */
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen old_offset = stream->istream.v_offset;
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen do {
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen i_stream_skip(&stream->istream,
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen stream->pos - stream->skip);
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen } while ((ret = i_stream_seekable_read(stream)) > 0);
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen if (ret == 0) {
d22390f33eedbd2413debabc0662dde5241b1aa6Timo Sirainen i_panic("i_stream_stat() used for non-blocking "
d22390f33eedbd2413debabc0662dde5241b1aa6Timo Sirainen "seekable stream %s offset %"PRIuUOFF_T,
d22390f33eedbd2413debabc0662dde5241b1aa6Timo Sirainen i_stream_get_name(sstream->cur_input),
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen sstream->cur_input->v_offset);
d22390f33eedbd2413debabc0662dde5241b1aa6Timo Sirainen }
d22390f33eedbd2413debabc0662dde5241b1aa6Timo Sirainen i_stream_skip(&stream->istream, stream->pos - stream->skip);
d22390f33eedbd2413debabc0662dde5241b1aa6Timo Sirainen i_stream_seek(&stream->istream, old_offset);
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen unref_streams(sstream);
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen }
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (sstream->fd_input != NULL) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* using a file backed buffer, we can use real fstat() */
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen return i_stream_stat(sstream->fd_input, exact);
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen } else {
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen /* buffer is completely in memory */
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen i_assert(sstream->membuf != NULL);
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen stream->statbuf.st_size = sstream->membuf->used;
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen return &stream->statbuf;
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen }
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen}
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainenstruct istream *
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Siraineni_stream_create_seekable(struct istream *input[],
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen size_t max_buffer_size,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen int (*fd_callback)(const char **path_r, void *context),
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen void *context)
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen{
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen struct seekable_istream *sstream;
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen const unsigned char *data;
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen unsigned int count;
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen size_t size;
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen bool blocking = TRUE;
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen /* If all input streams are seekable, use concat istream instead */
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen for (count = 0; input[count] != NULL; count++) {
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen if (!input[count]->seekable)
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen break;
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen }
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen if (input[count] == NULL)
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen return i_stream_create_concat(input);
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen
365435901c22df7e5838f574c950b0e32e77f78aTimo Sirainen /* if any of the streams isn't blocking, set ourself also nonblocking */
365435901c22df7e5838f574c950b0e32e77f78aTimo Sirainen for (count = 0; input[count] != NULL; count++) {
365435901c22df7e5838f574c950b0e32e77f78aTimo Sirainen if (!input[count]->blocking)
365435901c22df7e5838f574c950b0e32e77f78aTimo Sirainen blocking = FALSE;
365435901c22df7e5838f574c950b0e32e77f78aTimo Sirainen i_stream_ref(input[count]);
365435901c22df7e5838f574c950b0e32e77f78aTimo Sirainen }
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen i_assert(count != 0);
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen sstream = i_new(struct seekable_istream, 1);
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen sstream->fd_callback = fd_callback;
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen sstream->context = context;
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen sstream->membuf = buffer_create_dynamic(default_pool, BUF_INITIAL_SIZE);
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen sstream->istream.max_buffer_size = max_buffer_size;
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen sstream->fd = -1;
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen sstream->size = (uoff_t)-1;
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen
a817fdcc43aedf423e2134091d5f83f91d64bcc9Timo Sirainen sstream->input = i_new(struct istream *, count + 1);
a817fdcc43aedf423e2134091d5f83f91d64bcc9Timo Sirainen memcpy(sstream->input, input, sizeof(*input) * count);
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen sstream->cur_input = sstream->input[0];
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* initialize our buffer from first stream's pending data */
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen data = i_stream_get_data(sstream->cur_input, &size);
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen buffer_append(sstream->membuf, data, size);
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen i_stream_skip(sstream->cur_input, size);
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen sstream->istream.iostream.close = i_stream_seekable_close;
sstream->istream.iostream.destroy = i_stream_seekable_destroy;
sstream->istream.iostream.set_max_buffer_size =
i_stream_seekable_set_max_buffer_size;
sstream->istream.read = i_stream_seekable_read;
sstream->istream.stat = i_stream_seekable_stat;
sstream->istream.istream.readable_fd = FALSE;
sstream->istream.istream.blocking = blocking;
sstream->istream.istream.seekable = TRUE;
return i_stream_create(&sstream->istream, NULL, -1);
}