istream-file.c revision c06f4017027263cf3a08becc551f5126409e2a83
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2002-2003 Timo Sirainen */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen/* @UNSAFE: whole file */
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen#include "lib.h"
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen#include "ioloop.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "istream-internal.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "network.h"
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen#include <time.h>
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include <unistd.h>
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen#include <sys/stat.h>
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen#define I_STREAM_MIN_SIZE 4096
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainenstruct file_istream {
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen struct _istream istream;
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen struct timeval fstat_cache_stamp;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen size_t max_buffer_size;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen uoff_t skip_left;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainen unsigned int file:1;
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainen unsigned int autoclose_fd:1;
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainen};
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainen
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainenstatic void _close(struct _iostream *stream)
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainen{
9b7eeffb5752b500ac62ba1fd01c4a8c4ada14e9Timo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
9b7eeffb5752b500ac62ba1fd01c4a8c4ada14e9Timo Sirainen struct _istream *_stream = (struct _istream *) stream;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen
93fa87cf1a96c4f279ec4f5c311820313ba12c34Timo Sirainen if (fstream->autoclose_fd && _stream->fd != -1) {
43834f87bf431198f986e86052a4f6e558fdb07dTimo Sirainen if (close(_stream->fd) < 0)
43834f87bf431198f986e86052a4f6e558fdb07dTimo Sirainen i_error("file_istream.close() failed: %m");
93fa87cf1a96c4f279ec4f5c311820313ba12c34Timo Sirainen }
93fa87cf1a96c4f279ec4f5c311820313ba12c34Timo Sirainen _stream->fd = -1;
93fa87cf1a96c4f279ec4f5c311820313ba12c34Timo Sirainen}
93fa87cf1a96c4f279ec4f5c311820313ba12c34Timo Sirainen
43834f87bf431198f986e86052a4f6e558fdb07dTimo Sirainenstatic void _destroy(struct _iostream *stream)
b565a6a7a66fb9f224d00c06a950e3c1c585c18eTimo Sirainen{
b565a6a7a66fb9f224d00c06a950e3c1c585c18eTimo Sirainen struct _istream *_stream = (struct _istream *) stream;
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen p_free(_stream->iostream.pool, _stream->w_buffer);
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen}
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainenstatic void _set_max_buffer_size(struct _iostream *stream, size_t max_size)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen{
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen fstream->max_buffer_size = max_size;
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen}
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen
8b247780e911909a9fdc47f69ce6d1478902ad98Timo Sirainenstatic void i_stream_grow_buffer(struct _istream *stream, size_t bytes)
8b247780e911909a9fdc47f69ce6d1478902ad98Timo Sirainen{
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen size_t old_size;
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen
8b247780e911909a9fdc47f69ce6d1478902ad98Timo Sirainen old_size = stream->buffer_size;
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen stream->buffer_size = stream->pos + bytes;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (stream->buffer_size <= I_STREAM_MIN_SIZE)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen stream->buffer_size = I_STREAM_MIN_SIZE;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen else {
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen stream->buffer_size =
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen pool_get_exp_grown_size(stream->iostream.pool,
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen old_size, stream->buffer_size);
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen }
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen if (fstream->max_buffer_size > 0 &&
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen stream->buffer_size > fstream->max_buffer_size)
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen stream->buffer_size = fstream->max_buffer_size;
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen stream->buffer = stream->w_buffer =
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen p_realloc(stream->iostream.pool, stream->w_buffer,
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen old_size, stream->buffer_size);
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainenstatic void i_stream_compress(struct _istream *stream)
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen{
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen memmove(stream->w_buffer, stream->w_buffer + stream->skip,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen stream->pos - stream->skip);
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainen stream->pos -= stream->skip;
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainen
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen stream->skip = 0;
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainen}
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainenstatic ssize_t _read(struct _istream *stream)
27a44fcfd8d19bffe0f267f20a2b5d3fe7600fddTimo Sirainen{
27a44fcfd8d19bffe0f267f20a2b5d3fe7600fddTimo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
27a44fcfd8d19bffe0f267f20a2b5d3fe7600fddTimo Sirainen size_t size;
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen ssize_t ret;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen if (stream->istream.closed)
556f95092c3bc850517d5ab2bb502024a55645f1Timo Sirainen return -1;
556f95092c3bc850517d5ab2bb502024a55645f1Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen stream->istream.stream_errno = 0;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (stream->pos == stream->buffer_size) {
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen if (stream->skip > 0) {
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen /* remove the unused bytes from beginning of buffer */
10ff47d5d6146995e16da00d36eca7d162064a7bTimo Sirainen i_stream_compress(stream);
683eebe490bbe5caec246c535a10ea9f93f5c330Timo Sirainen } else if (fstream->max_buffer_size == 0 ||
683eebe490bbe5caec246c535a10ea9f93f5c330Timo Sirainen stream->buffer_size < fstream->max_buffer_size) {
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen /* buffer is full - grow it */
5238111c460098d9cc8cc22527026138a278b9a4Timo Sirainen i_stream_grow_buffer(stream, I_STREAM_MIN_SIZE);
5238111c460098d9cc8cc22527026138a278b9a4Timo Sirainen }
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (stream->pos == stream->buffer_size)
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen return -2; /* buffer full */
de954ff15b495be13007a8aca2c09fd1d356a283Timo Sirainen }
de954ff15b495be13007a8aca2c09fd1d356a283Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen size = stream->buffer_size - stream->pos;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen ret = -1;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen do {
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen if (fstream->file) {
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen ret = pread(stream->fd, stream->w_buffer + stream->pos,
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen size, stream->istream.v_offset +
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen (stream->pos - stream->skip));
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen } else {
ceb43cc04edb94445fab8f914bc4da6d740403d1Timo Sirainen ret = read(stream->fd, stream->w_buffer + stream->pos,
ceb43cc04edb94445fab8f914bc4da6d740403d1Timo Sirainen size);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen }
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen } while (ret < 0 && errno == EINTR && stream->istream.blocking);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if (ret == 0) {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen /* EOF */
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen stream->istream.eof = TRUE;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen return -1;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen }
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if (ret < 0) {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if (errno == EINTR || errno == EAGAIN) {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen i_assert(!stream->istream.blocking);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen ret = 0;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen } else {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen stream->istream.eof = TRUE;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen stream->istream.stream_errno = errno;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen return -1;
d77fb3be552eefbfb9cdd43ff23d794796d7a36cTimo Sirainen }
d77fb3be552eefbfb9cdd43ff23d794796d7a36cTimo Sirainen }
d77fb3be552eefbfb9cdd43ff23d794796d7a36cTimo Sirainen
03f5c621d06d6b6d77a145196c9633a7aa64dc78Timo Sirainen if (ret > 0 && fstream->skip_left > 0) {
d7e72877b7a5085c3addf9729d0bfbe1b5357853Timo Sirainen i_assert(!fstream->file);
d7e72877b7a5085c3addf9729d0bfbe1b5357853Timo Sirainen i_assert(stream->skip == stream->pos);
d7e72877b7a5085c3addf9729d0bfbe1b5357853Timo Sirainen
d7e72877b7a5085c3addf9729d0bfbe1b5357853Timo Sirainen if (fstream->skip_left >= (size_t)ret) {
03f5c621d06d6b6d77a145196c9633a7aa64dc78Timo Sirainen fstream->skip_left -= ret;
03f5c621d06d6b6d77a145196c9633a7aa64dc78Timo Sirainen ret = 0;
03f5c621d06d6b6d77a145196c9633a7aa64dc78Timo Sirainen } else {
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen ret -= fstream->skip_left;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen stream->pos += fstream->skip_left;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen stream->skip += fstream->skip_left;
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen fstream->skip_left = 0;
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen }
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen }
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen
71da447014454c84828d9dface77219875554d7dTimo Sirainen stream->pos += ret;
71da447014454c84828d9dface77219875554d7dTimo Sirainen i_assert(ret != 0 || !fstream->file);
71da447014454c84828d9dface77219875554d7dTimo Sirainen return ret;
71da447014454c84828d9dface77219875554d7dTimo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
static void _seek(struct _istream *stream, uoff_t v_offset,
bool mark __attr_unused__)
{
struct file_istream *fstream = (struct file_istream *) stream;
if (!stream->istream.seekable) {
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 void _sync(struct _istream *stream)
{
struct file_istream *fstream = (struct file_istream *) stream;
fstream->fstat_cache_stamp.tv_sec = 0;
if (!stream->istream.seekable) {
/* can't do anything or data would be lost */
return;
}
stream->skip = stream->pos = 0;
}
static int fstat_cached(struct file_istream *fstream)
{
if (fstream->fstat_cache_stamp.tv_sec == ioloop_timeval.tv_sec &&
fstream->fstat_cache_stamp.tv_usec == ioloop_timeval.tv_usec)
return 0;
if (fstat(fstream->istream.fd, &fstream->istream.statbuf) < 0) {
i_error("file_istream.fstat() failed: %m");
return -1;
}
fstream->fstat_cache_stamp = ioloop_timeval;
return 0;
}
static const struct stat *
_stat(struct _istream *stream, bool exact __attr_unused__)
{
struct file_istream *fstream = (struct file_istream *) stream;
if (fstream->file) {
if (fstat_cached(fstream) < 0)
return NULL;
}
return &stream->statbuf;
}
struct istream *i_stream_create_file(int fd, pool_t pool,
size_t max_buffer_size, bool 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.read = _read;
fstream->istream.seek = _seek;
fstream->istream.sync = _sync;
fstream->istream.stat = _stat;
/* if it's a file, set the flags properly */
if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
fstream->file = TRUE;
fstream->istream.istream.blocking = TRUE;
fstream->istream.istream.seekable = TRUE;
}
return _i_stream_create(&fstream->istream, pool, fd, 0);
}