ostream-file.c revision 4c52424aaaa0e1ad5a63a5e5540767634f207806
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen/* Copyright (c) 2002-2003 Timo Sirainen */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen/* @UNSAFE: whole file */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "lib.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "ioloop.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "write-full.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "network.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "sendfile-util.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "istream.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "istream-internal.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "ostream-internal.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
53dfcefa9440a49d703e49193819a79be99c9ba6Timo Sirainen#include <unistd.h>
53dfcefa9440a49d703e49193819a79be99c9ba6Timo Sirainen#include <sys/stat.h>
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#ifdef HAVE_SYS_UIO_H
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen# include <sys/uio.h>
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#endif
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#ifndef UIO_MAXIOV
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen# define UIO_MAXIOV 16
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#endif
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen/* try to keep the buffer size within 4k..128k. ReiserFS may actually return
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen 128k as optimal size. */
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen#define DEFAULT_OPTIMAL_BLOCK_SIZE 4096
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#define MAX_OPTIMAL_BLOCK_SIZE (128*1024)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#define IS_STREAM_EMPTY(fstream) \
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ((fstream)->head == (fstream)->tail && !(fstream)->full)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#define MAX_SSIZE_T(size) \
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ((size) < SSIZE_T_MAX ? (size_t)(size) : SSIZE_T_MAX)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstruct file_ostream {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct _ostream ostream;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen int fd;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct io *io;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned char *buffer; /* ring-buffer */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size_t buffer_size, max_buffer_size, optimal_block_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size_t head, tail; /* first unsent/unused byte */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned int full:1; /* if head == tail, is buffer empty or full? */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned int file:1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned int corked:1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned int no_socket_cork:1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned int no_sendfile:1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned int autoclose_fd:1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen};
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void stream_send_io(void *context);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void stream_closed(struct file_ostream *fstream)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (fstream->autoclose_fd && fstream->fd != -1) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (close(fstream->fd) < 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_error("file_ostream.close() failed: %m");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->fd = -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (fstream->io != NULL) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen io_remove(fstream->io);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->io = NULL;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->ostream.ostream.closed = TRUE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void _close(struct _iostream *stream)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct file_ostream *fstream = (struct file_ostream *)stream;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* flush output before really closing it */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen o_stream_flush(&fstream->ostream.ostream);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen stream_closed(fstream);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void _destroy(struct _iostream *stream)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct file_ostream *fstream = (struct file_ostream *)stream;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen p_free(fstream->ostream.iostream.pool, fstream->buffer);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void _set_max_buffer_size(struct _iostream *stream, size_t max_size)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct file_ostream *fstream = (struct file_ostream *)stream;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->max_buffer_size = max_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void update_buffer(struct file_ostream *fstream, size_t size)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size_t used;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (IS_STREAM_EMPTY(fstream))
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (fstream->head < fstream->tail) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* ...HXXXT... */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen used = fstream->tail - fstream->head;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_assert(size <= used);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->head += size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* XXXT...HXXX */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen used = fstream->buffer_size - fstream->head;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (size > used) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size -= used;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_assert(size <= fstream->tail);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->head = size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->head += size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->full = FALSE;
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen }
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (fstream->head == fstream->tail)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->head = fstream->tail = 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (fstream->head == fstream->buffer_size)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->head = 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic ssize_t o_stream_writev(struct file_ostream *fstream,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const struct const_iovec *iov, int iov_size)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ssize_t ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size_t size, sent;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen int i;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (iov_size == 1)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = write(fstream->fd, iov->iov_base, iov->iov_len);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen else {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen sent = 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen while (iov_size > UIO_MAXIOV) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size = 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen for (i = 0; i < UIO_MAXIOV; i++)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size += iov[i].iov_len;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = writev(fstream->fd, (const struct iovec *)iov,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen UIO_MAXIOV);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (ret != (ssize_t)size)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen break;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen sent += ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen iov += UIO_MAXIOV;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen iov_size -= UIO_MAXIOV;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (iov_size <= UIO_MAXIOV) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = writev(fstream->fd, (const struct iovec *)iov,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen iov_size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (ret > 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret += sent;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (ret < 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (errno == EAGAIN || errno == EINTR)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->ostream.ostream.stream_errno = errno;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen stream_closed(fstream);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen/* returns how much of vector was used */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int o_stream_fill_iovec(struct file_ostream *fstream,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct const_iovec iov[2])
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (IS_STREAM_EMPTY(fstream))
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (fstream->head < fstream->tail) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen iov[0].iov_base = fstream->buffer + fstream->head;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen iov[0].iov_len = fstream->tail - fstream->head;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return 1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen iov[0].iov_base = fstream->buffer + fstream->head;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen iov[0].iov_len = fstream->buffer_size - fstream->head;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (fstream->tail == 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return 1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen else {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen iov[1].iov_base = fstream->buffer;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen iov[1].iov_len = fstream->tail;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return 2;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int buffer_flush(struct file_ostream *fstream)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct const_iovec iov[2];
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen int iov_len;
53dfcefa9440a49d703e49193819a79be99c9ba6Timo Sirainen ssize_t ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen iov_len = o_stream_fill_iovec(fstream, iov);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (iov_len > 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = o_stream_writev(fstream, iov, iov_len);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (ret < 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen update_buffer(fstream, ret);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return IS_STREAM_EMPTY(fstream) ? 1 : 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void _cork(struct _ostream *stream, int set)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct file_ostream *fstream = (struct file_ostream *)stream;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (fstream->corked != set) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (!fstream->no_socket_cork) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (net_set_cork(fstream->fd, set) < 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->no_socket_cork = TRUE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->corked = set;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (set && fstream->io != NULL) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen io_remove(fstream->io);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->io = NULL;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else if (!set && fstream->io == NULL) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (fstream->file)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buffer_flush(fstream);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen else {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->io = io_add(fstream->fd, IO_WRITE,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen stream_send_io, fstream);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int _flush(struct _ostream *stream)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct file_ostream *fstream = (struct file_ostream *) stream;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return buffer_flush(fstream);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic size_t get_unused_space(struct file_ostream *fstream)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (fstream->head > fstream->tail) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* XXXT...HXXX */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return fstream->head - fstream->tail;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else if (fstream->head < fstream->tail) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* ...HXXXT... */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return (fstream->buffer_size - fstream->tail) + fstream->head;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* either fully unused or fully used */
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen return fstream->full ? 0 : fstream->buffer_size;
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic size_t _get_used_size(struct _ostream *stream)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct file_ostream *fstream = (struct file_ostream *)stream;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return fstream->buffer_size - get_unused_space(fstream);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
53dfcefa9440a49d703e49193819a79be99c9ba6Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int _seek(struct _ostream *stream, uoff_t offset)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
04b7dc631f33bf61f273138c679da9bd0910fb6dTimo Sirainen struct file_ostream *fstream = (struct file_ostream *)stream;
04b7dc631f33bf61f273138c679da9bd0910fb6dTimo Sirainen off_t ret;
04b7dc631f33bf61f273138c679da9bd0910fb6dTimo Sirainen
04b7dc631f33bf61f273138c679da9bd0910fb6dTimo Sirainen if (offset > OFF_T_MAX) {
04b7dc631f33bf61f273138c679da9bd0910fb6dTimo Sirainen stream->ostream.stream_errno = EINVAL;
04b7dc631f33bf61f273138c679da9bd0910fb6dTimo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (buffer_flush(fstream) < 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = lseek(fstream->fd, (off_t)offset, SEEK_SET);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (ret < 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen stream->ostream.stream_errno = errno;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (ret != (off_t)offset) {
53dfcefa9440a49d703e49193819a79be99c9ba6Timo Sirainen stream->ostream.stream_errno = EINVAL;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen stream->ostream.stream_errno = 0;
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen stream->ostream.offset = offset;
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen return 1;
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen}
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainenstatic void o_stream_grow_buffer(struct file_ostream *fstream, size_t bytes)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen size_t size, head_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size = nearest_power(fstream->buffer_size + bytes);
53dfcefa9440a49d703e49193819a79be99c9ba6Timo Sirainen if (size > fstream->max_buffer_size) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* limit the size */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size = fstream->max_buffer_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else if (fstream->corked) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* use optimal buffer size with corking */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size = I_MIN(fstream->optimal_block_size,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->max_buffer_size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (size <= fstream->buffer_size)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->buffer = p_realloc(fstream->ostream.iostream.pool,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->buffer,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->buffer_size, size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (fstream->tail <= fstream->head && !IS_STREAM_EMPTY(fstream)) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen head_size = I_MIN(fstream->head, size - fstream->buffer_size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen memcpy(fstream->buffer + fstream->buffer_size, fstream->buffer,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen head_size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (head_size == fstream->head)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->tail = fstream->buffer_size + head_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen else {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen memmove(fstream->buffer, fstream->buffer + head_size,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->head - head_size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->tail = fstream->head - head_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->full = FALSE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->buffer_size = size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void stream_send_io(void *context)
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen{
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen struct file_ostream *fstream = context;
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen if (fstream->ostream.callback != NULL)
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen fstream->ostream.callback(fstream->ostream.context);
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen else {
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen if (_flush(&fstream->ostream) <= 0)
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen return;
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen }
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen if (IS_STREAM_EMPTY(fstream) && fstream->io != NULL) {
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen /* all sent */
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen io_remove(fstream->io);
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen fstream->io = NULL;
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen }
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen}
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainenstatic size_t o_stream_add(struct file_ostream *fstream,
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen const void *data, size_t size)
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size_t unused, sent;
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen int i;
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen unused = get_unused_space(fstream);
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen if (unused < size)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen o_stream_grow_buffer(fstream, size-unused);
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen sent = 0;
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen for (i = 0; i < 2 && sent < size && !fstream->full; i++) {
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen unused = fstream->tail >= fstream->head ?
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen fstream->buffer_size - fstream->tail :
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen fstream->head - fstream->tail;
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen if (unused > size-sent)
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen unused = size-sent;
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen memcpy(fstream->buffer + fstream->tail,
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen CONST_PTR_OFFSET(data, sent), unused);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen sent += unused;
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->tail += unused;
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen if (fstream->tail == fstream->buffer_size)
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen fstream->tail = 0;
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen if (fstream->head == fstream->tail)
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen fstream->full = TRUE;
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen }
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen if (sent != 0 && fstream->io == NULL &&
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen !fstream->corked && !fstream->file) {
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen fstream->io = io_add(fstream->fd, IO_WRITE, stream_send_io,
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen fstream);
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen }
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen return sent;
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen}
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainenstatic ssize_t _sendv(struct _ostream *stream, const struct const_iovec *iov,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size_t iov_count)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen struct file_ostream *fstream = (struct file_ostream *)stream;
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen size_t i, size, added, optimal_size;
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen ssize_t ret = 0;
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen stream->ostream.stream_errno = 0;
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen for (i = 0, size = 0; i < iov_count; i++)
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen size += iov[i].iov_len;
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen if (size > get_unused_space(fstream)) {
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen if (_flush(stream) < 0)
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen return -1;
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen }
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen optimal_size = I_MIN(fstream->optimal_block_size,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fstream->max_buffer_size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (IS_STREAM_EMPTY(fstream) &&
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen (!fstream->corked || size >= optimal_size)) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* send immediately */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = o_stream_writev(fstream, iov, iov_count);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (ret < 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size = ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen while (size > 0 && size >= iov[0].iov_len) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size -= iov[0].iov_len;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen iov++;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen iov_count--;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (iov_count > 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen added = o_stream_add(fstream,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen CONST_PTR_OFFSET(iov[0].iov_base, size),
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen iov[0].iov_len - size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret += added;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (added != iov[0].iov_len - size) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* buffer full */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen stream->ostream.offset += ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen iov++;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen iov_count--;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* buffer it, at least partly */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen for (i = 0; i < iov_count; i++) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen added = o_stream_add(fstream, iov[i].iov_base, iov[i].iov_len);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret += added;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (added != iov[i].iov_len)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen break;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen stream->ostream.offset += ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic off_t io_stream_sendfile(struct _ostream *outstream,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct istream *instream,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen int in_fd, uoff_t in_size)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct file_ostream *foutstream = (struct file_ostream *)outstream;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen uoff_t start_offset;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen uoff_t offset, send_size, v_offset;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ssize_t ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* flush out any data in buffer */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if ((ret = buffer_flush(foutstream)) <= 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen start_offset = v_offset = instream->v_offset;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen do {
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen offset = instream->real_stream->abs_start_offset + v_offset;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen send_size = in_size - v_offset;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = safe_sendfile(foutstream->fd, in_fd, &offset,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen MAX_SSIZE_T(send_size));
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen if (ret <= 0) {
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen if (ret == 0 || errno == EINTR || errno == EAGAIN) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen break;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen outstream->ostream.stream_errno = errno;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (errno != EINVAL) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* close only if error wasn't because
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen sendfile() isn't supported */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen stream_closed(foutstream);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen break;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainen v_offset += ret;
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainen outstream->ostream.offset += ret;
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainen } while ((uoff_t)ret != send_size);
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainen
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainen i_stream_seek(instream, v_offset);
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainen return ret < 0 ? -1 : (off_t)(instream->v_offset - start_offset);
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainen}
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainen
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainenstatic off_t io_stream_copy(struct _ostream *outstream,
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainen struct istream *instream, uoff_t in_size)
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainen{
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainen struct file_ostream *foutstream = (struct file_ostream *)outstream;
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen uoff_t start_offset;
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen struct const_iovec iov[3];
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen int iov_len;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const unsigned char *data;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size_t size, skip_size, block_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ssize_t ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen int pos;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen iov_len = o_stream_fill_iovec(foutstream, iov);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen skip_size = 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen for (pos = 0; pos < iov_len; pos++)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen skip_size += iov[pos].iov_len;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen start_offset = instream->v_offset;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen while (in_size > 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen block_size = I_MIN(foutstream->optimal_block_size, in_size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen (void)i_stream_read_data(instream, &data, &size, block_size-1);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen in_size -= size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (size == 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* all sent */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen break;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen pos = iov_len++;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen iov[pos].iov_base = (void *) data;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen iov[pos].iov_len = size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = o_stream_writev(foutstream, iov, iov_len);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (ret < 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (skip_size > 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if ((size_t)ret < skip_size) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen update_buffer(foutstream, ret);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen skip_size -= ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen update_buffer(foutstream, skip_size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret -= skip_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen skip_size = 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen outstream->ostream.offset += ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_stream_skip(instream, ret);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if ((size_t)ret != iov[pos].iov_len)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen break;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_assert(skip_size == 0);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen iov_len = 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return (off_t) (instream->v_offset - start_offset);
}
static off_t io_stream_copy_backwards(struct _ostream *outstream,
struct istream *instream, uoff_t in_size)
{
struct file_ostream *foutstream = (struct file_ostream *)outstream;
uoff_t in_start_offset, in_offset, in_limit, out_offset;
const unsigned char *data;
size_t buffer_size, size, read_size;
ssize_t ret;
i_assert(IS_STREAM_EMPTY(foutstream));
/* figure out optimal buffer size */
buffer_size = instream->real_stream->buffer_size;
if (buffer_size == 0 || buffer_size > foutstream->buffer_size) {
if (foutstream->optimal_block_size > foutstream->buffer_size) {
o_stream_grow_buffer(foutstream,
foutstream->optimal_block_size -
foutstream->buffer_size);
}
buffer_size = foutstream->buffer_size;
}
in_start_offset = instream->v_offset;
in_offset = in_limit = in_size;
out_offset = outstream->ostream.offset + (in_offset - in_start_offset);
while (in_offset > in_start_offset) {
if (in_offset - in_start_offset <= buffer_size)
read_size = in_offset - in_start_offset;
else
read_size = buffer_size;
in_offset -= read_size;
out_offset -= read_size;
for (;;) {
i_assert(in_offset <= in_limit);
i_stream_seek(instream, in_offset);
read_size = in_limit - in_offset;
(void)i_stream_read_data(instream, &data, &size,
read_size-1);
if (size >= read_size) {
size = read_size;
if (instream->mmaped) {
/* we'll have to write it through
buffer or the file gets corrupted */
i_assert(size <=
foutstream->buffer_size);
memcpy(foutstream->buffer, data, size);
data = foutstream->buffer;
}
break;
}
/* buffer too large probably, try with smaller */
read_size -= size;
in_offset += read_size;
out_offset += read_size;
buffer_size -= read_size;
}
in_limit -= size;
if (o_stream_seek(&outstream->ostream, out_offset) < 0)
return -1;
ret = write_full(foutstream->fd, data, size);
if (ret < 0) {
/* error */
outstream->ostream.stream_errno = errno;
return -1;
}
}
return (off_t) (in_size - in_start_offset);
}
static off_t _send_istream(struct _ostream *outstream, struct istream *instream)
{
struct file_ostream *foutstream = (struct file_ostream *)outstream;
uoff_t in_size;
off_t ret;
int in_fd, overlapping;
in_fd = i_stream_get_fd(instream);
in_size = i_stream_get_size(instream);
i_assert(instream->v_offset <= in_size);
outstream->ostream.stream_errno = 0;
if (in_fd != foutstream->fd)
overlapping = 0;
else {
/* copying data within same fd. we'll have to be careful with
seeks and overlapping writes. */
if (in_size == (uoff_t)-1) {
outstream->ostream.stream_errno = EINVAL;
return -1;
}
ret = (off_t)outstream->ostream.offset -
(off_t)(instream->real_stream->abs_start_offset +
instream->v_offset);
if (ret == 0) {
/* copying data over itself. we don't really
need to do that, just fake it. */
return in_size - instream->v_offset;
}
overlapping = ret < 0 ? -1 : 1;
}
if (!foutstream->no_sendfile && in_fd != -1 && overlapping <= 0) {
ret = io_stream_sendfile(outstream, instream, in_fd, in_size);
if (ret >= 0 || outstream->ostream.stream_errno != EINVAL)
return ret;
/* sendfile() not supported (with this fd), fallback to
regular sending. */
outstream->ostream.stream_errno = 0;
foutstream->no_sendfile = TRUE;
}
if (overlapping <= 0)
return io_stream_copy(outstream, instream, in_size);
else
return io_stream_copy_backwards(outstream, instream, in_size);
}
struct ostream *
o_stream_create_file(int fd, pool_t pool, size_t max_buffer_size,
int autoclose_fd)
{
struct file_ostream *fstream;
struct ostream *ostream;
struct stat st;
off_t offset;
fstream = p_new(pool, struct file_ostream, 1);
fstream->fd = fd;
fstream->max_buffer_size = max_buffer_size;
fstream->autoclose_fd = autoclose_fd;
fstream->optimal_block_size = DEFAULT_OPTIMAL_BLOCK_SIZE;
fstream->ostream.iostream.close = _close;
fstream->ostream.iostream.destroy = _destroy;
fstream->ostream.iostream.set_max_buffer_size = _set_max_buffer_size;
fstream->ostream.cork = _cork;
fstream->ostream.flush = _flush;
fstream->ostream.get_used_size = _get_used_size;
fstream->ostream.seek = _seek;
fstream->ostream.sendv = _sendv;
fstream->ostream.send_istream = _send_istream;
ostream = _o_stream_create(&fstream->ostream, pool);
offset = lseek(fd, 0, SEEK_CUR);
if (offset >= 0) {
ostream->offset = offset;
if (fstat(fd, &st) == 0) {
if ((uoff_t)st.st_blksize >
fstream->optimal_block_size) {
/* use the optimal block size, but with a
reasonable limit */
fstream->optimal_block_size =
I_MIN(st.st_blksize,
MAX_OPTIMAL_BLOCK_SIZE);
}
if (S_ISREG(st.st_mode)) {
fstream->no_socket_cork = TRUE;
fstream->file = TRUE;
}
}
#ifndef HAVE_LINUX_SENDFILE
/* only Linux supports sendfile() with non-sockets. Other
systems fail more or less gracefully if it's tried, so
don't bother to even try with them. */
fstream->no_sendfile = TRUE;
#endif
} else {
if (net_getsockname(fd, NULL, NULL) < 0) {
fstream->no_sendfile = TRUE;
fstream->no_socket_cork = TRUE;
}
}
if (max_buffer_size == 0)
fstream->max_buffer_size = fstream->optimal_block_size;
return ostream;
}