ostream-file.c revision 62355fdb8b7c5fb4ed1b28433bc7df718407e2c6
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2002-2003 Timo Sirainen */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen/* @UNSAFE: whole file */
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen/* try to keep the buffer size within 4k..128k. ReiserFS may actually return
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen 128k as optimal size. */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen ((fstream)->head == (fstream)->tail && !(fstream)->full)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen ((size) < SSIZE_T_MAX ? (size_t)(size) : SSIZE_T_MAX)
a35cbba04d0a2823da98e693bd09a051addffdb2Timo Sirainen size_t buffer_size, max_buffer_size, optimal_block_size;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen size_t head, tail; /* first unsent/unused byte */
c4db1218645ed8ec8b5ae67c05bc5d7a80c1b8aeTimo Sirainen unsigned int full:1; /* if head == tail, is buffer empty or full? */
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic void stream_closed(struct file_ostream *fstream)
4316355ca8b7698516272520a972291378698140Timo Sirainen if (fstream->autoclose_fd && fstream->fd != -1) {
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen struct file_ostream *fstream = (struct file_ostream *)stream;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen /* flush output before really closing it */
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen struct file_ostream *fstream = (struct file_ostream *)stream;
4316355ca8b7698516272520a972291378698140Timo Sirainen p_free(fstream->ostream.iostream.pool, fstream->buffer);
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic void _set_max_buffer_size(struct _iostream *stream, size_t max_size)
4316355ca8b7698516272520a972291378698140Timo Sirainen struct file_ostream *fstream = (struct file_ostream *)stream;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainenstatic void update_buffer(struct file_ostream *fstream, size_t size)
4316355ca8b7698516272520a972291378698140Timo Sirainen /* ...HXXXT... */
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainenstatic ssize_t o_stream_writev(struct file_ostream *fstream,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen ret = write(fstream->fd, iov->iov_base, iov->iov_len);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen for (i = 0; i < IOV_MAX; i++)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen ret = writev(fstream->fd, (const struct iovec *)iov,
4316355ca8b7698516272520a972291378698140Timo Sirainen ret = writev(fstream->fd, (const struct iovec *)iov,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (errno == EINVAL) i_error("o_stream_sendv() -> EINVAL");
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen fstream->ostream.ostream.stream_errno = errno;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen/* returns how much of vector was used */
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenstatic int o_stream_fill_iovec(struct file_ostream *fstream,
e3077468777f5d324224365e34d7bbc449168e52Timo Sirainen iov[0].iov_base = fstream->buffer + fstream->head;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen iov[0].iov_len = fstream->tail - fstream->head;
4316355ca8b7698516272520a972291378698140Timo Sirainen iov[0].iov_base = fstream->buffer + fstream->head;
4316355ca8b7698516272520a972291378698140Timo Sirainen iov[0].iov_len = fstream->buffer_size - fstream->head;
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic int buffer_flush(struct file_ostream *fstream)
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic void _cork(struct _ostream *stream, int set)
4316355ca8b7698516272520a972291378698140Timo Sirainen struct file_ostream *fstream = (struct file_ostream *)stream;
4316355ca8b7698516272520a972291378698140Timo Sirainen if (fstream->corked != set && !stream->ostream.closed) {
4316355ca8b7698516272520a972291378698140Timo Sirainen } else if (!set) {
4316355ca8b7698516272520a972291378698140Timo Sirainen struct file_ostream *fstream = (struct file_ostream *) stream;
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic void _flush_pending(struct _ostream *stream, int set)
4316355ca8b7698516272520a972291378698140Timo Sirainen struct file_ostream *fstream = (struct file_ostream *) stream;
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen if (set && !fstream->corked && fstream->io == NULL) {
b8a4aab1f117f6760184ad50b1af41ba810b51f9Timo Sirainenstatic size_t get_unused_space(struct file_ostream *fstream)
4316355ca8b7698516272520a972291378698140Timo Sirainen /* ...HXXXT... */
4316355ca8b7698516272520a972291378698140Timo Sirainen return (fstream->buffer_size - fstream->tail) + fstream->head;
4316355ca8b7698516272520a972291378698140Timo Sirainen /* either fully unused or fully used */
4316355ca8b7698516272520a972291378698140Timo Sirainen return fstream->full ? 0 : fstream->buffer_size;
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic size_t _get_used_size(struct _ostream *stream)
4316355ca8b7698516272520a972291378698140Timo Sirainen struct file_ostream *fstream = (struct file_ostream *)stream;
4316355ca8b7698516272520a972291378698140Timo Sirainen return fstream->buffer_size - get_unused_space(fstream);
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic int _seek(struct _ostream *stream, uoff_t offset)
4316355ca8b7698516272520a972291378698140Timo Sirainen struct file_ostream *fstream = (struct file_ostream *)stream;
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen ret = lseek(fstream->fd, (off_t)offset, SEEK_SET);
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen if (errno == EINVAL) i_error("_seek(2) -> EINVAL");
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic void o_stream_grow_buffer(struct file_ostream *fstream, size_t bytes)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen size = pool_get_exp_grown_size(fstream->ostream.iostream.pool,
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen /* limit the size */
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen /* try to use optimal buffer size with corking */
9b61a6db87c026656f8d2ae214e4486b98a069c0Timo Sirainen fstream->buffer = p_realloc(fstream->ostream.iostream.pool,
a4922fa0c7de2aa332bc1361abf6f93f001fc02eTimo Sirainen if (fstream->tail <= fstream->head && !IS_STREAM_EMPTY(fstream)) {
4316355ca8b7698516272520a972291378698140Timo Sirainen /* move head forward to end of buffer */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen end_size = fstream->buffer_size - fstream->head;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen /* Set flush_pending = FALSE first before calling the flush callback,
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen and change it to TRUE only if callback returns 0. That way the
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen callback can call o_stream_set_flush_pending() again and we don't
4316355ca8b7698516272520a972291378698140Timo Sirainen forget it even if flush callback returns 1. */
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen ret = fstream->ostream.callback(fstream->ostream.context);
4316355ca8b7698516272520a972291378698140Timo Sirainen if (!fstream->flush_pending && IS_STREAM_EMPTY(fstream)) {
4316355ca8b7698516272520a972291378698140Timo Sirainen /* all sent */
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainenstatic size_t o_stream_add(struct file_ostream *fstream,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen for (i = 0; i < 2 && sent < size && !fstream->full; i++) {
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen fstream->io = io_add(fstream->fd, IO_WRITE, stream_send_io,
b089505afb8b5c5dfb43ef88af0ea7e0920db587Timo Sirainenstatic ssize_t _sendv(struct _ostream *stream, const struct const_iovec *iov,
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen struct file_ostream *fstream = (struct file_ostream *)stream;
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen unsigned int i;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (size > get_unused_space(fstream) && !IS_STREAM_EMPTY(fstream)) {
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen optimal_size = I_MIN(fstream->optimal_block_size,
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen /* send immediately */
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen ret = o_stream_writev(fstream, iov, iov_count);
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen /* buffer full */
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen /* buffer it, at least partly */
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen for (i = 0; i < iov_count; i++) {
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen added = o_stream_add(fstream, iov[i].iov_base, iov[i].iov_len);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenstatic off_t io_stream_sendfile(struct _ostream *outstream,
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen struct file_ostream *foutstream = (struct file_ostream *)outstream;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* flush out any data in buffer */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen offset = instream->real_stream->abs_start_offset + v_offset;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen ret = safe_sendfile(foutstream->fd, in_fd, &offset,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (ret == 0 || errno == EINTR || errno == EAGAIN) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (errno == EINVAL) i_error("io_stream_sendfile() -> EINVAL");
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen /* close only if error wasn't because
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen sendfile() isn't supported */
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return ret < 0 ? -1 : (off_t)(instream->v_offset - start_offset);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic off_t io_stream_copy(struct _ostream *outstream,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct file_ostream *foutstream = (struct file_ostream *)outstream;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen const unsigned char *data;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen iov_len = o_stream_fill_iovec(foutstream, iov);
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen while (in_size > 0) {
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen block_size = I_MIN(foutstream->optimal_block_size, in_size);
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen (void)i_stream_read_data(instream, &data, &size, block_size-1);
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen /* all sent */
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen ret = o_stream_writev(foutstream, iov, iov_len);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return (off_t) (instream->v_offset - start_offset);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic off_t io_stream_copy_backwards(struct _ostream *outstream,
169b679209f7b3e97888704ad3693c709265ef24Timo Sirainen struct file_ostream *foutstream = (struct file_ostream *)outstream;
169b679209f7b3e97888704ad3693c709265ef24Timo Sirainen uoff_t in_start_offset, in_offset, in_limit, out_offset;
169b679209f7b3e97888704ad3693c709265ef24Timo Sirainen const unsigned char *data;
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen /* figure out optimal buffer size */
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen buffer_size = instream->real_stream->buffer_size;
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen if (buffer_size == 0 || buffer_size > foutstream->buffer_size) {
0e5819a061034f1636b124c03a89f67d37c852b1Timo Sirainen if (foutstream->optimal_block_size > foutstream->buffer_size) {
0e5819a061034f1636b124c03a89f67d37c852b1Timo Sirainen out_offset = outstream->ostream.offset + (in_offset - in_start_offset);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen if (in_offset - in_start_offset <= buffer_size)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen (void)i_stream_read_data(instream, &data, &size,
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen /* we'll have to write it through
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen buffer or the file gets corrupted */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* buffer too large probably, try with smaller */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (o_stream_seek(&outstream->ostream, out_offset) < 0)
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen if (errno == EINVAL) i_error("copy backwards -> EINVAL");
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic off_t _send_istream(struct _ostream *outstream, struct istream *instream)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct file_ostream *foutstream = (struct file_ostream *)outstream;
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen if (errno == EINVAL) i_error("_send_istream() / stat -> EINVAL");
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen outstream->ostream.stream_errno = instream->stream_errno;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* copying data within same fd. we'll have to be careful with
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen seeks and overlapping writes. */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_error("_send_istream() / in_size == -1 -> EINVAL");
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen (off_t)(instream->real_stream->abs_start_offset +
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen /* copying data over itself. we don't really
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen need to do that, just fake it. */
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen if (!foutstream->no_sendfile && in_fd != -1 && overlapping <= 0) {
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen ret = io_stream_sendfile(outstream, instream, in_fd, in_size);
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen if (ret >= 0 || outstream->ostream.stream_errno != EINVAL)
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen /* sendfile() not supported (with this fd), fallback to
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen regular sending. */
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen return io_stream_copy(outstream, instream, in_size);
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen return io_stream_copy_backwards(outstream, instream, in_size);
9346506a9f4dd9a6285fe8595588e73161849235Timo Siraineno_stream_create_file(int fd, pool_t pool, size_t max_buffer_size,
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen fstream = p_new(pool, struct file_ostream, 1);
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen fstream->optimal_block_size = DEFAULT_OPTIMAL_BLOCK_SIZE;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen fstream->ostream.iostream.set_max_buffer_size = _set_max_buffer_size;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen fstream->ostream.flush_pending = _flush_pending;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen fstream->ostream.get_used_size = _get_used_size;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen fstream->ostream.send_istream = _send_istream;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen ostream = _o_stream_create(&fstream->ostream, pool);
10ccd0e45768923d69be459e87ef6cd2574cec60Timo Sirainen /* use the optimal block size, but with a
97daba82224dd757b7b7526ab3fd5d574a5f35d8Timo Sirainen reasonable limit */