istream-file.c revision b3142c8e513bc78da821fa70f479016148fa95e5
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2002-2003 Timo Sirainen */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen/* @UNSAFE: whole file */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "lib.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "alarm-hup.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "istream-internal.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "network.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#include <time.h>
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include <unistd.h>
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include <sys/stat.h>
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#define I_STREAM_MIN_SIZE 4096
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#define STREAM_IS_BLOCKING(fstream) \
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ((fstream)->timeout_msecs != 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstruct file_istream {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct _istream istream;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen size_t max_buffer_size;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen uoff_t skip_left;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen int timeout_msecs;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen void (*timeout_cb)(void *);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen void *timeout_context;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int file:1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int autoclose_fd:1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen};
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void _close(struct _iostream *stream)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct _istream *_stream = (struct _istream *) stream;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (fstream->autoclose_fd && _stream->fd != -1) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (close(_stream->fd) < 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_error("file_istream.close() failed: %m");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen _stream->fd = -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void _destroy(struct _iostream *stream)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
7631f16156aca373004953fe6b01a7f343fb47e0Timo Sirainen struct _istream *_stream = (struct _istream *) stream;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen p_free(_stream->iostream.pool, _stream->w_buffer);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void _set_max_buffer_size(struct _iostream *stream, size_t max_size)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen fstream->max_buffer_size = max_size;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void _set_blocking(struct _iostream *stream, int timeout_msecs,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen void (*timeout_cb)(void *), void *context)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen fstream->timeout_msecs = timeout_msecs;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen fstream->timeout_cb = timeout_cb;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen fstream->timeout_context = context;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen net_set_nonblock(fstream->istream.fd, timeout_msecs == 0);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (timeout_msecs != 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen alarm_hup_init();
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void i_stream_grow_buffer(struct _istream *stream, size_t bytes)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen size_t old_size;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen old_size = stream->buffer_size;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen stream->buffer_size = stream->pos + bytes;
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen if (stream->buffer_size <= I_STREAM_MIN_SIZE)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen stream->buffer_size = I_STREAM_MIN_SIZE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen else
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen stream->buffer_size = nearest_power(stream->buffer_size);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (fstream->max_buffer_size > 0 &&
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen stream->buffer_size > fstream->max_buffer_size)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen stream->buffer_size = fstream->max_buffer_size;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen stream->buffer = stream->w_buffer =
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen p_realloc(stream->iostream.pool, stream->w_buffer,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen old_size, stream->buffer_size);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void i_stream_compress(struct _istream *stream)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen memmove(stream->w_buffer, stream->w_buffer + stream->skip,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen stream->pos - stream->skip);
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen stream->pos -= stream->skip;
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen stream->skip = 0;
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic ssize_t _read(struct _istream *stream)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
c3d9da3955043aef88c17b71f2081e894186aa6bTimo Sirainen time_t timeout_time;
c3d9da3955043aef88c17b71f2081e894186aa6bTimo Sirainen size_t size;
c3d9da3955043aef88c17b71f2081e894186aa6bTimo Sirainen ssize_t ret;
c3d9da3955043aef88c17b71f2081e894186aa6bTimo Sirainen
c3d9da3955043aef88c17b71f2081e894186aa6bTimo Sirainen if (stream->istream.closed)
c3d9da3955043aef88c17b71f2081e894186aa6bTimo Sirainen return -1;
c3d9da3955043aef88c17b71f2081e894186aa6bTimo Sirainen
c3d9da3955043aef88c17b71f2081e894186aa6bTimo Sirainen stream->istream.stream_errno = 0;
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen if (stream->pos == stream->buffer_size) {
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen if (stream->skip > 0) {
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen /* remove the unused bytes from beginning of buffer */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_stream_compress(stream);
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen } else if (fstream->max_buffer_size == 0 ||
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen stream->buffer_size < fstream->max_buffer_size) {
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen /* buffer is full - grow it */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_stream_grow_buffer(stream, I_STREAM_MIN_SIZE);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (stream->pos == stream->buffer_size)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -2; /* buffer full */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen size = stream->buffer_size - stream->pos;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen timeout_time = GET_TIMEOUT_TIME(fstream);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ret = -1;
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen do {
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen if (ret == 0 && timeout_time > 0 && time(NULL) > timeout_time) {
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen /* timeouted */
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen if (fstream->timeout_cb != NULL)
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen fstream->timeout_cb(fstream->timeout_context);
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen stream->istream.stream_errno = EAGAIN;
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen return -1;
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen if (fstream->file) {
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen ret = pread(stream->fd,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen stream->w_buffer + stream->pos, size,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen stream->istream.v_offset +
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (stream->pos - stream->skip));
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen } else {
7cd7bd65aba6d84f4e4f5066d248437eaa4e5e54Timo Sirainen ret = read(stream->fd,
a8bc64d2ec8babb5109fa23aa3c90383de61cd69Timo Sirainen stream->w_buffer + stream->pos, size);
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen }
a8bc64d2ec8babb5109fa23aa3c90383de61cd69Timo Sirainen if (ret == 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* EOF */
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen stream->istream.eof = TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen if (ret < 0) {
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen if (errno == ECONNRESET || errno == ETIMEDOUT) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* treat as disconnection */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen stream->istream.eof = TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (errno == EINTR || errno == EAGAIN)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ret = 0;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen else {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen stream->istream.stream_errno = errno;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (ret > 0 && fstream->skip_left > 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_assert(!fstream->file);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(stream->skip == stream->pos);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (fstream->skip_left >= (size_t)ret) {
fstream->skip_left -= ret;
ret = 0;
} else {
ret -= fstream->skip_left;
stream->pos += fstream->skip_left;
stream->skip += fstream->skip_left;
fstream->skip_left = 0;
}
}
} while (ret == 0 && STREAM_IS_BLOCKING(fstream));
stream->pos += ret;
return ret;
}
static void _seek(struct _istream *stream, uoff_t v_offset)
{
struct file_istream *fstream = (struct file_istream *) stream;
if (!fstream->file) {
if (v_offset < stream->istream.v_offset) {
stream->istream.stream_errno = ESPIPE;
return;
}
fstream->skip_left += v_offset - stream->istream.v_offset;
}
stream->istream.stream_errno = 0;
stream->istream.v_offset = v_offset;
stream->skip = stream->pos = 0;
}
static uoff_t _get_size(struct _istream *stream)
{
struct file_istream *fstream = (struct file_istream *) stream;
struct stat st;
if (fstream->file && fstat(stream->fd, &st) == 0 && S_ISREG(st.st_mode))
return (uoff_t)st.st_size;
else
return (uoff_t)-1;
}
struct istream *i_stream_create_file(int fd, pool_t pool,
size_t max_buffer_size, int autoclose_fd)
{
struct file_istream *fstream;
struct stat st;
fstream = p_new(pool, struct file_istream, 1);
fstream->max_buffer_size = max_buffer_size;
fstream->autoclose_fd = autoclose_fd;
fstream->istream.iostream.close = _close;
fstream->istream.iostream.destroy = _destroy;
fstream->istream.iostream.set_max_buffer_size = _set_max_buffer_size;
fstream->istream.iostream.set_blocking = _set_blocking;
fstream->istream.read = _read;
fstream->istream.seek = _seek;
fstream->istream.get_size = _get_size;
/* get size of fd if it's a file */
if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode))
fstream->file = TRUE;
return _i_stream_create(&fstream->istream, pool, fd, 0);
}