istream-file.c revision a94936bafd127680184da114c6a177b37ff656e5
5e0ce63bb65db34d7f48b34bbb5545fa791781c4Timo Sirainen/* Copyright (c) 2002-2003 Timo Sirainen */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
5254d77805cd35b9356d072ba325c356c43b0d51Timo Sirainen/* @UNSAFE: whole file */
5254d77805cd35b9356d072ba325c356c43b0d51Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "lib.h"
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen#include "ioloop.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "istream-internal.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "network.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include <time.h>
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include <unistd.h>
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include <sys/stat.h>
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainenstruct file_istream {
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen struct istream_private istream;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen uoff_t skip_left;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen unsigned int file:1;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen unsigned int autoclose_fd:1;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen};
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainenstatic void i_stream_file_close(struct iostream_private *stream)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen{
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen struct file_istream *fstream = (struct file_istream *)stream;
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen struct istream_private *_stream = (struct istream_private *)stream;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (fstream->autoclose_fd && _stream->fd != -1) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (close(_stream->fd) < 0)
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen i_error("file_istream.close() failed: %m");
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
2c25e1360d4b5cc55eda969a3a7204d950de5a8fTimo Sirainen _stream->fd = -1;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainenstatic void i_stream_file_destroy(struct iostream_private *stream)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen{
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen struct istream_private *_stream = (struct istream_private *)stream;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen i_free(_stream->w_buffer);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainenstatic ssize_t i_stream_file_read(struct istream_private *stream)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen{
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen size_t size;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen ssize_t ret;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (stream->istream.closed)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return -1;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen stream->istream.stream_errno = 0;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (stream->pos == stream->buffer_size) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (stream->skip > 0) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* remove the unused bytes from beginning of buffer */
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen i_stream_compress(stream);
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen } else if (stream->max_buffer_size == 0 ||
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen stream->buffer_size < stream->max_buffer_size) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* buffer is full - grow it */
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen i_stream_grow_buffer(stream, I_STREAM_MIN_SIZE);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (stream->pos == stream->buffer_size)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return -2; /* buffer full */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen size = stream->buffer_size - stream->pos;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen ret = -1;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen do {
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen if (fstream->file) {
1060afdc2fcdf647dbb3bc11647401f1b44a3a8aTimo Sirainen ret = pread(stream->fd, stream->w_buffer + stream->pos,
1060afdc2fcdf647dbb3bc11647401f1b44a3a8aTimo Sirainen size, stream->istream.v_offset +
1060afdc2fcdf647dbb3bc11647401f1b44a3a8aTimo Sirainen (stream->pos - stream->skip));
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen } else {
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen ret = read(stream->fd, stream->w_buffer + stream->pos,
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen size);
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen }
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen } while (ret < 0 && errno == EINTR && stream->istream.blocking);
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (ret == 0) {
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen /* EOF */
9e095dd6a77097356aca8216356d4d71ef1bea45Timo Sirainen stream->istream.eof = TRUE;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen return -1;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen }
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (ret < 0) {
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen if (errno == EINTR || errno == EAGAIN) {
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen i_assert(!stream->istream.blocking);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen ret = 0;
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen } else {
9e095dd6a77097356aca8216356d4d71ef1bea45Timo Sirainen stream->istream.eof = TRUE;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen stream->istream.stream_errno = errno;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return -1;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (ret > 0 && fstream->skip_left > 0) {
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen i_assert(!fstream->file);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen i_assert(stream->skip == stream->pos);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (fstream->skip_left >= (size_t)ret) {
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen fstream->skip_left -= ret;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen ret = 0;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen } else {
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen ret -= fstream->skip_left;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen stream->pos += fstream->skip_left;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen stream->skip += fstream->skip_left;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen fstream->skip_left = 0;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen stream->pos += ret;
1060afdc2fcdf647dbb3bc11647401f1b44a3a8aTimo Sirainen i_assert(ret != 0 || !fstream->file);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return ret;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainenstatic void i_stream_file_seek(struct istream_private *stream, uoff_t v_offset,
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen bool mark ATTR_UNUSED)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen{
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
411d6baa37f31d90730e90c4a28c43e1974bbe58Timo Sirainen if (!stream->istream.seekable) {
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (v_offset < stream->istream.v_offset) {
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen stream->istream.stream_errno = ESPIPE;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen return;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen }
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen fstream->skip_left += v_offset - stream->istream.v_offset;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
143cb2e0744e647f8fc637bbdea1106c1587a4bfTimo Sirainen
143cb2e0744e647f8fc637bbdea1106c1587a4bfTimo Sirainen stream->istream.stream_errno = 0;
143cb2e0744e647f8fc637bbdea1106c1587a4bfTimo Sirainen stream->istream.v_offset = v_offset;
143cb2e0744e647f8fc637bbdea1106c1587a4bfTimo Sirainen stream->skip = stream->pos = 0;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainenstatic void i_stream_file_sync(struct istream_private *stream)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen{
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen if (!stream->istream.seekable) {
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen /* can't do anything or data would be lost */
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen return;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen }
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen stream->skip = stream->pos = 0;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen}
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
7d207b1e77a7b5e3fda640e353acfc86d261fedfTimo Sirainenstatic const struct stat *
a94936bafd127680184da114c6a177b37ff656e5Timo Siraineni_stream_file_stat(struct istream_private *stream, bool exact ATTR_UNUSED)
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen{
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen if (fstream->file) {
cd1eef2109b4476842b7757f1d69b104196d5941Timo Sirainen if (fstat(fstream->istream.fd, &fstream->istream.statbuf) < 0) {
cd1eef2109b4476842b7757f1d69b104196d5941Timo Sirainen i_error("file_istream.fstat() failed: %m");
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen return NULL;
cd1eef2109b4476842b7757f1d69b104196d5941Timo Sirainen }
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen }
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen return &stream->statbuf;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen}
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
2526d52441ef368215ab6bf04fd0356d3b09d235Timo Sirainenstruct istream *i_stream_create_fd(int fd, size_t max_buffer_size,
2526d52441ef368215ab6bf04fd0356d3b09d235Timo Sirainen bool autoclose_fd)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen{
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen struct file_istream *fstream;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen struct stat st;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen fstream = i_new(struct file_istream, 1);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen fstream->autoclose_fd = autoclose_fd;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen fstream->istream.iostream.close = i_stream_file_close;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen fstream->istream.iostream.destroy = i_stream_file_destroy;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen fstream->istream.max_buffer_size = max_buffer_size;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen fstream->istream.read = i_stream_file_read;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen fstream->istream.seek = i_stream_file_seek;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen fstream->istream.sync = i_stream_file_sync;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen fstream->istream.stat = i_stream_file_stat;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen /* if it's a file, set the flags properly */
411d6baa37f31d90730e90c4a28c43e1974bbe58Timo Sirainen if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen fstream->file = TRUE;
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen fstream->istream.istream.blocking = TRUE;
411d6baa37f31d90730e90c4a28c43e1974bbe58Timo Sirainen fstream->istream.istream.seekable = TRUE;
411d6baa37f31d90730e90c4a28c43e1974bbe58Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen return i_stream_create(&fstream->istream, fd, 0);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen}