istream-file.c revision 09801f106cd531a28b4e03ec665e44c421264560
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen/* @UNSAFE: whole file */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include "lib.h"
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include "ioloop.h"
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include "istream-internal.h"
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include "network.h"
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include <time.h>
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include <unistd.h>
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include <fcntl.h>
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include <sys/stat.h>
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstruct file_istream {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen struct istream_private istream;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen uoff_t skip_left;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen unsigned int file:1;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen unsigned int autoclose_fd:1;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen unsigned int seen_eof:1;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen};
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstatic void i_stream_file_close(struct iostream_private *stream)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen struct file_istream *fstream = (struct file_istream *)stream;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen struct istream_private *_stream = (struct istream_private *)stream;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (fstream->autoclose_fd && _stream->fd != -1) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (close(_stream->fd) < 0) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_error("file_istream.close(%s) failed: %m",
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_stream_get_name(&_stream->istream));
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen _stream->fd = -1;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstatic int i_stream_file_open(struct istream_private *stream)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen const char *path = i_stream_get_name(&stream->istream);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->fd = open(path, O_RDONLY);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (stream->fd == -1) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->istream.stream_errno = errno;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_error("file_istream.open(%s) failed: %m", path);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return -1;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return 0;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstatic ssize_t i_stream_file_read(struct istream_private *stream)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen size_t size;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen ssize_t ret;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (!i_stream_get_buffer_space(stream, 1, &size))
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return -2;
0ca3b9cb0f2a322a25ce7f229dc3d3a0b46be17bTimo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (stream->fd == -1) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (i_stream_file_open(stream) < 0)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return -1;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen do {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (fstream->file) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen ret = pread(stream->fd, stream->w_buffer + stream->pos,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen size, stream->istream.v_offset +
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen (stream->pos - stream->skip));
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen } else if (fstream->seen_eof) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* don't try to read() again. EOF from keyboard (^D)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen requires this to work right. */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen ret = 0;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen } else {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen ret = read(stream->fd, stream->w_buffer + stream->pos,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen size);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen } while (unlikely(ret < 0 && errno == EINTR &&
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->istream.blocking));
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (ret == 0) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* EOF */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->istream.eof = TRUE;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen fstream->seen_eof = TRUE;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return -1;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (unlikely(ret < 0)) {
56aa97d74071f3a2987140c2ff1cfd5a59cb35aaTimo Sirainen if (errno == EINTR || errno == EAGAIN) {
56aa97d74071f3a2987140c2ff1cfd5a59cb35aaTimo Sirainen i_assert(!stream->istream.blocking);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen ret = 0;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen } else {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_assert(errno != 0);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->istream.stream_errno = errno;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return -1;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (ret > 0 && fstream->skip_left > 0) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_assert(!fstream->file);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_assert(stream->skip == stream->pos);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (fstream->skip_left >= (size_t)ret) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen fstream->skip_left -= ret;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen ret = 0;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen } else {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen ret -= fstream->skip_left;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->pos += fstream->skip_left;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->skip += fstream->skip_left;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen fstream->skip_left = 0;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->pos += ret;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_assert(ret != 0 || !fstream->file);
8c909e451d14075c05d90382cf8eebc4e354f569Timo Sirainen i_assert(ret != -1);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return ret;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstatic void i_stream_file_seek(struct istream_private *stream, uoff_t v_offset,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen bool mark ATTR_UNUSED)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (!stream->istream.seekable) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (v_offset < stream->istream.v_offset)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_panic("stream doesn't support seeking backwards");
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen fstream->skip_left += v_offset - stream->istream.v_offset;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->istream.v_offset = v_offset;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->skip = stream->pos = 0;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen fstream->seen_eof = FALSE;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstatic void i_stream_file_sync(struct istream_private *stream)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (!stream->istream.seekable) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* can't do anything or data would be lost */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->skip = stream->pos = 0;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstatic const struct stat *
01435c38e7d671d5a892c4b802cfb204881cd454Timo Siraineni_stream_file_stat(struct istream_private *stream, bool exact ATTR_UNUSED)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen const char *name = i_stream_get_name(&stream->istream);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (!fstream->file) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* return defaults */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen } else if (stream->fd != -1) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (fstat(stream->fd, &stream->statbuf) < 0) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_error("file_istream.fstat(%s) failed: %m", name);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return NULL;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen } else {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (stat(name, &stream->statbuf) < 0) {
9a1f68e5ab08eabd352d533315cba1c69006e2c1Timo Sirainen i_error("file_istream.fstat(%s) failed: %m", name);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return NULL;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return &stream->statbuf;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstatic struct istream *
01435c38e7d671d5a892c4b802cfb204881cd454Timo Siraineni_stream_create_file_common(int fd, size_t max_buffer_size, bool autoclose_fd)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen struct file_istream *fstream;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen struct stat st;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen fstream = i_new(struct file_istream, 1);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen fstream->autoclose_fd = autoclose_fd;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen fstream->istream.iostream.close = i_stream_file_close;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen fstream->istream.max_buffer_size = max_buffer_size;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen fstream->istream.read = i_stream_file_read;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen fstream->istream.seek = i_stream_file_seek;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen fstream->istream.sync = i_stream_file_sync;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen fstream->istream.stat = i_stream_file_stat;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* if it's a file, set the flags properly */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (fd == -1 || (fstat(fd, &st) == 0 && S_ISREG(st.st_mode))) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen fstream->file = TRUE;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen fstream->istream.istream.blocking = TRUE;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen fstream->istream.istream.seekable = TRUE;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen fstream->istream.istream.readable_fd = TRUE;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return i_stream_create(&fstream->istream, NULL, fd);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstruct istream *i_stream_create_fd(int fd, size_t max_buffer_size,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen bool autoclose_fd)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_assert(fd != -1);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return i_stream_create_file_common(fd, max_buffer_size, autoclose_fd);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstruct istream *i_stream_create_file(const char *path, size_t max_buffer_size)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen struct istream *input;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen input = i_stream_create_file_common(-1, max_buffer_size, TRUE);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_stream_set_name(input, path);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return input;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen