ostream-file.c revision 62355fdb8b7c5fb4ed1b28433bc7df718407e2c6
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2002-2003 Timo Sirainen */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen/* @UNSAFE: whole file */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen#include "lib.h"
76a99afe0914951d20d96e0bf5e6d8d3ea3fd503Timo Sirainen#include "ioloop.h"
bdd36cfdba3ff66d25570a9ff568d69e1eb543cfTimo Sirainen#include "write-full.h"
76a99afe0914951d20d96e0bf5e6d8d3ea3fd503Timo Sirainen#include "network.h"
596ec384269cad3b0f0661df89b9cf33cbd171b7Timo Sirainen#include "sendfile-util.h"
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen#include "istream.h"
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen#include "istream-internal.h"
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen#include "ostream-internal.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
a35cbba04d0a2823da98e693bd09a051addffdb2Timo Sirainen#include <unistd.h>
94e1adead9faddec88a623485b9999a87b1684faTimo Sirainen#include <sys/stat.h>
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen#ifdef HAVE_SYS_UIO_H
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen# include <sys/uio.h>
cb2b5a7d6e7e651191bf9ee1eda94a6e207288b0Timo Sirainen#endif
cb2b5a7d6e7e651191bf9ee1eda94a6e207288b0Timo Sirainen
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen/* try to keep the buffer size within 4k..128k. ReiserFS may actually return
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen 128k as optimal size. */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen#define DEFAULT_OPTIMAL_BLOCK_SIZE 4096
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen#define MAX_OPTIMAL_BLOCK_SIZE (128*1024)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen#define IS_STREAM_EMPTY(fstream) \
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen ((fstream)->head == (fstream)->tail && !(fstream)->full)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#define MAX_SSIZE_T(size) \
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen ((size) < SSIZE_T_MAX ? (size_t)(size) : SSIZE_T_MAX)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
c4db1218645ed8ec8b5ae67c05bc5d7a80c1b8aeTimo Sirainenstruct file_ostream {
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen struct _ostream ostream;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen int fd;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen struct io *io;
a35cbba04d0a2823da98e693bd09a051addffdb2Timo Sirainen
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen unsigned char *buffer; /* ring-buffer */
a35cbba04d0a2823da98e693bd09a051addffdb2Timo Sirainen size_t buffer_size, max_buffer_size, optimal_block_size;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen size_t head, tail; /* first unsent/unused byte */
a35cbba04d0a2823da98e693bd09a051addffdb2Timo Sirainen
c4db1218645ed8ec8b5ae67c05bc5d7a80c1b8aeTimo Sirainen unsigned int full:1; /* if head == tail, is buffer empty or full? */
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen unsigned int file:1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen unsigned int corked:1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen unsigned int flush_pending:1;
a35cbba04d0a2823da98e693bd09a051addffdb2Timo Sirainen unsigned int no_socket_cork:1;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen unsigned int no_sendfile:1;
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen unsigned int autoclose_fd:1;
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen};
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic void stream_send_io(void *context);
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic void stream_closed(struct file_ostream *fstream)
4316355ca8b7698516272520a972291378698140Timo Sirainen{
4316355ca8b7698516272520a972291378698140Timo Sirainen if (fstream->autoclose_fd && fstream->fd != -1) {
4316355ca8b7698516272520a972291378698140Timo Sirainen if (close(fstream->fd) < 0)
4316355ca8b7698516272520a972291378698140Timo Sirainen i_error("file_ostream.close() failed: %m");
4316355ca8b7698516272520a972291378698140Timo Sirainen fstream->fd = -1;
4316355ca8b7698516272520a972291378698140Timo Sirainen }
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainen if (fstream->io != NULL) {
4316355ca8b7698516272520a972291378698140Timo Sirainen io_remove(fstream->io);
4316355ca8b7698516272520a972291378698140Timo Sirainen fstream->io = NULL;
4316355ca8b7698516272520a972291378698140Timo Sirainen }
4316355ca8b7698516272520a972291378698140Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen fstream->ostream.ostream.closed = TRUE;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen}
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainenstatic void _close(struct _iostream *stream)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen{
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen struct file_ostream *fstream = (struct file_ostream *)stream;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen /* flush output before really closing it */
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen o_stream_flush(&fstream->ostream.ostream);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen stream_closed(fstream);
4316355ca8b7698516272520a972291378698140Timo Sirainen}
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic void _destroy(struct _iostream *stream)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen{
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen struct file_ostream *fstream = (struct file_ostream *)stream;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainen p_free(fstream->ostream.iostream.pool, fstream->buffer);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen}
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic void _set_max_buffer_size(struct _iostream *stream, size_t max_size)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen{
4316355ca8b7698516272520a972291378698140Timo Sirainen struct file_ostream *fstream = (struct file_ostream *)stream;
4316355ca8b7698516272520a972291378698140Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen fstream->max_buffer_size = max_size;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen}
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainenstatic void update_buffer(struct file_ostream *fstream, size_t size)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen{
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen size_t used;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (IS_STREAM_EMPTY(fstream) || size == 0)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen return;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (fstream->head < fstream->tail) {
4316355ca8b7698516272520a972291378698140Timo Sirainen /* ...HXXXT... */
4316355ca8b7698516272520a972291378698140Timo Sirainen used = fstream->tail - fstream->head;
4316355ca8b7698516272520a972291378698140Timo Sirainen i_assert(size <= used);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen fstream->head += size;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen } else {
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen /* XXXT...HXXX */
4316355ca8b7698516272520a972291378698140Timo Sirainen used = fstream->buffer_size - fstream->head;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (size > used) {
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen size -= used;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen i_assert(size <= fstream->tail);
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen fstream->head = size;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen } else {
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen fstream->head += size;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen }
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen fstream->full = FALSE;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen }
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen if (fstream->head == fstream->tail)
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen fstream->head = fstream->tail = 0;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen if (fstream->head == fstream->buffer_size)
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen fstream->head = 0;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen}
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainenstatic ssize_t o_stream_writev(struct file_ostream *fstream,
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen const struct const_iovec *iov, int iov_size)
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen{
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen ssize_t ret;
4316355ca8b7698516272520a972291378698140Timo Sirainen size_t size, sent;
4316355ca8b7698516272520a972291378698140Timo Sirainen int i;
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainen if (iov_size == 1)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen ret = write(fstream->fd, iov->iov_base, iov->iov_len);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen else {
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen sent = 0;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen while (iov_size > IOV_MAX) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen size = 0;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen for (i = 0; i < IOV_MAX; i++)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen size += iov[i].iov_len;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen ret = writev(fstream->fd, (const struct iovec *)iov,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen IOV_MAX);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (ret != (ssize_t)size)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen break;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen sent += ret;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen iov += IOV_MAX;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen iov_size -= IOV_MAX;
d9b8c65d0a0ffc709ba7d23c449dbf2f46b10674Timo Sirainen }
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainen if (iov_size <= IOV_MAX) {
4316355ca8b7698516272520a972291378698140Timo Sirainen ret = writev(fstream->fd, (const struct iovec *)iov,
d9b8c65d0a0ffc709ba7d23c449dbf2f46b10674Timo Sirainen iov_size);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen if (ret > 0)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen ret += sent;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (ret < 0) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (errno == EAGAIN || errno == EINTR)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return 0;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (errno == EINVAL) i_error("o_stream_sendv() -> EINVAL");
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen fstream->ostream.ostream.stream_errno = errno;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen stream_closed(fstream);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return -1;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen }
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return ret;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen}
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen/* returns how much of vector was used */
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenstatic int o_stream_fill_iovec(struct file_ostream *fstream,
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen struct const_iovec iov[2])
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen{
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen if (IS_STREAM_EMPTY(fstream))
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk return 0;
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk
e3077468777f5d324224365e34d7bbc449168e52Timo Sirainen if (fstream->head < fstream->tail) {
e3077468777f5d324224365e34d7bbc449168e52Timo Sirainen iov[0].iov_base = fstream->buffer + fstream->head;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen iov[0].iov_len = fstream->tail - fstream->head;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen return 1;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen } else {
4316355ca8b7698516272520a972291378698140Timo Sirainen iov[0].iov_base = fstream->buffer + fstream->head;
4316355ca8b7698516272520a972291378698140Timo Sirainen iov[0].iov_len = fstream->buffer_size - fstream->head;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen if (fstream->tail == 0)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return 1;
4316355ca8b7698516272520a972291378698140Timo Sirainen else {
4316355ca8b7698516272520a972291378698140Timo Sirainen iov[1].iov_base = fstream->buffer;
4316355ca8b7698516272520a972291378698140Timo Sirainen iov[1].iov_len = fstream->tail;
4316355ca8b7698516272520a972291378698140Timo Sirainen return 2;
4316355ca8b7698516272520a972291378698140Timo Sirainen }
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen }
4316355ca8b7698516272520a972291378698140Timo Sirainen}
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic int buffer_flush(struct file_ostream *fstream)
666286d8ecc6c450b2232dcc628f79454215acfcTimo Sirainen{
4316355ca8b7698516272520a972291378698140Timo Sirainen struct const_iovec iov[2];
4316355ca8b7698516272520a972291378698140Timo Sirainen int iov_len;
4316355ca8b7698516272520a972291378698140Timo Sirainen ssize_t ret;
4316355ca8b7698516272520a972291378698140Timo Sirainen
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen iov_len = o_stream_fill_iovec(fstream, iov);
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen if (iov_len > 0) {
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen ret = o_stream_writev(fstream, iov, iov_len);
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen if (ret < 0)
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen return -1;
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen update_buffer(fstream, ret);
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen }
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen return IS_STREAM_EMPTY(fstream) ? 1 : 0;
4316355ca8b7698516272520a972291378698140Timo Sirainen}
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic void _cork(struct _ostream *stream, int set)
4316355ca8b7698516272520a972291378698140Timo Sirainen{
4316355ca8b7698516272520a972291378698140Timo Sirainen struct file_ostream *fstream = (struct file_ostream *)stream;
4316355ca8b7698516272520a972291378698140Timo Sirainen int ret;
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainen if (fstream->corked != set && !stream->ostream.closed) {
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen if (set && fstream->io != NULL) {
4316355ca8b7698516272520a972291378698140Timo Sirainen io_remove(fstream->io);
4316355ca8b7698516272520a972291378698140Timo Sirainen fstream->io = NULL;
4316355ca8b7698516272520a972291378698140Timo Sirainen } else if (!set) {
4316355ca8b7698516272520a972291378698140Timo Sirainen ret = buffer_flush(fstream);
4316355ca8b7698516272520a972291378698140Timo Sirainen if (fstream->io == NULL &&
4316355ca8b7698516272520a972291378698140Timo Sirainen (ret == 0 || fstream->flush_pending)) {
4316355ca8b7698516272520a972291378698140Timo Sirainen fstream->io = io_add(fstream->fd, IO_WRITE,
4316355ca8b7698516272520a972291378698140Timo Sirainen stream_send_io, fstream);
4316355ca8b7698516272520a972291378698140Timo Sirainen }
4316355ca8b7698516272520a972291378698140Timo Sirainen }
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainen if (!fstream->no_socket_cork) {
4316355ca8b7698516272520a972291378698140Timo Sirainen if (net_set_cork(fstream->fd, set) < 0)
93f1642397e46497894e6695749e5c52fda61774Timo Sirainen fstream->no_socket_cork = TRUE;
4316355ca8b7698516272520a972291378698140Timo Sirainen }
4316355ca8b7698516272520a972291378698140Timo Sirainen fstream->corked = set;
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen }
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen}
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic int _flush(struct _ostream *stream)
3cfc375f0d939c346b9b0e6f0ac78b9bc367dd95Timo Sirainen{
4316355ca8b7698516272520a972291378698140Timo Sirainen struct file_ostream *fstream = (struct file_ostream *) stream;
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainen return buffer_flush(fstream);
4316355ca8b7698516272520a972291378698140Timo Sirainen}
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic void _flush_pending(struct _ostream *stream, int set)
4316355ca8b7698516272520a972291378698140Timo Sirainen{
4316355ca8b7698516272520a972291378698140Timo Sirainen struct file_ostream *fstream = (struct file_ostream *) stream;
4316355ca8b7698516272520a972291378698140Timo Sirainen
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen fstream->flush_pending = set;
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen if (set && !fstream->corked && fstream->io == NULL) {
4316355ca8b7698516272520a972291378698140Timo Sirainen fstream->io = io_add(fstream->fd, IO_WRITE,
4316355ca8b7698516272520a972291378698140Timo Sirainen stream_send_io, fstream);
4316355ca8b7698516272520a972291378698140Timo Sirainen }
4316355ca8b7698516272520a972291378698140Timo Sirainen}
4316355ca8b7698516272520a972291378698140Timo Sirainen
b8a4aab1f117f6760184ad50b1af41ba810b51f9Timo Sirainenstatic size_t get_unused_space(struct file_ostream *fstream)
b8a4aab1f117f6760184ad50b1af41ba810b51f9Timo Sirainen{
4316355ca8b7698516272520a972291378698140Timo Sirainen if (fstream->head > fstream->tail) {
4316355ca8b7698516272520a972291378698140Timo Sirainen /* XXXT...HXXX */
4316355ca8b7698516272520a972291378698140Timo Sirainen return fstream->head - fstream->tail;
4316355ca8b7698516272520a972291378698140Timo Sirainen } else if (fstream->head < fstream->tail) {
4316355ca8b7698516272520a972291378698140Timo Sirainen /* ...HXXXT... */
4316355ca8b7698516272520a972291378698140Timo Sirainen return (fstream->buffer_size - fstream->tail) + fstream->head;
4316355ca8b7698516272520a972291378698140Timo Sirainen } else {
4316355ca8b7698516272520a972291378698140Timo Sirainen /* either fully unused or fully used */
4316355ca8b7698516272520a972291378698140Timo Sirainen return fstream->full ? 0 : fstream->buffer_size;
4316355ca8b7698516272520a972291378698140Timo Sirainen }
4316355ca8b7698516272520a972291378698140Timo Sirainen}
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic size_t _get_used_size(struct _ostream *stream)
4316355ca8b7698516272520a972291378698140Timo Sirainen{
4316355ca8b7698516272520a972291378698140Timo Sirainen struct file_ostream *fstream = (struct file_ostream *)stream;
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainen return fstream->buffer_size - get_unused_space(fstream);
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen}
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic int _seek(struct _ostream *stream, uoff_t offset)
4316355ca8b7698516272520a972291378698140Timo Sirainen{
4316355ca8b7698516272520a972291378698140Timo Sirainen struct file_ostream *fstream = (struct file_ostream *)stream;
4316355ca8b7698516272520a972291378698140Timo Sirainen off_t ret;
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen if (offset > OFF_T_MAX) {
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen stream->ostream.stream_errno = EINVAL;
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen i_error("_seek(1) -> EINVAL");
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen return -1;
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen }
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen if (buffer_flush(fstream) < 0)
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen return -1;
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen ret = lseek(fstream->fd, (off_t)offset, SEEK_SET);
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen if (ret < 0) {
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen if (errno == EINVAL) i_error("_seek(2) -> EINVAL");
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen stream->ostream.stream_errno = errno;
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen return -1;
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen }
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen if (ret != (off_t)offset) {
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen i_error("_seek(3) -> EINVAL");
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen stream->ostream.stream_errno = EINVAL;
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen return -1;
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen }
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen stream->ostream.stream_errno = 0;
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen stream->ostream.offset = offset;
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen return 1;
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen}
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic void o_stream_grow_buffer(struct file_ostream *fstream, size_t bytes)
4316355ca8b7698516272520a972291378698140Timo Sirainen{
4316355ca8b7698516272520a972291378698140Timo Sirainen size_t size, new_size, end_size;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen size = pool_get_exp_grown_size(fstream->ostream.iostream.pool,
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen fstream->buffer_size,
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen fstream->buffer_size + bytes);
0e5819a061034f1636b124c03a89f67d37c852b1Timo Sirainen if (size > fstream->max_buffer_size) {
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen /* limit the size */
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen size = fstream->max_buffer_size;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen } else if (fstream->corked) {
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen /* try to use optimal buffer size with corking */
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen new_size = I_MIN(fstream->optimal_block_size,
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen fstream->max_buffer_size);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen if (new_size > size)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen size = new_size;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen }
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen if (size <= fstream->buffer_size)
9b61a6db87c026656f8d2ae214e4486b98a069c0Timo Sirainen return;
9b61a6db87c026656f8d2ae214e4486b98a069c0Timo Sirainen
9b61a6db87c026656f8d2ae214e4486b98a069c0Timo Sirainen fstream->buffer = p_realloc(fstream->ostream.iostream.pool,
4316355ca8b7698516272520a972291378698140Timo Sirainen fstream->buffer,
4316355ca8b7698516272520a972291378698140Timo Sirainen fstream->buffer_size, size);
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen if (fstream->tail <= fstream->head && !IS_STREAM_EMPTY(fstream)) {
4316355ca8b7698516272520a972291378698140Timo Sirainen /* move head forward to end of buffer */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen end_size = fstream->buffer_size - fstream->head;
d19d3aa4eafa34b48b7d7d311c9db31e1898576aTimo Sirainen memmove(fstream->buffer + size - end_size,
d19d3aa4eafa34b48b7d7d311c9db31e1898576aTimo Sirainen fstream->buffer + fstream->head, end_size);
4316355ca8b7698516272520a972291378698140Timo Sirainen fstream->head = size - end_size;
4316355ca8b7698516272520a972291378698140Timo Sirainen }
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainen fstream->full = FALSE;
4316355ca8b7698516272520a972291378698140Timo Sirainen fstream->buffer_size = size;
4316355ca8b7698516272520a972291378698140Timo Sirainen}
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainenstatic void stream_send_io(void *context)
4316355ca8b7698516272520a972291378698140Timo Sirainen{
4316355ca8b7698516272520a972291378698140Timo Sirainen struct file_ostream *fstream = context;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen int ret;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen /* Set flush_pending = FALSE first before calling the flush callback,
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen and change it to TRUE only if callback returns 0. That way the
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen callback can call o_stream_set_flush_pending() again and we don't
4316355ca8b7698516272520a972291378698140Timo Sirainen forget it even if flush callback returns 1. */
4316355ca8b7698516272520a972291378698140Timo Sirainen fstream->flush_pending = FALSE;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen o_stream_ref(&fstream->ostream.ostream);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen if (fstream->ostream.callback != NULL)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen ret = fstream->ostream.callback(fstream->ostream.context);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen else
8a1c866a4c429f26c8746525f82024bc387f1407Timo Sirainen ret = _flush(&fstream->ostream);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (ret == 0)
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen fstream->flush_pending = TRUE;
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainen if (!fstream->flush_pending && IS_STREAM_EMPTY(fstream)) {
4316355ca8b7698516272520a972291378698140Timo Sirainen if (fstream->io != NULL) {
4316355ca8b7698516272520a972291378698140Timo Sirainen /* all sent */
4316355ca8b7698516272520a972291378698140Timo Sirainen io_remove(fstream->io);
4316355ca8b7698516272520a972291378698140Timo Sirainen fstream->io = NULL;
4316355ca8b7698516272520a972291378698140Timo Sirainen }
4316355ca8b7698516272520a972291378698140Timo Sirainen } else {
d19d3aa4eafa34b48b7d7d311c9db31e1898576aTimo Sirainen if (fstream->io == NULL) {
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen fstream->io = io_add(fstream->fd, IO_WRITE,
4316355ca8b7698516272520a972291378698140Timo Sirainen stream_send_io, fstream);
4316355ca8b7698516272520a972291378698140Timo Sirainen }
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen }
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen o_stream_unref(&fstream->ostream.ostream);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen}
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainenstatic size_t o_stream_add(struct file_ostream *fstream,
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen const void *data, size_t size)
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen{
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen size_t unused, sent;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen int i;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
6b43203878bd3c87f5ae690617a1cbc694e24c01Timo Sirainen unused = get_unused_space(fstream);
6b43203878bd3c87f5ae690617a1cbc694e24c01Timo Sirainen if (unused < size)
6b43203878bd3c87f5ae690617a1cbc694e24c01Timo Sirainen o_stream_grow_buffer(fstream, size-unused);
6b43203878bd3c87f5ae690617a1cbc694e24c01Timo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen sent = 0;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen for (i = 0; i < 2 && sent < size && !fstream->full; i++) {
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen unused = fstream->tail >= fstream->head ?
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen fstream->buffer_size - fstream->tail :
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen fstream->head - fstream->tail;
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen if (unused > size-sent)
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen unused = size-sent;
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen memcpy(fstream->buffer + fstream->tail,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen CONST_PTR_OFFSET(data, sent), unused);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen sent += unused;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
426e50e7647009bb22db67d9012043f0a59e7452Timo Sirainen fstream->tail += unused;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (fstream->tail == fstream->buffer_size)
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen fstream->tail = 0;
faeaff460d763bb4b574d31d78773f91aaba5510Timo Sirainen
faeaff460d763bb4b574d31d78773f91aaba5510Timo Sirainen if (fstream->head == fstream->tail)
faeaff460d763bb4b574d31d78773f91aaba5510Timo Sirainen fstream->full = TRUE;
faeaff460d763bb4b574d31d78773f91aaba5510Timo Sirainen }
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen if (sent != 0 && fstream->io == NULL &&
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen !fstream->corked && !fstream->file) {
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen fstream->io = io_add(fstream->fd, IO_WRITE, stream_send_io,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen fstream);
426e50e7647009bb22db67d9012043f0a59e7452Timo Sirainen }
426e50e7647009bb22db67d9012043f0a59e7452Timo Sirainen
426e50e7647009bb22db67d9012043f0a59e7452Timo Sirainen return sent;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen}
b089505afb8b5c5dfb43ef88af0ea7e0920db587Timo Sirainen
b089505afb8b5c5dfb43ef88af0ea7e0920db587Timo Sirainenstatic ssize_t _sendv(struct _ostream *stream, const struct const_iovec *iov,
426e50e7647009bb22db67d9012043f0a59e7452Timo Sirainen unsigned int iov_count)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen{
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen struct file_ostream *fstream = (struct file_ostream *)stream;
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen size_t size, added, optimal_size;
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen unsigned int i;
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen ssize_t ret = 0;
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen stream->ostream.stream_errno = 0;
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen for (i = 0, size = 0; i < iov_count; i++)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen size += iov[i].iov_len;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (size > get_unused_space(fstream) && !IS_STREAM_EMPTY(fstream)) {
426e50e7647009bb22db67d9012043f0a59e7452Timo Sirainen if (_flush(stream) < 0)
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen return -1;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen }
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen optimal_size = I_MIN(fstream->optimal_block_size,
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen fstream->max_buffer_size);
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen if (IS_STREAM_EMPTY(fstream) &&
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen (!fstream->corked || size >= optimal_size)) {
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen /* send immediately */
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen ret = o_stream_writev(fstream, iov, iov_count);
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen if (ret < 0)
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen return -1;
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen size = ret;
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen while (size > 0 && size >= iov[0].iov_len) {
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen size -= iov[0].iov_len;
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen iov++;
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen iov_count--;
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen }
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen if (iov_count > 0) {
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen added = o_stream_add(fstream,
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen CONST_PTR_OFFSET(iov[0].iov_base, size),
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen iov[0].iov_len - size);
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen ret += added;
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen if (added != iov[0].iov_len - size) {
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen /* buffer full */
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen stream->ostream.offset += ret;
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen return ret;
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen }
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen iov++;
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen iov_count--;
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen }
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen }
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen /* buffer it, at least partly */
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen for (i = 0; i < iov_count; i++) {
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen added = o_stream_add(fstream, iov[i].iov_base, iov[i].iov_len);
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen ret += added;
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen if (added != iov[i].iov_len)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen break;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen stream->ostream.offset += ret;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return ret;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen}
ccd44abfe14f51cc1f6d8c0ec1aa6dc31242e2d3Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenstatic off_t io_stream_sendfile(struct _ostream *outstream,
de5c7c99783cd86f3bdbc057345cbee923b51a20Timo Sirainen struct istream *instream,
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen int in_fd, uoff_t in_size)
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen{
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen struct file_ostream *foutstream = (struct file_ostream *)outstream;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen uoff_t start_offset;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen uoff_t offset, send_size, v_offset;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen ssize_t ret;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* flush out any data in buffer */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if ((ret = buffer_flush(foutstream)) <= 0)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return ret;
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen start_offset = v_offset = instream->v_offset;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen do {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen offset = instream->real_stream->abs_start_offset + v_offset;
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen send_size = in_size - v_offset;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen ret = safe_sendfile(foutstream->fd, in_fd, &offset,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen MAX_SSIZE_T(send_size));
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (ret <= 0) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (ret == 0 || errno == EINTR || errno == EAGAIN) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen ret = 0;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen break;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (errno == EINVAL) i_error("io_stream_sendfile() -> EINVAL");
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen outstream->ostream.stream_errno = errno;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (errno != EINVAL) {
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen /* close only if error wasn't because
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen sendfile() isn't supported */
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen stream_closed(foutstream);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen }
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen break;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen }
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen v_offset += ret;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen outstream->ostream.offset += ret;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen } while ((uoff_t)ret != send_size);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen i_stream_seek(instream, v_offset);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return ret < 0 ? -1 : (off_t)(instream->v_offset - start_offset);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen}
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic off_t io_stream_copy(struct _ostream *outstream,
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen struct istream *instream, uoff_t in_size)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct file_ostream *foutstream = (struct file_ostream *)outstream;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen uoff_t start_offset;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen struct const_iovec iov[3];
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen int iov_len;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen const unsigned char *data;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen size_t size, skip_size, block_size;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen ssize_t ret;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen int pos;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen iov_len = o_stream_fill_iovec(foutstream, iov);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen skip_size = 0;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen for (pos = 0; pos < iov_len; pos++)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen skip_size += iov[pos].iov_len;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen start_offset = instream->v_offset;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen in_size -= instream->v_offset;
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen while (in_size > 0) {
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen block_size = I_MIN(foutstream->optimal_block_size, in_size);
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen (void)i_stream_read_data(instream, &data, &size, block_size-1);
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen in_size -= size;
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen if (size == 0) {
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen /* all sent */
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen break;
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen }
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen
766115d2b2e6dbcf59f90d3b3866851cf6f740feTimo Sirainen pos = iov_len++;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen iov[pos].iov_base = (void *) data;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen iov[pos].iov_len = size;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen ret = o_stream_writev(foutstream, iov, iov_len);
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen if (ret < 0)
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen return -1;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen if (skip_size > 0) {
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen if ((size_t)ret < skip_size) {
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen update_buffer(foutstream, ret);
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen skip_size -= ret;
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen ret = 0;
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen } else {
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen update_buffer(foutstream, skip_size);
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen ret -= skip_size;
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen skip_size = 0;
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen }
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen }
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen outstream->ostream.offset += ret;
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen i_stream_skip(instream, ret);
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen if ((size_t)ret != iov[pos].iov_len)
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen break;
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen i_assert(skip_size == 0);
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen iov_len = 0;
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return (off_t) (instream->v_offset - start_offset);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen}
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic off_t io_stream_copy_backwards(struct _ostream *outstream,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct istream *instream, uoff_t in_size)
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen{
169b679209f7b3e97888704ad3693c709265ef24Timo Sirainen struct file_ostream *foutstream = (struct file_ostream *)outstream;
169b679209f7b3e97888704ad3693c709265ef24Timo Sirainen uoff_t in_start_offset, in_offset, in_limit, out_offset;
169b679209f7b3e97888704ad3693c709265ef24Timo Sirainen const unsigned char *data;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen size_t buffer_size, size, read_size;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen ssize_t ret;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_assert(IS_STREAM_EMPTY(foutstream));
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen /* figure out optimal buffer size */
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen buffer_size = instream->real_stream->buffer_size;
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen if (buffer_size == 0 || buffer_size > foutstream->buffer_size) {
0e5819a061034f1636b124c03a89f67d37c852b1Timo Sirainen if (foutstream->optimal_block_size > foutstream->buffer_size) {
0e5819a061034f1636b124c03a89f67d37c852b1Timo Sirainen o_stream_grow_buffer(foutstream,
0e5819a061034f1636b124c03a89f67d37c852b1Timo Sirainen foutstream->optimal_block_size -
0e5819a061034f1636b124c03a89f67d37c852b1Timo Sirainen foutstream->buffer_size);
0e5819a061034f1636b124c03a89f67d37c852b1Timo Sirainen }
0e5819a061034f1636b124c03a89f67d37c852b1Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen buffer_size = foutstream->buffer_size;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen in_start_offset = instream->v_offset;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen in_offset = in_limit = in_size;
0e5819a061034f1636b124c03a89f67d37c852b1Timo Sirainen out_offset = outstream->ostream.offset + (in_offset - in_start_offset);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen while (in_offset > in_start_offset) {
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen if (in_offset - in_start_offset <= buffer_size)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen read_size = in_offset - in_start_offset;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen else
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen read_size = buffer_size;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen in_offset -= read_size;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen out_offset -= read_size;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen for (;;) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen i_assert(in_offset <= in_limit);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen i_stream_seek(instream, in_offset);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen read_size = in_limit - in_offset;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen (void)i_stream_read_data(instream, &data, &size,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen read_size-1);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (size >= read_size) {
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen size = read_size;
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen if (instream->mmaped) {
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen /* we'll have to write it through
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen buffer or the file gets corrupted */
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen i_assert(size <=
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen foutstream->buffer_size);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen memcpy(foutstream->buffer, data, size);
169b679209f7b3e97888704ad3693c709265ef24Timo Sirainen data = foutstream->buffer;
169b679209f7b3e97888704ad3693c709265ef24Timo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen break;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen }
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* buffer too large probably, try with smaller */
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen read_size -= size;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen in_offset += read_size;
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen out_offset += read_size;
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen buffer_size -= read_size;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen in_limit -= size;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (o_stream_seek(&outstream->ostream, out_offset) < 0)
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen return -1;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen ret = write_full(foutstream->fd, data, size);
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen if (ret < 0) {
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen /* error */
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen if (errno == EINVAL) i_error("copy backwards -> EINVAL");
3f7583189249a28892cd5bd69e15681c7a8a619bTimo Sirainen outstream->ostream.stream_errno = errno;
3f7583189249a28892cd5bd69e15681c7a8a619bTimo Sirainen return -1;
3f7583189249a28892cd5bd69e15681c7a8a619bTimo Sirainen }
3f7583189249a28892cd5bd69e15681c7a8a619bTimo Sirainen }
3f7583189249a28892cd5bd69e15681c7a8a619bTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return (off_t) (in_size - in_start_offset);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic off_t _send_istream(struct _ostream *outstream, struct istream *instream)
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct file_ostream *foutstream = (struct file_ostream *)outstream;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen const struct stat *st;
68d7af0bb1d5f9da9dffda7d0616e99624d784e6Timo Sirainen uoff_t in_size;
68d7af0bb1d5f9da9dffda7d0616e99624d784e6Timo Sirainen off_t ret;
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen int in_fd, overlapping;
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen st = i_stream_stat(instream);
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen if (st == NULL) {
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen if (errno == EINVAL) i_error("_send_istream() / stat -> EINVAL");
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen outstream->ostream.stream_errno = instream->stream_errno;
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen return -1;
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen }
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen in_fd = i_stream_get_fd(instream);
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen in_size = st->st_size;
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen i_assert(instream->v_offset <= in_size);
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen outstream->ostream.stream_errno = 0;
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen if (in_fd != foutstream->fd)
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen overlapping = 0;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen else {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* copying data within same fd. we'll have to be careful with
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen seeks and overlapping writes. */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (in_size == (uoff_t)-1) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_error("_send_istream() / in_size == -1 -> EINVAL");
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen outstream->ostream.stream_errno = EINVAL;
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen return -1;
e265563be2031bc1f9b4ef15888e31a94a344e5eTimo Sirainen }
e265563be2031bc1f9b4ef15888e31a94a344e5eTimo Sirainen
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen ret = (off_t)outstream->ostream.offset -
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen (off_t)(instream->real_stream->abs_start_offset +
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen instream->v_offset);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen if (ret == 0) {
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen /* copying data over itself. we don't really
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen need to do that, just fake it. */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return in_size - instream->v_offset;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen }
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen overlapping = ret < 0 ? -1 : 1;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen }
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen if (!foutstream->no_sendfile && in_fd != -1 && overlapping <= 0) {
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen ret = io_stream_sendfile(outstream, instream, in_fd, in_size);
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen if (ret >= 0 || outstream->ostream.stream_errno != EINVAL)
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen return ret;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen /* sendfile() not supported (with this fd), fallback to
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen regular sending. */
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen outstream->ostream.stream_errno = 0;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen foutstream->no_sendfile = TRUE;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen }
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen if (overlapping <= 0)
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen return io_stream_copy(outstream, instream, in_size);
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen else
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen return io_stream_copy_backwards(outstream, instream, in_size);
9346506a9f4dd9a6285fe8595588e73161849235Timo Sirainen}
9346506a9f4dd9a6285fe8595588e73161849235Timo Sirainen
9346506a9f4dd9a6285fe8595588e73161849235Timo Sirainenstruct ostream *
9346506a9f4dd9a6285fe8595588e73161849235Timo Siraineno_stream_create_file(int fd, pool_t pool, size_t max_buffer_size,
9346506a9f4dd9a6285fe8595588e73161849235Timo Sirainen int autoclose_fd)
9346506a9f4dd9a6285fe8595588e73161849235Timo Sirainen{
9346506a9f4dd9a6285fe8595588e73161849235Timo Sirainen struct file_ostream *fstream;
9346506a9f4dd9a6285fe8595588e73161849235Timo Sirainen struct ostream *ostream;
9346506a9f4dd9a6285fe8595588e73161849235Timo Sirainen struct stat st;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen off_t offset;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen fstream = p_new(pool, struct file_ostream, 1);
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen fstream->fd = fd;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen fstream->max_buffer_size = max_buffer_size;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen fstream->autoclose_fd = autoclose_fd;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen fstream->optimal_block_size = DEFAULT_OPTIMAL_BLOCK_SIZE;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen fstream->ostream.iostream.close = _close;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen fstream->ostream.iostream.destroy = _destroy;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen fstream->ostream.iostream.set_max_buffer_size = _set_max_buffer_size;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen fstream->ostream.cork = _cork;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen fstream->ostream.flush = _flush;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen fstream->ostream.flush_pending = _flush_pending;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen fstream->ostream.get_used_size = _get_used_size;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen fstream->ostream.seek = _seek;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen fstream->ostream.sendv = _sendv;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen fstream->ostream.send_istream = _send_istream;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen ostream = _o_stream_create(&fstream->ostream, pool);
6b43203878bd3c87f5ae690617a1cbc694e24c01Timo Sirainen
6b43203878bd3c87f5ae690617a1cbc694e24c01Timo Sirainen offset = lseek(fd, 0, SEEK_CUR);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (offset >= 0) {
30ad2b0309119501efad06c72ec9b1561b90d4afTimo Sirainen ostream->offset = offset;
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen
30ad2b0309119501efad06c72ec9b1561b90d4afTimo Sirainen if (fstat(fd, &st) == 0) {
97daba82224dd757b7b7526ab3fd5d574a5f35d8Timo Sirainen if ((uoff_t)st.st_blksize >
10ccd0e45768923d69be459e87ef6cd2574cec60Timo Sirainen fstream->optimal_block_size) {
10ccd0e45768923d69be459e87ef6cd2574cec60Timo Sirainen /* use the optimal block size, but with a
97daba82224dd757b7b7526ab3fd5d574a5f35d8Timo Sirainen reasonable limit */
97daba82224dd757b7b7526ab3fd5d574a5f35d8Timo Sirainen fstream->optimal_block_size =
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen I_MIN(st.st_blksize,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen MAX_OPTIMAL_BLOCK_SIZE);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen if (S_ISREG(st.st_mode)) {
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen fstream->no_socket_cork = TRUE;
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen fstream->file = TRUE;
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen }
269370f66e320a071700e33d9f2584eb46d96a90Timo Sirainen }
50ecf65993bff429af04deef6c832deb019c76a5Timo Sirainen fstream->no_sendfile = TRUE;
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen } else {
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen if (net_getsockname(fd, NULL, NULL) < 0) {
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen fstream->no_sendfile = TRUE;
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen fstream->no_socket_cork = TRUE;
e66cd209fcab4817d2234d0121b404925dc60034Timo Sirainen }
50ecf65993bff429af04deef6c832deb019c76a5Timo Sirainen }
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen if (max_buffer_size == 0)
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen fstream->max_buffer_size = fstream->optimal_block_size;
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen return ostream;
d5d23d5ff8b7a06d2ead489ddcf55ee8fb5ca7b6Timo Sirainen}
d5d23d5ff8b7a06d2ead489ddcf55ee8fb5ca7b6Timo Sirainen