iostream-temp.c revision baf346e71ebd7b44fcba4b48f4d39845453b778b
70ee483d320a270993b56c713e350b736edd753fAki Tuomi/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomi#include "lib.h"
70ee483d320a270993b56c713e350b736edd753fAki Tuomi#include "buffer.h"
70ee483d320a270993b56c713e350b736edd753fAki Tuomi#include "str.h"
70ee483d320a270993b56c713e350b736edd753fAki Tuomi#include "safe-mkstemp.h"
70ee483d320a270993b56c713e350b736edd753fAki Tuomi#include "write-full.h"
70ee483d320a270993b56c713e350b736edd753fAki Tuomi#include "istream-private.h"
70ee483d320a270993b56c713e350b736edd753fAki Tuomi#include "ostream-private.h"
70ee483d320a270993b56c713e350b736edd753fAki Tuomi#include "iostream-temp.h"
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomi#include <unistd.h>
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomi#define IOSTREAM_TEMP_MAX_BUF_SIZE (1024*128)
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomistruct temp_ostream {
70ee483d320a270993b56c713e350b736edd753fAki Tuomi struct ostream_private ostream;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomi char *temp_path_prefix;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi enum iostream_temp_flags flags;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomi struct istream *dupstream;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi uoff_t dupstream_offset, dupstream_start_offset;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomi buffer_t *buf;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi int fd;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi bool fd_tried;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi};
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomistatic void o_stream_temp_close(struct iostream_private *stream)
70ee483d320a270993b56c713e350b736edd753fAki Tuomi{
70ee483d320a270993b56c713e350b736edd753fAki Tuomi struct temp_ostream *tstream = (struct temp_ostream *)stream;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomi if (tstream->fd != -1)
70ee483d320a270993b56c713e350b736edd753fAki Tuomi i_close_fd(&tstream->fd);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi if (tstream->buf != NULL)
70ee483d320a270993b56c713e350b736edd753fAki Tuomi buffer_free(&tstream->buf);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi i_free(tstream->temp_path_prefix);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi}
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomistatic int o_stream_temp_move_to_fd(struct temp_ostream *tstream)
70ee483d320a270993b56c713e350b736edd753fAki Tuomi{
70ee483d320a270993b56c713e350b736edd753fAki Tuomi string_t *path;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomi if (tstream->fd_tried)
70ee483d320a270993b56c713e350b736edd753fAki Tuomi return -1;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi tstream->fd_tried = TRUE;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomi path = t_str_new(128);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi str_append(path, tstream->temp_path_prefix);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi tstream->fd = safe_mkstemp_hostpid(path, 0600, (uid_t)-1, (gid_t)-1);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi if (tstream->fd == -1) {
70ee483d320a270993b56c713e350b736edd753fAki Tuomi i_error("safe_mkstemp(%s) failed: %m", str_c(path));
70ee483d320a270993b56c713e350b736edd753fAki Tuomi return -1;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi }
70ee483d320a270993b56c713e350b736edd753fAki Tuomi if (unlink(str_c(path)) < 0) {
70ee483d320a270993b56c713e350b736edd753fAki Tuomi i_error("unlink(%s) failed: %m", str_c(path));
70ee483d320a270993b56c713e350b736edd753fAki Tuomi i_close_fd(&tstream->fd);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi return -1;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi }
70ee483d320a270993b56c713e350b736edd753fAki Tuomi if (write_full(tstream->fd, tstream->buf->data, tstream->buf->used) < 0) {
70ee483d320a270993b56c713e350b736edd753fAki Tuomi i_error("write(%s) failed: %m", str_c(path));
70ee483d320a270993b56c713e350b736edd753fAki Tuomi i_close_fd(&tstream->fd);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi return -1;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi }
70ee483d320a270993b56c713e350b736edd753fAki Tuomi buffer_free(&tstream->buf);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi return 0;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi}
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomistatic ssize_t
70ee483d320a270993b56c713e350b736edd753fAki Tuomio_stream_temp_fd_sendv(struct temp_ostream *tstream,
70ee483d320a270993b56c713e350b736edd753fAki Tuomi const struct const_iovec *iov, unsigned int iov_count)
70ee483d320a270993b56c713e350b736edd753fAki Tuomi{
70ee483d320a270993b56c713e350b736edd753fAki Tuomi size_t bytes = 0;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi unsigned int i;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomi for (i = 0; i < iov_count; i++) {
70ee483d320a270993b56c713e350b736edd753fAki Tuomi if (write_full(tstream->fd, iov[i].iov_base, iov[i].iov_len) < 0) {
70ee483d320a270993b56c713e350b736edd753fAki Tuomi tstream->ostream.ostream.stream_errno = errno;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi return -1;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi }
70ee483d320a270993b56c713e350b736edd753fAki Tuomi bytes += iov[i].iov_len;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi tstream->ostream.ostream.offset += iov[i].iov_len;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi }
70ee483d320a270993b56c713e350b736edd753fAki Tuomi return bytes;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi}
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomistatic ssize_t
70ee483d320a270993b56c713e350b736edd753fAki Tuomio_stream_temp_sendv(struct ostream_private *stream,
70ee483d320a270993b56c713e350b736edd753fAki Tuomi const struct const_iovec *iov, unsigned int iov_count)
70ee483d320a270993b56c713e350b736edd753fAki Tuomi{
70ee483d320a270993b56c713e350b736edd753fAki Tuomi struct temp_ostream *tstream = (struct temp_ostream *)stream;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi ssize_t ret = 0;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi unsigned int i;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomi tstream->flags &= ~IOSTREAM_TEMP_FLAG_TRY_FD_DUP;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomi if (tstream->fd != -1)
70ee483d320a270993b56c713e350b736edd753fAki Tuomi return o_stream_temp_fd_sendv(tstream, iov, iov_count);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomi for (i = 0; i < iov_count; i++) {
70ee483d320a270993b56c713e350b736edd753fAki Tuomi if (tstream->buf->used + iov[i].iov_len > IOSTREAM_TEMP_MAX_BUF_SIZE) {
70ee483d320a270993b56c713e350b736edd753fAki Tuomi if (o_stream_temp_move_to_fd(tstream) == 0) {
70ee483d320a270993b56c713e350b736edd753fAki Tuomi return o_stream_temp_fd_sendv(tstream, iov+i,
70ee483d320a270993b56c713e350b736edd753fAki Tuomi iov_count-i);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi }
70ee483d320a270993b56c713e350b736edd753fAki Tuomi /* failed to move to temp fd, just keep it in memory */
70ee483d320a270993b56c713e350b736edd753fAki Tuomi }
70ee483d320a270993b56c713e350b736edd753fAki Tuomi buffer_append(tstream->buf, iov[i].iov_base, iov[i].iov_len);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi ret += iov[i].iov_len;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi stream->ostream.offset += iov[i].iov_len;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi }
70ee483d320a270993b56c713e350b736edd753fAki Tuomi return ret;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi}
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomistatic int o_stream_temp_dup_cancel(struct temp_ostream *tstream)
70ee483d320a270993b56c713e350b736edd753fAki Tuomi{
70ee483d320a270993b56c713e350b736edd753fAki Tuomi struct istream *input;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi uoff_t size = tstream->dupstream_offset -
70ee483d320a270993b56c713e350b736edd753fAki Tuomi tstream->dupstream_start_offset;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi off_t ret;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomi i_stream_seek(tstream->dupstream, tstream->dupstream_start_offset);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomi input = i_stream_create_limit(tstream->dupstream, size);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi do {
70ee483d320a270993b56c713e350b736edd753fAki Tuomi ret = io_stream_copy(&tstream->ostream.ostream,
70ee483d320a270993b56c713e350b736edd753fAki Tuomi input, IO_BLOCK_SIZE);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi } while (input->v_offset < tstream->dupstream_offset && ret > 0);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi if (ret < 0 && tstream->ostream.ostream.stream_errno == 0) {
70ee483d320a270993b56c713e350b736edd753fAki Tuomi i_assert(input->stream_errno != 0);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi tstream->ostream.ostream.stream_errno = input->stream_errno;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi }
70ee483d320a270993b56c713e350b736edd753fAki Tuomi i_stream_destroy(&input);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi i_stream_unref(&tstream->dupstream);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi return ret < 0 ? -1 : 0;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi}
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomistatic int o_stream_temp_dup_istream(struct temp_ostream *outstream,
70ee483d320a270993b56c713e350b736edd753fAki Tuomi struct istream *instream)
70ee483d320a270993b56c713e350b736edd753fAki Tuomi{
70ee483d320a270993b56c713e350b736edd753fAki Tuomi uoff_t in_size;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi off_t ret;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomi if (!instream->readable_fd || i_stream_get_fd(instream) == -1)
70ee483d320a270993b56c713e350b736edd753fAki Tuomi return 0;
a05819736f348d0c5ac8b4966ac6b04c21e1a391Timo Sirainen
a05819736f348d0c5ac8b4966ac6b04c21e1a391Timo Sirainen if (i_stream_get_size(instream, TRUE, &in_size) <= 0) {
70ee483d320a270993b56c713e350b736edd753fAki Tuomi if (outstream->dupstream != NULL)
70ee483d320a270993b56c713e350b736edd753fAki Tuomi return o_stream_temp_dup_cancel(outstream);
a05819736f348d0c5ac8b4966ac6b04c21e1a391Timo Sirainen return 0;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi }
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomi if (outstream->dupstream == NULL) {
70ee483d320a270993b56c713e350b736edd753fAki Tuomi outstream->dupstream = instream;
34742a4b92c16c300e3d75731685b678712340b2Timo Sirainen outstream->dupstream_start_offset = instream->v_offset;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi i_stream_ref(outstream->dupstream);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi } else {
a05819736f348d0c5ac8b4966ac6b04c21e1a391Timo Sirainen if (outstream->dupstream != instream ||
a05819736f348d0c5ac8b4966ac6b04c21e1a391Timo Sirainen outstream->dupstream_offset != instream->v_offset ||
70ee483d320a270993b56c713e350b736edd753fAki Tuomi outstream->dupstream_offset > in_size)
70ee483d320a270993b56c713e350b736edd753fAki Tuomi return o_stream_temp_dup_cancel(outstream);
a05819736f348d0c5ac8b4966ac6b04c21e1a391Timo Sirainen }
70ee483d320a270993b56c713e350b736edd753fAki Tuomi ret = in_size - instream->v_offset;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi i_stream_seek(instream, in_size);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi outstream->dupstream_offset = instream->v_offset;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi return ret;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi}
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomistatic off_t o_stream_temp_send_istream(struct ostream_private *_outstream,
70ee483d320a270993b56c713e350b736edd753fAki Tuomi struct istream *instream)
70ee483d320a270993b56c713e350b736edd753fAki Tuomi{
70ee483d320a270993b56c713e350b736edd753fAki Tuomi struct temp_ostream *outstream = (struct temp_ostream *)_outstream;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi uoff_t orig_offset;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi int ret;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomi if ((outstream->flags & IOSTREAM_TEMP_FLAG_TRY_FD_DUP) != 0) {
70ee483d320a270993b56c713e350b736edd753fAki Tuomi orig_offset = outstream->dupstream_offset;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi if ((ret = o_stream_temp_dup_istream(outstream, instream)) > 0)
70ee483d320a270993b56c713e350b736edd753fAki Tuomi return outstream->dupstream_offset - orig_offset;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi if (ret < 0)
70ee483d320a270993b56c713e350b736edd753fAki Tuomi return -1;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi outstream->flags &= ~IOSTREAM_TEMP_FLAG_TRY_FD_DUP;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi }
70ee483d320a270993b56c713e350b736edd753fAki Tuomi return io_stream_copy(&outstream->ostream.ostream,
70ee483d320a270993b56c713e350b736edd753fAki Tuomi instream, IO_BLOCK_SIZE);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi}
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomistruct ostream *iostream_temp_create(const char *temp_path_prefix,
70ee483d320a270993b56c713e350b736edd753fAki Tuomi enum iostream_temp_flags flags)
70ee483d320a270993b56c713e350b736edd753fAki Tuomi{
70ee483d320a270993b56c713e350b736edd753fAki Tuomi struct temp_ostream *tstream;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi struct ostream *output;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomi tstream = i_new(struct temp_ostream, 1);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi tstream->ostream.sendv = o_stream_temp_sendv;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi tstream->ostream.send_istream = o_stream_temp_send_istream;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi tstream->ostream.iostream.close = o_stream_temp_close;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi tstream->temp_path_prefix = i_strdup(temp_path_prefix);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi tstream->flags = flags;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi tstream->buf = buffer_create_dynamic(default_pool, 8192);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi tstream->fd = -1;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomi output = o_stream_create(&tstream->ostream, NULL, -1);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi o_stream_set_name(output, "(temp iostream)");
70ee483d320a270993b56c713e350b736edd753fAki Tuomi return output;
a05819736f348d0c5ac8b4966ac6b04c21e1a391Timo Sirainen}
a05819736f348d0c5ac8b4966ac6b04c21e1a391Timo Sirainen
70ee483d320a270993b56c713e350b736edd753fAki Tuomistatic void iostream_temp_buf_destroyed(buffer_t *buf)
70ee483d320a270993b56c713e350b736edd753fAki Tuomi{
70ee483d320a270993b56c713e350b736edd753fAki Tuomi buffer_free(&buf);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi}
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomistruct istream *iostream_temp_finish(struct ostream **output,
70ee483d320a270993b56c713e350b736edd753fAki Tuomi size_t max_buffer_size)
70ee483d320a270993b56c713e350b736edd753fAki Tuomi{
70ee483d320a270993b56c713e350b736edd753fAki Tuomi struct temp_ostream *tstream =
70ee483d320a270993b56c713e350b736edd753fAki Tuomi (struct temp_ostream *)(*output)->real_stream;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi struct istream *input, *input2;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi uoff_t abs_offset, size;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi int fd;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi
70ee483d320a270993b56c713e350b736edd753fAki Tuomi if (tstream->dupstream != NULL) {
70ee483d320a270993b56c713e350b736edd753fAki Tuomi abs_offset = tstream->dupstream->real_stream->abs_start_offset +
70ee483d320a270993b56c713e350b736edd753fAki Tuomi tstream->dupstream_start_offset;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi size = tstream->dupstream_offset -
70ee483d320a270993b56c713e350b736edd753fAki Tuomi tstream->dupstream_start_offset;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi fd = dup(i_stream_get_fd(tstream->dupstream));
70ee483d320a270993b56c713e350b736edd753fAki Tuomi if (fd == -1)
70ee483d320a270993b56c713e350b736edd753fAki Tuomi input = i_stream_create_error(errno);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi else {
70ee483d320a270993b56c713e350b736edd753fAki Tuomi input2 = i_stream_create_fd(fd, max_buffer_size, TRUE);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi i_stream_seek(input2, abs_offset);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi input = i_stream_create_limit(input2, size);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi i_stream_unref(&input2);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi }
70ee483d320a270993b56c713e350b736edd753fAki Tuomi i_stream_unref(&tstream->dupstream);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi } else if (tstream->fd != -1) {
70ee483d320a270993b56c713e350b736edd753fAki Tuomi input = i_stream_create_fd(tstream->fd, max_buffer_size, TRUE);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi tstream->fd = -1;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi } else {
70ee483d320a270993b56c713e350b736edd753fAki Tuomi input = i_stream_create_from_data(tstream->buf->data,
70ee483d320a270993b56c713e350b736edd753fAki Tuomi tstream->buf->used);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi i_stream_set_destroy_callback(input, iostream_temp_buf_destroyed,
70ee483d320a270993b56c713e350b736edd753fAki Tuomi tstream->buf);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi tstream->buf = NULL;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi }
70ee483d320a270993b56c713e350b736edd753fAki Tuomi o_stream_destroy(output);
70ee483d320a270993b56c713e350b736edd753fAki Tuomi return input;
70ee483d320a270993b56c713e350b736edd753fAki Tuomi}
70ee483d320a270993b56c713e350b736edd753fAki Tuomi