istream-file.c revision 8cb72c59d5ea4e9e5f638d7ec840bb853f5a188e
45312f52ff3a3d4c137447be4c7556500c2f8bf2Timo Sirainen/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger/* @UNSAFE: whole file */
345648b341f228bd7f0b89f8aa3ecb9c470d817eTimo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "lib.h"
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen#include "ioloop.h"
5a2cb3d097a2d9a9e930af997e7bf3400a8d840dTimo Sirainen#include "istream-internal.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "network.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include <time.h>
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen#include <unistd.h>
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include <sys/stat.h>
16f46efe0e090fe6975acf012a61a160f4787985Andrey Panin
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainenstruct file_istream {
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen struct istream_private istream;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen uoff_t skip_left;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen unsigned int file:1;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen unsigned int autoclose_fd:1;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen unsigned int seen_eof:1;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen};
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
16f46efe0e090fe6975acf012a61a160f4787985Andrey Paninstatic void i_stream_file_close(struct iostream_private *stream)
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen{
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen struct file_istream *fstream = (struct file_istream *)stream;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen struct istream_private *_stream = (struct istream_private *)stream;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen if (fstream->autoclose_fd && _stream->fd != -1) {
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (close(_stream->fd) < 0) {
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen i_error("file_istream.close(%s) failed: %m",
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen i_stream_get_name(&_stream->istream));
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen }
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen }
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen _stream->fd = -1;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen}
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
7242e1ce7803b83bc82e239ef111b47c1c72dd4bAndrey Paninstatic ssize_t i_stream_file_read(struct istream_private *stream)
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen{
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen size_t size;
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen ssize_t ret;
c57776c06ec99ba9b0dafdbf9475ea72ea8ca134Timo Sirainen
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen if (!i_stream_get_buffer_space(stream, 1, &size))
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen return -2;
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen do {
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen if (fstream->file) {
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen ret = pread(stream->fd, stream->w_buffer + stream->pos,
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen size, stream->istream.v_offset +
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen (stream->pos - stream->skip));
4051fa1f367553cac34f74c2e332a678390bcee5Timo Sirainen } else if (fstream->seen_eof) {
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen /* don't try to read() again. EOF from keyboard (^D)
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen requires this to work right. */
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen ret = 0;
4051fa1f367553cac34f74c2e332a678390bcee5Timo Sirainen } else {
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen ret = read(stream->fd, stream->w_buffer + stream->pos,
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen size);
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen }
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen } while (unlikely(ret < 0 && errno == EINTR &&
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen stream->istream.blocking));
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen
648d24583c1574441c4fa0331a90bd4d6e7996c5Timo Sirainen if (ret == 0) {
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen /* EOF */
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen stream->istream.eof = TRUE;
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen fstream->seen_eof = TRUE;
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen return -1;
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen }
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen if (unlikely(ret < 0)) {
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen if (errno == EINTR || errno == EAGAIN) {
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen i_assert(!stream->istream.blocking);
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen ret = 0;
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen } else {
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen i_assert(errno != 0);
0469ed17dafcc56589ce00960a23f4f06817dfb5Timo Sirainen stream->istream.stream_errno = errno;
1ddec6312bc6882aeb17d4d46d19cbca1723b68bTimo Sirainen return -1;
0469ed17dafcc56589ce00960a23f4f06817dfb5Timo Sirainen }
704fbadd78375da18dcaf2c5d93ac8cfe2c61358Timo Sirainen }
704fbadd78375da18dcaf2c5d93ac8cfe2c61358Timo Sirainen
704fbadd78375da18dcaf2c5d93ac8cfe2c61358Timo Sirainen if (ret > 0 && fstream->skip_left > 0) {
2c38504860da8a8de915f8e0f5f39d7e7bd00cf8Timo Sirainen i_assert(!fstream->file);
2c38504860da8a8de915f8e0f5f39d7e7bd00cf8Timo Sirainen i_assert(stream->skip == stream->pos);
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (fstream->skip_left >= (size_t)ret) {
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen fstream->skip_left -= ret;
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen ret = 0;
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen } else {
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen ret -= fstream->skip_left;
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen stream->pos += fstream->skip_left;
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen stream->skip += fstream->skip_left;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen fstream->skip_left = 0;
2c38504860da8a8de915f8e0f5f39d7e7bd00cf8Timo Sirainen }
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen }
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen
2c38504860da8a8de915f8e0f5f39d7e7bd00cf8Timo Sirainen stream->pos += ret;
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen i_assert(ret != 0 || !fstream->file);
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen i_assert(ret != -1);
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen return ret;
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen}
e9e2d23e1ea5a149a7d8828d2a45b9f2313c3785Timo Sirainen
e9e2d23e1ea5a149a7d8828d2a45b9f2313c3785Timo Sirainenstatic void i_stream_file_seek(struct istream_private *stream, uoff_t v_offset,
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen bool mark ATTR_UNUSED)
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen{
f97cf1c086715db87094bc3d0a4fefdd80bd869cTimo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
0469ed17dafcc56589ce00960a23f4f06817dfb5Timo Sirainen
0469ed17dafcc56589ce00960a23f4f06817dfb5Timo Sirainen if (!stream->istream.seekable) {
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (v_offset < stream->istream.v_offset)
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen i_panic("stream doesn't support seeking backwards");
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen fstream->skip_left += v_offset - stream->istream.v_offset;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen }
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
33c6d5807b449463e9b81db5ec99fe027cc1b984Timo Sirainen stream->istream.v_offset = v_offset;
8eea67470c1bd8562a62e7445d930bb2079b1a43Timo Sirainen stream->skip = stream->pos = 0;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen fstream->seen_eof = FALSE;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
2c38504860da8a8de915f8e0f5f39d7e7bd00cf8Timo Sirainenstatic void i_stream_file_sync(struct istream_private *stream)
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen{
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen if (!stream->istream.seekable) {
2c38504860da8a8de915f8e0f5f39d7e7bd00cf8Timo Sirainen /* can't do anything or data would be lost */
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen return;
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen }
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen
6e873f135368bcfdd1de4458dded791d0c4d00cdTimo Sirainen stream->skip = stream->pos = 0;
e9e2d23e1ea5a149a7d8828d2a45b9f2313c3785Timo Sirainen}
e9e2d23e1ea5a149a7d8828d2a45b9f2313c3785Timo Sirainen
a4ac325c2802693c6b761e5a8fda961e5d7490eaTimo Sirainenstatic const struct stat *
b7c2065b3f10f9ae27787a9db5aaefbfc70d4502Timo Siraineni_stream_file_stat(struct istream_private *stream, bool exact ATTR_UNUSED)
f97cf1c086715db87094bc3d0a4fefdd80bd869cTimo Sirainen{
0469ed17dafcc56589ce00960a23f4f06817dfb5Timo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
0469ed17dafcc56589ce00960a23f4f06817dfb5Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (fstream->file) {
if (fstat(fstream->istream.fd, &fstream->istream.statbuf) < 0) {
i_error("file_istream.fstat(%s) failed: %m",
i_stream_get_name(&stream->istream));
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.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;
}
fstream->istream.istream.readable_fd = TRUE;
return i_stream_create(&fstream->istream, NULL, fd);
}