istream-file.c revision c09f9f95db314e7482c95e502e1c56ed6c555797
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen
46552a931924c2d743f045e95b08c3ce6beda91aTimo Sirainen/* @UNSAFE: whole file */
3cb26db7f4756b71ba06c6e4950fa4f8ce7fad66Timo Sirainen
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen#include "lib.h"
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen#include "ioloop.h"
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen#include "istream-internal.h"
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen#include "network.h"
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen
45155bb1250cf5a120278f349465aded513a100fTimo Sirainen#include <time.h>
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen#include <unistd.h>
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen#include <sys/stat.h>
0af9ef2e9bb71a426bba236e74ceec30be699fb7Timo Sirainen
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainenstruct file_istream {
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen struct istream_private istream;
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen uoff_t skip_left;
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen
3cb26db7f4756b71ba06c6e4950fa4f8ce7fad66Timo Sirainen unsigned int file:1;
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen unsigned int autoclose_fd:1;
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen unsigned int seen_eof:1;
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen};
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainenstatic void i_stream_file_close(struct iostream_private *stream)
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen{
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen struct file_istream *fstream = (struct file_istream *)stream;
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen struct istream_private *_stream = (struct istream_private *)stream;
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen if (fstream->autoclose_fd && _stream->fd != -1) {
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen if (close(_stream->fd) < 0)
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen i_error("file_istream.close() failed: %m");
9508ac436fff0e1dcea975855c139cd251deb703Timo Sirainen }
ddbdc644a15f56f4b43596f1b8c0fc196c101445Timo Sirainen _stream->fd = -1;
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen}
b55f914c0ade77252cfd798ea8eb9a84bda56315Timo Sirainen
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainenstatic void i_stream_file_destroy(struct iostream_private *stream)
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen{
493123e38ca1f27b07ac30dcbc59663c5fcdcba2Timo Sirainen struct istream_private *_stream = (struct istream_private *)stream;
ddbdc644a15f56f4b43596f1b8c0fc196c101445Timo Sirainen
ddbdc644a15f56f4b43596f1b8c0fc196c101445Timo Sirainen i_free(_stream->w_buffer);
b55f914c0ade77252cfd798ea8eb9a84bda56315Timo Sirainen}
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainenstatic ssize_t i_stream_file_read(struct istream_private *stream)
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen{
ddbdc644a15f56f4b43596f1b8c0fc196c101445Timo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
ddbdc644a15f56f4b43596f1b8c0fc196c101445Timo Sirainen size_t size;
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen ssize_t ret;
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (!i_stream_get_buffer_space(stream, 1, &size))
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen return -2;
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen do {
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen if (fstream->file) {
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen ret = pread(stream->fd, stream->w_buffer + stream->pos,
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen size, stream->istream.v_offset +
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen (stream->pos - stream->skip));
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen } else if (fstream->seen_eof) {
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi /* don't try to read() again. EOF from keyboard (^D)
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi requires this to work right. */
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi ret = 0;
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi } else {
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi ret = read(stream->fd, stream->w_buffer + stream->pos,
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi size);
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi }
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi } while (unlikely(ret < 0 && errno == EINTR &&
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi stream->istream.blocking));
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi if (ret == 0) {
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi /* EOF */
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi stream->istream.eof = TRUE;
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi fstream->seen_eof = TRUE;
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi return -1;
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi }
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen if (unlikely(ret < 0)) {
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen if (errno == EINTR || errno == EAGAIN) {
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen i_assert(!stream->istream.blocking);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen ret = 0;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen } else {
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen i_assert(errno != 0);
3bc9d91f987261e989aa653fd412b0e8095e4810Timo Sirainen stream->istream.stream_errno = errno;
3bc9d91f987261e989aa653fd412b0e8095e4810Timo Sirainen return -1;
3bc9d91f987261e989aa653fd412b0e8095e4810Timo Sirainen }
3bc9d91f987261e989aa653fd412b0e8095e4810Timo Sirainen }
3bc9d91f987261e989aa653fd412b0e8095e4810Timo Sirainen
3bc9d91f987261e989aa653fd412b0e8095e4810Timo Sirainen if (ret > 0 && fstream->skip_left > 0) {
41783dcf1bcd7118440c9c40a691a09fb98a9460Timo Sirainen i_assert(!fstream->file);
41783dcf1bcd7118440c9c40a691a09fb98a9460Timo Sirainen i_assert(stream->skip == stream->pos);
41783dcf1bcd7118440c9c40a691a09fb98a9460Timo Sirainen
41783dcf1bcd7118440c9c40a691a09fb98a9460Timo Sirainen if (fstream->skip_left >= (size_t)ret) {
5324117274df8564eeaebe369cb1eca76edb3165Timo Sirainen fstream->skip_left -= ret;
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainen ret = 0;
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainen } else {
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainen ret -= fstream->skip_left;
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainen stream->pos += fstream->skip_left;
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainen stream->skip += fstream->skip_left;
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen fstream->skip_left = 0;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen }
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen }
52fe791133ad838c3aca3f1c88f96aab755950f8Timo Sirainen
52fe791133ad838c3aca3f1c88f96aab755950f8Timo Sirainen stream->pos += ret;
52fe791133ad838c3aca3f1c88f96aab755950f8Timo Sirainen i_assert(ret != 0 || !fstream->file);
52fe791133ad838c3aca3f1c88f96aab755950f8Timo Sirainen i_assert(ret != -1);
52fe791133ad838c3aca3f1c88f96aab755950f8Timo Sirainen return ret;
52fe791133ad838c3aca3f1c88f96aab755950f8Timo Sirainen}
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen
686ad6d723004b807fd558f3ef9d1f88afa7e127Timo Sirainenstatic void i_stream_file_seek(struct istream_private *stream, uoff_t v_offset,
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen bool mark ATTR_UNUSED)
686ad6d723004b807fd558f3ef9d1f88afa7e127Timo Sirainen{
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (!stream->istream.seekable) {
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (v_offset < stream->istream.v_offset) {
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen stream->istream.stream_errno = ESPIPE;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen return;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen }
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen fstream->skip_left += v_offset - stream->istream.v_offset;
26681e71837ebbb3eb92455ec4e3cadefa710f82Timo Sirainen }
26681e71837ebbb3eb92455ec4e3cadefa710f82Timo Sirainen
26681e71837ebbb3eb92455ec4e3cadefa710f82Timo Sirainen stream->istream.v_offset = v_offset;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen stream->skip = stream->pos = 0;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen fstream->seen_eof = FALSE;
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen}
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainenstatic void i_stream_file_sync(struct istream_private *stream)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen{
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (!stream->istream.seekable) {
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen /* can't do anything or data would be lost */
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen return;
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen }
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen stream->skip = stream->pos = 0;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen}
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainenstatic const struct stat *
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Siraineni_stream_file_stat(struct istream_private *stream, bool exact ATTR_UNUSED)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen{
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen struct file_istream *fstream = (struct file_istream *) stream;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (fstream->file) {
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen if (fstat(fstream->istream.fd, &fstream->istream.statbuf) < 0) {
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen i_error("file_istream.fstat() failed: %m");
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen return NULL;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen }
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen }
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen return &stream->statbuf;
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen}
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen
3cb26db7f4756b71ba06c6e4950fa4f8ce7fad66Timo Sirainenstruct istream *i_stream_create_fd(int fd, size_t max_buffer_size,
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen bool autoclose_fd)
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi{
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen struct file_istream *fstream;
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen struct stat st;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen fstream = i_new(struct file_istream, 1);
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen fstream->autoclose_fd = autoclose_fd;
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen fstream->istream.iostream.close = i_stream_file_close;
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen fstream->istream.iostream.destroy = i_stream_file_destroy;
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen
53d564c421ca7292d7b1bd945f86894a34b75370Timo Sirainen fstream->istream.max_buffer_size = max_buffer_size;
3cb26db7f4756b71ba06c6e4950fa4f8ce7fad66Timo Sirainen fstream->istream.read = i_stream_file_read;
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen fstream->istream.seek = i_stream_file_seek;
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen fstream->istream.sync = i_stream_file_sync;
52fe791133ad838c3aca3f1c88f96aab755950f8Timo Sirainen fstream->istream.stat = i_stream_file_stat;
52fe791133ad838c3aca3f1c88f96aab755950f8Timo Sirainen
5324117274df8564eeaebe369cb1eca76edb3165Timo Sirainen /* if it's a file, set the flags properly */
5324117274df8564eeaebe369cb1eca76edb3165Timo Sirainen if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
5324117274df8564eeaebe369cb1eca76edb3165Timo Sirainen fstream->file = TRUE;
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi fstream->istream.istream.blocking = TRUE;
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi fstream->istream.istream.seekable = TRUE;
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi }
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi return i_stream_create(&fstream->istream, NULL, fd);
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi}
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi