istream-file.c revision 8edc373587d75f8040e3c4416e50638aa2a32188
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
57f4445a46726a17bfe78b0964dd301a6ccb40ecTimo Sirainen/* @UNSAFE: whole file */
57f4445a46726a17bfe78b0964dd301a6ccb40ecTimo Sirainen
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen#include "lib.h"
57f4445a46726a17bfe78b0964dd301a6ccb40ecTimo Sirainen#include "ioloop.h"
39ed514f9d401b3cb589595c6a2f532050254d77Timo Sirainen#include "istream-internal.h"
39ed514f9d401b3cb589595c6a2f532050254d77Timo Sirainen#include "network.h"
39ed514f9d401b3cb589595c6a2f532050254d77Timo Sirainen
c37098f8ce6d512ba41f09564d04ed25720f0a77Timo Sirainen#include <time.h>
39ed514f9d401b3cb589595c6a2f532050254d77Timo Sirainen#include <unistd.h>
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen#include <sys/stat.h>
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
39ed514f9d401b3cb589595c6a2f532050254d77Timo Sirainenstruct file_istream {
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen struct istream_private istream;
39ed514f9d401b3cb589595c6a2f532050254d77Timo Sirainen
39ed514f9d401b3cb589595c6a2f532050254d77Timo Sirainen uoff_t skip_left;
39ed514f9d401b3cb589595c6a2f532050254d77Timo Sirainen
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen unsigned int file:1;
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen unsigned int autoclose_fd:1;
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen unsigned int seen_eof:1;
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen};
57f4445a46726a17bfe78b0964dd301a6ccb40ecTimo Sirainen
57f4445a46726a17bfe78b0964dd301a6ccb40ecTimo Sirainenstatic void i_stream_file_close(struct iostream_private *stream)
62bf16bd8bb79e308e64110ae8d0b2a55a4c1490Timo Sirainen{
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen struct file_istream *fstream = (struct file_istream *)stream;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen struct istream_private *_stream = (struct istream_private *)stream;
37f96554a5734557cd454691d163e602d36384b4Timo Sirainen
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen if (fstream->autoclose_fd && _stream->fd != -1) {
6380f2bc729a03b328793e8ad6ba7587620fa184Timo Sirainen if (close(_stream->fd) < 0)
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen i_error("file_istream.close() failed: %m");
57f4445a46726a17bfe78b0964dd301a6ccb40ecTimo Sirainen }
20b136f04257b0ba338e49f31a999c0d4b243647Timo Sirainen _stream->fd = -1;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen}
57f4445a46726a17bfe78b0964dd301a6ccb40ecTimo Sirainen
57f4445a46726a17bfe78b0964dd301a6ccb40ecTimo Sirainenstatic void i_stream_file_destroy(struct iostream_private *stream)
009d6d90b33bc7f64fa8251ac392cc87a835b833Timo Sirainen{
03f4c5f3502801f5b318f464cc75313a88558805Timo Sirainen struct istream_private *_stream = (struct istream_private *)stream;
027c729b3107441f54a2602ccf2c67c6206998d5Timo Sirainen
027c729b3107441f54a2602ccf2c67c6206998d5Timo Sirainen i_free(_stream->w_buffer);
009d6d90b33bc7f64fa8251ac392cc87a835b833Timo Sirainen}
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainenstatic ssize_t i_stream_file_read(struct istream_private *stream)
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen{
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
be044d4f3d08652d7332cdec5aaf8391474908bbTimo Sirainen size_t size;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen ssize_t ret;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen stream->istream.stream_errno = 0;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen if (!i_stream_get_buffer_space(stream, 1, &size))
57f4445a46726a17bfe78b0964dd301a6ccb40ecTimo Sirainen return -2;
39ed514f9d401b3cb589595c6a2f532050254d77Timo Sirainen
39ed514f9d401b3cb589595c6a2f532050254d77Timo Sirainen do {
39ed514f9d401b3cb589595c6a2f532050254d77Timo Sirainen if (fstream->file) {
39ed514f9d401b3cb589595c6a2f532050254d77Timo Sirainen ret = pread(stream->fd, stream->w_buffer + stream->pos,
39ed514f9d401b3cb589595c6a2f532050254d77Timo Sirainen size, stream->istream.v_offset +
39ed514f9d401b3cb589595c6a2f532050254d77Timo Sirainen (stream->pos - stream->skip));
c8296ac1ed68ed5c5168de545b76f9b27fc76d35Timo Sirainen } else if (fstream->seen_eof) {
c8296ac1ed68ed5c5168de545b76f9b27fc76d35Timo Sirainen /* don't try to read() again. EOF from keyboard (^D)
c8296ac1ed68ed5c5168de545b76f9b27fc76d35Timo Sirainen requires this to work right. */
c8296ac1ed68ed5c5168de545b76f9b27fc76d35Timo Sirainen ret = 0;
64f30df0bee5218c9a69915e796d9d1376cfbf29Timo Sirainen } else {
64f30df0bee5218c9a69915e796d9d1376cfbf29Timo Sirainen ret = read(stream->fd, stream->w_buffer + stream->pos,
57f4445a46726a17bfe78b0964dd301a6ccb40ecTimo Sirainen size);
}
} while (unlikely(ret < 0 && errno == EINTR &&
stream->istream.blocking));
if (ret == 0) {
/* EOF */
stream->istream.eof = TRUE;
fstream->seen_eof = TRUE;
return -1;
}
if (unlikely(ret < 0)) {
if (errno == EINTR || errno == EAGAIN) {
i_assert(!stream->istream.blocking);
ret = 0;
} else {
stream->istream.eof = TRUE;
stream->istream.stream_errno = errno;
return -1;
}
}
if (ret > 0 && fstream->skip_left > 0) {
i_assert(!fstream->file);
i_assert(stream->skip == stream->pos);
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;
}
}
stream->pos += ret;
i_assert(ret != 0 || !fstream->file);
return ret;
}
static void i_stream_file_seek(struct istream_private *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;
fstream->seen_eof = FALSE;
}
static void i_stream_file_sync(struct istream_private *stream)
{
if (!stream->istream.seekable) {
/* can't do anything or data would be lost */
return;
}
stream->skip = stream->pos = 0;
}
static const struct stat *
i_stream_file_stat(struct istream_private *stream, bool exact ATTR_UNUSED)
{
struct file_istream *fstream = (struct file_istream *) stream;
if (fstream->file) {
if (fstat(fstream->istream.fd, &fstream->istream.statbuf) < 0) {
i_error("file_istream.fstat() failed: %m");
return NULL;
}
}
return &stream->statbuf;
}
struct istream *i_stream_create_fd(int fd, size_t max_buffer_size,
bool autoclose_fd)
{
struct file_istream *fstream;
struct stat st;
fstream = i_new(struct file_istream, 1);
fstream->autoclose_fd = autoclose_fd;
fstream->istream.iostream.close = i_stream_file_close;
fstream->istream.iostream.destroy = i_stream_file_destroy;
fstream->istream.max_buffer_size = max_buffer_size;
fstream->istream.read = i_stream_file_read;
fstream->istream.seek = i_stream_file_seek;
fstream->istream.sync = i_stream_file_sync;
fstream->istream.stat = i_stream_file_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, NULL, fd);
}