istream-file.c revision 1060afdc2fcdf647dbb3bc11647401f1b44a3a8a
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2002-2003 Timo Sirainen */
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen/* @UNSAFE: whole file */
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen#include "lib.h"
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen#include "istream-internal.h"
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen#include "network.h"
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen#include <time.h>
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen#include <unistd.h>
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen#include <sys/stat.h>
93ae7fcd39c6982f7e338adfe71139942d9bbad1Timo Sirainen
93ae7fcd39c6982f7e338adfe71139942d9bbad1Timo Sirainen#define I_STREAM_MIN_SIZE 4096
93ae7fcd39c6982f7e338adfe71139942d9bbad1Timo Sirainen
93ae7fcd39c6982f7e338adfe71139942d9bbad1Timo Sirainenstruct file_istream {
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen struct _istream istream;
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen size_t max_buffer_size;
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen uoff_t skip_left;
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen unsigned int file:1;
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen unsigned int autoclose_fd:1;
8e361d2906b0e44f7175a20981f8d2280645b58bTimo Sirainen};
8e361d2906b0e44f7175a20981f8d2280645b58bTimo Sirainen
3281669db44d09a087a203201248abbc81b3cc1aTimo Sirainenstatic void _close(struct _iostream *stream)
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen{
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen struct _istream *_stream = (struct _istream *) stream;
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen if (fstream->autoclose_fd && _stream->fd != -1) {
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen if (close(_stream->fd) < 0)
cd466fe7b84b0223735a6469c7f7bc225f65996dTimo Sirainen i_error("file_istream.close() failed: %m");
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen _stream->fd = -1;
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen }
0e3f8c6edad565112d91f0a53568c0313d657e48Timo Sirainen}
0e3f8c6edad565112d91f0a53568c0313d657e48Timo Sirainen
0e3f8c6edad565112d91f0a53568c0313d657e48Timo Sirainenstatic void _destroy(struct _iostream *stream)
0e3f8c6edad565112d91f0a53568c0313d657e48Timo Sirainen{
0e3f8c6edad565112d91f0a53568c0313d657e48Timo Sirainen struct _istream *_stream = (struct _istream *) stream;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen
d56384d5226c8860079d0d0b08b83404e8c42986Timo Sirainen p_free(_stream->iostream.pool, _stream->w_buffer);
0f66f12eb4cdbf47670975044c88d8f388bf92dfTimo Sirainen}
d56384d5226c8860079d0d0b08b83404e8c42986Timo Sirainen
d56384d5226c8860079d0d0b08b83404e8c42986Timo Sirainenstatic void _set_max_buffer_size(struct _iostream *stream, size_t max_size)
4b058f90f9e8a2c6b2eed275de4eb8cc5195a71dTimo Sirainen{
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen fstream->max_buffer_size = max_size;
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen}
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainenstatic void i_stream_grow_buffer(struct _istream *stream, size_t bytes)
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen{
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen size_t old_size;
d6af1e63bc7824f1cc5b9b73a1c5f8f8789788d6Timo Sirainen
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen old_size = stream->buffer_size;
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen stream->buffer_size = stream->pos + bytes;
6a9f9a5101b665fd2ef80c9e048a5eace78e01efTimo Sirainen if (stream->buffer_size <= I_STREAM_MIN_SIZE)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen stream->buffer_size = I_STREAM_MIN_SIZE;
0f66f12eb4cdbf47670975044c88d8f388bf92dfTimo Sirainen else
6a9f9a5101b665fd2ef80c9e048a5eace78e01efTimo Sirainen stream->buffer_size = nearest_power(stream->buffer_size);
6a9f9a5101b665fd2ef80c9e048a5eace78e01efTimo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen if (fstream->max_buffer_size > 0 &&
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen stream->buffer_size > fstream->max_buffer_size)
4b058f90f9e8a2c6b2eed275de4eb8cc5195a71dTimo Sirainen stream->buffer_size = fstream->max_buffer_size;
b88c43d09a288e99d439c78de4cc613212ea924cTimo Sirainen
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen stream->buffer = stream->w_buffer =
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen p_realloc(stream->iostream.pool, stream->w_buffer,
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen old_size, stream->buffer_size);
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen}
4b058f90f9e8a2c6b2eed275de4eb8cc5195a71dTimo Sirainen
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainenstatic void i_stream_compress(struct _istream *stream)
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen{
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen memmove(stream->w_buffer, stream->w_buffer + stream->skip,
1f57716285d4c5bc9bf2fd5569e3c85fd496afd9Timo Sirainen stream->pos - stream->skip);
1f57716285d4c5bc9bf2fd5569e3c85fd496afd9Timo Sirainen stream->pos -= stream->skip;
1f57716285d4c5bc9bf2fd5569e3c85fd496afd9Timo Sirainen
1f57716285d4c5bc9bf2fd5569e3c85fd496afd9Timo Sirainen stream->skip = 0;
1f57716285d4c5bc9bf2fd5569e3c85fd496afd9Timo Sirainen}
1f57716285d4c5bc9bf2fd5569e3c85fd496afd9Timo Sirainen
1f57716285d4c5bc9bf2fd5569e3c85fd496afd9Timo Sirainenstatic ssize_t _read(struct _istream *stream)
1f57716285d4c5bc9bf2fd5569e3c85fd496afd9Timo Sirainen{
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen size_t size;
88bcf81213b292e58ddaa41643e4146299cc750fTimo Sirainen ssize_t ret;
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen if (stream->istream.closed)
88bcf81213b292e58ddaa41643e4146299cc750fTimo Sirainen return -1;
88bcf81213b292e58ddaa41643e4146299cc750fTimo Sirainen
88bcf81213b292e58ddaa41643e4146299cc750fTimo Sirainen stream->istream.stream_errno = 0;
88bcf81213b292e58ddaa41643e4146299cc750fTimo Sirainen
88bcf81213b292e58ddaa41643e4146299cc750fTimo Sirainen if (stream->pos == stream->buffer_size) {
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen if (stream->skip > 0) {
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen /* remove the unused bytes from beginning of buffer */
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen i_stream_compress(stream);
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen } else if (fstream->max_buffer_size == 0 ||
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen stream->buffer_size < fstream->max_buffer_size) {
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen /* buffer is full - grow it */
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen i_stream_grow_buffer(stream, I_STREAM_MIN_SIZE);
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen }
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen if (stream->pos == stream->buffer_size)
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen return -2; /* buffer full */
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen }
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen size = stream->buffer_size - stream->pos;
af208f4a43a81a39a2ec44e68d65d12b95f1b386Timo Sirainen
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen ret = -1;
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen
2ac5f36aa7c2e7a07ba8815d43a6d7483f62e74cTimo Sirainen if (fstream->file) {
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen do {
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen ret = pread(stream->fd, stream->w_buffer + stream->pos,
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen size, stream->istream.v_offset +
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen (stream->pos - stream->skip));
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen } while (ret < 0 && errno == EINTR);
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen } else {
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen ret = read(stream->fd, stream->w_buffer + stream->pos, size);
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen }
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen if (ret == 0) {
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen /* EOF */
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen stream->istream.eof = TRUE;
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen return -1;
8b48c53a81bdc67f267ffbcc45ba9860cb49e977Timo Sirainen }
8b48c53a81bdc67f267ffbcc45ba9860cb49e977Timo Sirainen
22c1ec434d7323e125c150e3fd237316c74de6d5Timo Sirainen if (ret < 0) {
22c1ec434d7323e125c150e3fd237316c74de6d5Timo Sirainen if (errno == EINTR || errno == EAGAIN)
8b48c53a81bdc67f267ffbcc45ba9860cb49e977Timo Sirainen ret = 0;
22c1ec434d7323e125c150e3fd237316c74de6d5Timo Sirainen else {
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen stream->istream.eof = TRUE;
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen stream->istream.stream_errno = errno;
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen return -1;
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen }
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen }
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen if (ret > 0 && fstream->skip_left > 0) {
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen i_assert(!fstream->file);
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen i_assert(stream->skip == stream->pos);
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen if (fstream->skip_left >= (size_t)ret) {
fa4da02c28abb00c722e3e983cf4cc9b28ef0ad3Phil Carmody fstream->skip_left -= ret;
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen ret = 0;
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen } else {
b09be485e9373be4288f5615bbce6ebed65a425aTimo Sirainen ret -= fstream->skip_left;
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen stream->pos += fstream->skip_left;
stream->skip += fstream->skip_left;
fstream->skip_left = 0;
}
}
stream->pos += ret;
i_assert(ret != 0 || !fstream->file);
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.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);
}