iobuffer.c revision 62505210a7e6d1b2e35fac335a6c875a7c98ccfb
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/*
1d4f710106fb498750456724628da6063e012e6dTimo Sirainen iobuffer.c : Input/output transmit buffer handling
46552a931924c2d743f045e95b08c3ce6beda91aTimo Sirainen
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen Copyright (c) 2002 Timo Sirainen
d3d769026fae5d21c2d29614d3bc4579e8d79e81Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen Permission is hereby granted, free of charge, to any person obtaining
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen a copy of this software and associated documentation files (the
baebb412a9a5a44b1756e01cfa3b99f5d8a846b6Timo Sirainen "Software"), to deal in the Software without restriction, including
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen without limitation the rights to use, copy, modify, merge, publish,
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen distribute, sublicense, and/or sell copies of the Software, and to
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen permit persons to whom the Software is furnished to do so, subject to
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen the following conditions:
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen
92c49f3005f4dff1a6f576fffa8112ef6d1cae7fTimo Sirainen The above copyright notice and this permission notice shall be
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen included in all copies or substantial portions of the Software.
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
4ead43ecc06d10047998966c4dc0b142ecce4b66Timo Sirainen MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
1d4f710106fb498750456724628da6063e012e6dTimo Sirainen IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
1d4f710106fb498750456724628da6063e012e6dTimo Sirainen CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen*/
ab0d9eecd85f74acae18fe88529302e0776cc500Timo Sirainen
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen#include "lib.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "ioloop.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "iobuffer.h"
336b825e0321b798690351d9899b1b0cb99ec462Timo Sirainen#include "mmap-util.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "sendfile-util.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "network.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include <unistd.h>
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include <sys/stat.h>
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#define IO_BUFFER_MIN_SIZE 4096
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#define MAX_SSIZE_T(size) ((size) < SSIZE_T_MAX ? (size_t)(size) : SSIZE_T_MAX)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
baebb412a9a5a44b1756e01cfa3b99f5d8a846b6Timo Sirainentypedef struct {
baebb412a9a5a44b1756e01cfa3b99f5d8a846b6Timo Sirainen IOLoop ioloop;
baebb412a9a5a44b1756e01cfa3b99f5d8a846b6Timo Sirainen IOBuffer *outbuf;
baebb412a9a5a44b1756e01cfa3b99f5d8a846b6Timo Sirainen
baebb412a9a5a44b1756e01cfa3b99f5d8a846b6Timo Sirainen const char *data;
baebb412a9a5a44b1756e01cfa3b99f5d8a846b6Timo Sirainen uoff_t size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen IOBuffer *inbuf;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
f059a046515f4b2b15a6c2a10a6f12f6166e39a5Timo Sirainen int timeout;
f059a046515f4b2b15a6c2a10a6f12f6166e39a5Timo Sirainen int last_block;
f059a046515f4b2b15a6c2a10a6f12f6166e39a5Timo Sirainen} IOBufferBlockContext;
f059a046515f4b2b15a6c2a10a6f12f6166e39a5Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic size_t mmap_pagesize = 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic size_t mmap_pagemask = 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo SirainenIOBuffer *io_buffer_create(int fd, Pool pool, int priority,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size_t max_buffer_size)
5a250816ffc4cc5db203f9410ea99b6601c7b91aTimo Sirainen{
5a250816ffc4cc5db203f9410ea99b6601c7b91aTimo Sirainen IOBuffer *buf;
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen i_assert(fd >= 0);
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen i_assert(pool != NULL);
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen pool_ref(pool);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf = p_new(pool, IOBuffer, 1);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->refcount = 1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->fd = fd;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->pool = pool;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->priority = priority;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->max_buffer_size = max_buffer_size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return buf;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo SirainenIOBuffer *io_buffer_create_file(int fd, Pool pool, size_t max_buffer_size,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen int flags)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen IOBuffer *buf;
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen buf = io_buffer_create(fd, pool, IO_PRIORITY_DEFAULT, max_buffer_size);
d3d769026fae5d21c2d29614d3bc4579e8d79e81Timo Sirainen buf->file = TRUE;
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen buf->close_file = (flags & IOBUFFER_FLAG_AUTOCLOSE) != 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return buf;
5a9e240ebf8d0daaf029973973b52e415148070bTimo Sirainen}
5a9e240ebf8d0daaf029973973b52e415148070bTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo SirainenIOBuffer *io_buffer_create_mmap(int fd, Pool pool, size_t block_size,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen uoff_t start_offset, uoff_t size, int flags)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
f059a046515f4b2b15a6c2a10a6f12f6166e39a5Timo Sirainen IOBuffer *buf;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct stat st;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen uoff_t stop_offset;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_assert(start_offset < OFF_T_MAX);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
54533aa265f5c87730022cc7576090bc51370f97Timo Sirainen /* block size must be page aligned, and at least two pages long */
54533aa265f5c87730022cc7576090bc51370f97Timo Sirainen if (mmap_pagesize == 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen mmap_pagesize = getpagesize();
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen mmap_pagemask = mmap_pagesize-1;
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen if (block_size < mmap_pagesize*2)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen block_size = mmap_pagesize*2;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen else if ((block_size & mmap_pagemask) != 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen block_size &= ~mmap_pagemask;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen block_size += mmap_pagesize;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf = io_buffer_create_file(fd, pool, block_size, flags);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->mmaped = TRUE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->receive = TRUE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen if (fstat(fd, &st) == 0)
54533aa265f5c87730022cc7576090bc51370f97Timo Sirainen stop_offset = (uoff_t)st.st_size;
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen else {
d3d769026fae5d21c2d29614d3bc4579e8d79e81Timo Sirainen i_error("io_buffer_create_mmap(): lseek() failed: %m");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen stop_offset = 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (start_offset > stop_offset)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen start_offset = stop_offset;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (size > stop_offset-start_offset) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_warning("Trying to create IOBuffer with size "
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "%"PRIuUOFF_T" but we have only %"PRIuUOFF_T
c7fca6cbb32388556d9f6d8313486cc4e4a3c058Timo Sirainen " bytes available in file", size,
c7fca6cbb32388556d9f6d8313486cc4e4a3c058Timo Sirainen stop_offset-start_offset);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen size = stop_offset-start_offset;
c7fca6cbb32388556d9f6d8313486cc4e4a3c058Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (size == 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size = stop_offset - start_offset;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->start_offset = start_offset;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->limit = buf->size = size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->skip = buf->pos = buf->start_offset;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return buf;
5a9e240ebf8d0daaf029973973b52e415148070bTimo Sirainen}
5a9e240ebf8d0daaf029973973b52e415148070bTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid io_buffer_ref(IOBuffer *buf)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen buf->refcount++;
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid io_buffer_unref(IOBuffer *buf)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (buf == NULL)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_assert(buf->refcount > 0);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (--buf->refcount > 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (buf->io != NULL)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen io_remove(buf->io);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (buf->buffer != NULL) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (!buf->mmaped)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen p_free(buf->pool, buf->buffer);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen else {
05e55893a799de645fc8cd2203d6013f0e0f1b79Timo Sirainen if (munmap(buf->buffer, buf->buffer_size) < 0) {
2aac7ca853f63b62ea79ef8eae9ded83ed6063a5Timo Sirainen i_error("io_buffer_destroy(): "
4de2a17e0a2aed3b57a6c1057329b6a132b56ae2Timo Sirainen "munmap() failed: %m");
4de2a17e0a2aed3b57a6c1057329b6a132b56ae2Timo Sirainen }
2aac7ca853f63b62ea79ef8eae9ded83ed6063a5Timo Sirainen }
2aac7ca853f63b62ea79ef8eae9ded83ed6063a5Timo Sirainen }
2aac7ca853f63b62ea79ef8eae9ded83ed6063a5Timo Sirainen io_buffer_close(buf);
9ddd3d7d8651985e373a6c48e0ddc76b8a4ef1c7Timo Sirainen p_free(buf->pool, buf);
4de2a17e0a2aed3b57a6c1057329b6a132b56ae2Timo Sirainen pool_unref(buf->pool);
5d2e7ec2ea725c8a6a63f56b771e746f93e782ecTimo Sirainen}
54533aa265f5c87730022cc7576090bc51370f97Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid io_buffer_close(IOBuffer *buf)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (buf == NULL)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->closed = TRUE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (buf->close_file && buf->fd != -1) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (close(buf->fd) < 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_error("io_buffer_close(): close() failed: %m");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->fd = -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
bdb9f7f7fbf828fb85a393bd2803167b1bb8ff0dTimo Sirainenvoid io_buffer_reset(IOBuffer *buf)
bdb9f7f7fbf828fb85a393bd2803167b1bb8ff0dTimo Sirainen{
bdb9f7f7fbf828fb85a393bd2803167b1bb8ff0dTimo Sirainen buf->pos = buf->skip = buf->cr_lookup_pos = 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->last_cr = FALSE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (buf->mmaped && buf->buffer != NULL) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (munmap(buf->buffer, buf->buffer_size) < 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_error("io_buffer_reset(): munmap() failed: %m");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->buffer = NULL;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->buffer_size = 0;
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen buf->mmap_offset = buf->offset = 0;
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo SirainenIOBuffer *io_buffer_set_pool(IOBuffer *buf, Pool pool)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen IOBuffer *newbuf;
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen i_assert(buf != NULL);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_assert(pool != NULL);
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen newbuf = p_new(pool, IOBuffer, 1);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen memcpy(newbuf, buf, sizeof(IOBuffer));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen newbuf->pool = pool;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (!newbuf->mmaped) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen newbuf->buffer = p_malloc(pool, buf->buffer_size);
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen memcpy(newbuf->buffer, buf->buffer + buf->skip,
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen buf->buffer_size - buf->skip);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen newbuf->cr_lookup_pos -= newbuf->skip;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen newbuf->pos -= newbuf->skip;
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen newbuf->skip = 0;
54533aa265f5c87730022cc7576090bc51370f97Timo Sirainen
54533aa265f5c87730022cc7576090bc51370f97Timo Sirainen p_free(buf->pool, buf->buffer);
54533aa265f5c87730022cc7576090bc51370f97Timo Sirainen }
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen
54533aa265f5c87730022cc7576090bc51370f97Timo Sirainen p_free(buf->pool, buf);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return newbuf;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid io_buffer_set_max_size(IOBuffer *buf, size_t max_size)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_assert(!buf->mmaped);
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen buf->max_buffer_size = max_size;
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen}
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid io_buffer_set_blocking(IOBuffer *buf, size_t max_size,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen int timeout_msecs, TimeoutFunc timeout_func,
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen void *context)
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen{
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen buf->timeout_msecs = timeout_msecs;
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen buf->timeout_func = timeout_func;
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen buf->timeout_context = context;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->blocking = max_size > 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen if (max_size != 0)
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen buf->max_buffer_size = max_size;
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen}
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic ssize_t my_write(int fd, const void *buf, size_t size)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
717bb0dbaf4bd3f745669570647845e6d493bfe0Timo Sirainen ssize_t ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = write(fd, buf, size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (ret < 0 && (errno == EINTR || errno == EAGAIN))
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
717bb0dbaf4bd3f745669570647845e6d493bfe0Timo Sirainen return ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic ssize_t io_buffer_write(IOBuffer *buf, const void *data, size_t size)
717bb0dbaf4bd3f745669570647845e6d493bfe0Timo Sirainen{
717bb0dbaf4bd3f745669570647845e6d493bfe0Timo Sirainen ssize_t ret;
717bb0dbaf4bd3f745669570647845e6d493bfe0Timo Sirainen
717bb0dbaf4bd3f745669570647845e6d493bfe0Timo Sirainen if (size > SSIZE_T_MAX)
717bb0dbaf4bd3f745669570647845e6d493bfe0Timo Sirainen size = SSIZE_T_MAX;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (size == 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = buf->file ? my_write(buf->fd, data, size) :
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen net_transmit(buf->fd, data, size);
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen if (ret < 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* disconnected */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->buf_errno = errno;
cec3230c9b2a96bac1ea42c69475e8aea4b91eabTimo Sirainen io_buffer_close(buf);
cec3230c9b2a96bac1ea42c69475e8aea4b91eabTimo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->offset += ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void buf_send_real(IOBuffer *buf)
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen{
cec3230c9b2a96bac1ea42c69475e8aea4b91eabTimo Sirainen ssize_t ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = io_buffer_write(buf, buf->buffer + buf->skip,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->pos - buf->skip);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (ret < 0)
54533aa265f5c87730022cc7576090bc51370f97Timo Sirainen return;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->skip += ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (buf->skip == buf->pos) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* everything sent */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->skip = buf->pos = 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* call flush function */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (buf->flush_func != NULL) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->flush_func(buf->flush_context, buf);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->flush_func = NULL;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (buf->corked) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* remove cork */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (!buf->file)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen net_set_cork(buf->fd, FALSE);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->corked = FALSE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int buf_send(IOBuffer *buf)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf_send_real(buf);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (buf->closed || buf->pos == 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen io_remove(buf->io);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->io = NULL;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return FALSE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return TRUE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#define IOBUFFER_IS_FULL(buf) \
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ((buf)->pos == (buf)->buffer_size)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen/* write only as much as needed, put the rest into buffer.
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen write() only full buffers. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void block_loop_send(IOBufferBlockContext *ctx)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size_t size, buffer_space_left;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ssize_t ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size = MAX_SSIZE_T(ctx->size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buffer_space_left = ctx->outbuf->buffer_size - ctx->outbuf->pos;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (ctx->outbuf->pos != 0 || ctx->size < buffer_space_left) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (buffer_space_left > 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* we have space in the buffer, fill it before
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen writing */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (size > buffer_space_left)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen size = buffer_space_left;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen memcpy(ctx->outbuf->buffer + ctx->outbuf->pos,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ctx->data, size);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ctx->outbuf->pos += size;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ctx->data += size;
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen ctx->size -= size;
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen }
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen if (IOBUFFER_IS_FULL(ctx->outbuf))
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen buf_send_real(ctx->outbuf);
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen } else {
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen ret = io_buffer_write(ctx->outbuf, ctx->data, size);
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen if (ret > 0) {
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen ctx->data += ret;
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen ctx->size -= ret;
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen }
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen }
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen if (ctx->outbuf->closed || (ctx->size == 0 && ctx->last_block &&
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen !IOBUFFER_IS_FULL(ctx->outbuf)))
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen io_loop_stop(ctx->ioloop);
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen}
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen/* write out all data from buffer */
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainenstatic void block_loop_flush(IOBufferBlockContext *ctx)
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen{
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen if (ctx->outbuf->skip != ctx->outbuf->pos)
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen buf_send_real(ctx->outbuf);
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen if (ctx->outbuf->closed || ctx->outbuf->skip == ctx->outbuf->pos)
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen io_loop_stop(ctx->ioloop);
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen}
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen
45155bb1250cf5a120278f349465aded513a100fTimo Sirainen/* this can be called with both io_buffer_ioloop() or
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen io_buffer_read_blocking() */
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainenstatic void block_loop_timeout(void *context, Timeout timeout __attr_unused__)
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen{
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen IOBufferBlockContext *ctx = context;
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen ctx->timeout = TRUE;
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen io_loop_stop(ctx->ioloop);
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen}
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen
45155bb1250cf5a120278f349465aded513a100fTimo Sirainenstatic int io_buffer_ioloop(IOBuffer *buf, IOBufferBlockContext *ctx,
45155bb1250cf5a120278f349465aded513a100fTimo Sirainen void (*send_func)(IOBufferBlockContext *ctx))
45155bb1250cf5a120278f349465aded513a100fTimo Sirainen{
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen Timeout to;
5a250816ffc4cc5db203f9410ea99b6601c7b91aTimo Sirainen int save_errno;
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen /* close old IO */
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen if (buf->io != NULL)
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen io_remove(buf->io);
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen t_push();
12cf3d0e03fc70fb0c8b91bc8fd83b4e14d7cdefTimo Sirainen
12cf3d0e03fc70fb0c8b91bc8fd83b4e14d7cdefTimo Sirainen /* create a new I/O loop */
4ead43ecc06d10047998966c4dc0b142ecce4b66Timo Sirainen ctx->ioloop = io_loop_create(data_stack_pool);
12cf3d0e03fc70fb0c8b91bc8fd83b4e14d7cdefTimo Sirainen ctx->outbuf = buf;
4e35bae013cee5a06d281776a347b534b958aaa4Timo Sirainen
4ead43ecc06d10047998966c4dc0b142ecce4b66Timo Sirainen buf->io = io_add(buf->fd, IO_WRITE, (IOFunc) send_func, ctx);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen to = buf->timeout_msecs <= 0 ? NULL :
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen timeout_add(buf->timeout_msecs, block_loop_timeout, ctx);
bb8d0ec26bdd548624d7a7424071cca693b72f55Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen io_loop_run(ctx->ioloop);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen save_errno = errno;
bb8d0ec26bdd548624d7a7424071cca693b72f55Timo Sirainen
bb8d0ec26bdd548624d7a7424071cca693b72f55Timo Sirainen if (buf->io != NULL) {
bb8d0ec26bdd548624d7a7424071cca693b72f55Timo Sirainen io_remove(buf->io);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->io = NULL;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
4ead43ecc06d10047998966c4dc0b142ecce4b66Timo Sirainen
506e41a4efbc7f4bba93cd295ca4dba18ed3cf09Timo Sirainen if (to != NULL) {
506e41a4efbc7f4bba93cd295ca4dba18ed3cf09Timo Sirainen if (ctx->timeout && buf->timeout_func != NULL) {
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen /* call user-given timeout function */
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen buf->timeout_func(buf->timeout_context, to);
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen }
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen timeout_remove(to);
1d4f710106fb498750456724628da6063e012e6dTimo Sirainen }
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen io_loop_destroy(ctx->ioloop);
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen t_pop();
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen errno = save_errno;
88c92ce2caa8f9fa34708471c6ed4e974d5a7953Timo Sirainen return ctx->size > 0 ? -1 : 1;
88c92ce2caa8f9fa34708471c6ed4e974d5a7953Timo Sirainen}
88c92ce2caa8f9fa34708471c6ed4e974d5a7953Timo Sirainen
88c92ce2caa8f9fa34708471c6ed4e974d5a7953Timo Sirainenstatic int io_buffer_send_blocking(IOBuffer *buf, const void *data,
88c92ce2caa8f9fa34708471c6ed4e974d5a7953Timo Sirainen size_t size)
88c92ce2caa8f9fa34708471c6ed4e974d5a7953Timo Sirainen{
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen IOBufferBlockContext ctx;
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen memset(&ctx, 0, sizeof(ctx));
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen
1d4f710106fb498750456724628da6063e012e6dTimo Sirainen ctx.data = data;
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen ctx.size = size;
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen ctx.last_block = TRUE;
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen return io_buffer_ioloop(buf, &ctx, block_loop_send);
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen}
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainenstatic int io_buffer_flush(IOBuffer *buf)
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen{
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen IOBufferBlockContext ctx;
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen ssize_t ret;
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen if (buf->skip == buf->pos)
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen return 1;
1d4f710106fb498750456724628da6063e012e6dTimo Sirainen
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen ret = io_buffer_write(buf, buf->buffer + buf->skip,
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen buf->pos - buf->skip);
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen if (ret < 0)
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen return -1;
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen buf->skip += ret;
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen if (buf->skip == buf->pos)
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen return 1;
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen memset(&ctx, 0, sizeof(ctx));
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen ctx.last_block = TRUE;
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen return io_buffer_ioloop(buf, &ctx, block_loop_flush);
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen}
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainenvoid io_buffer_cork(IOBuffer *buf)
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen{
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen i_assert(!buf->receive);
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen if (!buf->corked) {
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen if (!buf->file)
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen net_set_cork(buf->fd, TRUE);
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen buf->corked = TRUE;
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen }
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen}
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainenstatic void buffer_alloc_more(IOBuffer *buf, size_t size)
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen{
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen i_assert(!buf->mmaped);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen buf->buffer_size = buf->pos+size;
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen buf->buffer_size =
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen buf->buffer_size <= IO_BUFFER_MIN_SIZE ? IO_BUFFER_MIN_SIZE :
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen nearest_power(buf->buffer_size);
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen if (buf->max_buffer_size > 0 && buf->buffer_size > buf->max_buffer_size)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf->buffer_size = buf->max_buffer_size;
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen buf->buffer = p_realloc(buf->pool, buf->buffer, buf->buffer_size);
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen if (buf->buffer == NULL) {
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen /* pool limit exceeded */
1d4f710106fb498750456724628da6063e012e6dTimo Sirainen buf->pos = buf->buffer_size = 0;
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen }
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen}
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainenstatic void io_buffer_compress(IOBuffer *buf)
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen{
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen memmove(buf->buffer, buf->buffer + buf->skip,
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen buf->pos - buf->skip);
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen buf->pos -= buf->skip;
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen if (buf->skip > buf->cr_lookup_pos)
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen buf->cr_lookup_pos = 0;
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen else
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen buf->cr_lookup_pos -= buf->skip;
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen buf->skip = 0;
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen}
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainenint io_buffer_send(IOBuffer *buf, const void *data, size_t size)
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen{
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen ssize_t ret;
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen int i, corked;
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen i_assert(!buf->receive);
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen i_assert(data != NULL);
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen i_assert(size <= SSIZE_T_MAX);
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen buf->transmit = TRUE;
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen if (buf->closed)
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen return -1;
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen
38318f5e82662615cd88e99e398efe4a630ce020Timo Sirainen /* if we're corked, first try adding it to buffer. if it's larger
38318f5e82662615cd88e99e398efe4a630ce020Timo Sirainen than the buffer, send it immediately. */
38318f5e82662615cd88e99e398efe4a630ce020Timo Sirainen corked = buf->corked;
28dae6a0064e79f86da091625b0f2b92336a2a91Timo Sirainen for (i = 0; i < 2; i++) {
28dae6a0064e79f86da091625b0f2b92336a2a91Timo Sirainen if (buf->pos == 0 && !corked) {
28dae6a0064e79f86da091625b0f2b92336a2a91Timo Sirainen /* buffer is empty, try to send the data immediately */
434abef12f61881a5cfa28d27193d0854a9639a0Timo Sirainen ret = io_buffer_write(buf, data, size);
434abef12f61881a5cfa28d27193d0854a9639a0Timo Sirainen if (ret < 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
38318f5e82662615cd88e99e398efe4a630ce020Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen data = (const char *) data + ret;
38318f5e82662615cd88e99e398efe4a630ce020Timo Sirainen size -= ret;
38318f5e82662615cd88e99e398efe4a630ce020Timo Sirainen }
38318f5e82662615cd88e99e398efe4a630ce020Timo Sirainen
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen if (size == 0) {
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen /* all sent */
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen return 1;
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen }
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen
28dae6a0064e79f86da091625b0f2b92336a2a91Timo Sirainen if (io_buffer_get_space(buf, size) != NULL)
28dae6a0064e79f86da091625b0f2b92336a2a91Timo Sirainen break;
28dae6a0064e79f86da091625b0f2b92336a2a91Timo Sirainen
28dae6a0064e79f86da091625b0f2b92336a2a91Timo Sirainen if (corked)
38318f5e82662615cd88e99e398efe4a630ce020Timo Sirainen corked = FALSE;
38318f5e82662615cd88e99e398efe4a630ce020Timo Sirainen else {
38318f5e82662615cd88e99e398efe4a630ce020Timo Sirainen if (buf->blocking) {
38318f5e82662615cd88e99e398efe4a630ce020Timo Sirainen /* if we don't have space, we block */
b4b87fa19d26aadb2ea9e8a9ae7af6cfdaab4cfbTimo Sirainen return io_buffer_send_blocking(buf, data, size);
b4b87fa19d26aadb2ea9e8a9ae7af6cfdaab4cfbTimo Sirainen }
38318f5e82662615cd88e99e398efe4a630ce020Timo Sirainen return -2;
3ffb7fd86484c474b42f3f1e981ab0f7168b5df9Timo Sirainen }
3ffb7fd86484c474b42f3f1e981ab0f7168b5df9Timo Sirainen }
6998ca95b4947c90647ac5d4794ebd6311acada2Timo Sirainen
6998ca95b4947c90647ac5d4794ebd6311acada2Timo Sirainen i_assert(buf->pos + size <= buf->buffer_size);
6998ca95b4947c90647ac5d4794ebd6311acada2Timo Sirainen
6998ca95b4947c90647ac5d4794ebd6311acada2Timo Sirainen /* add to buffer */
28dae6a0064e79f86da091625b0f2b92336a2a91Timo Sirainen memcpy(buf->buffer + buf->pos, data, size);
28dae6a0064e79f86da091625b0f2b92336a2a91Timo Sirainen buf->pos += size;
28dae6a0064e79f86da091625b0f2b92336a2a91Timo Sirainen
28dae6a0064e79f86da091625b0f2b92336a2a91Timo Sirainen if (buf->io == NULL && !buf->corked) {
28dae6a0064e79f86da091625b0f2b92336a2a91Timo Sirainen buf->io = io_add_priority(buf->fd, buf->priority, IO_WRITE,
28dae6a0064e79f86da091625b0f2b92336a2a91Timo Sirainen (IOFunc) buf_send, buf);
28dae6a0064e79f86da091625b0f2b92336a2a91Timo Sirainen }
28dae6a0064e79f86da091625b0f2b92336a2a91Timo Sirainen return 1;
3ffb7fd86484c474b42f3f1e981ab0f7168b5df9Timo Sirainen}
3ffb7fd86484c474b42f3f1e981ab0f7168b5df9Timo Sirainen
7c849dbc7be089175c1a83a84ee7249ed695810dTimo Sirainenstatic void block_loop_sendfile(IOBufferBlockContext *ctx)
7c849dbc7be089175c1a83a84ee7249ed695810dTimo Sirainen{
38318f5e82662615cd88e99e398efe4a630ce020Timo Sirainen uoff_t offset;
80980955bb1bbcc1bd73623fe0912f334194ddd2Timo Sirainen ssize_t ret;
80980955bb1bbcc1bd73623fe0912f334194ddd2Timo Sirainen
843640f0ca224bb9999acb290bca5f76037ab984Timo Sirainen i_assert(ctx->inbuf->offset < OFF_T_MAX);
843640f0ca224bb9999acb290bca5f76037ab984Timo Sirainen
843640f0ca224bb9999acb290bca5f76037ab984Timo Sirainen offset = ctx->inbuf->offset;
843640f0ca224bb9999acb290bca5f76037ab984Timo Sirainen ret = safe_sendfile(ctx->outbuf->fd, ctx->inbuf->fd, &offset,
28dae6a0064e79f86da091625b0f2b92336a2a91Timo Sirainen MAX_SSIZE_T(ctx->size));
28dae6a0064e79f86da091625b0f2b92336a2a91Timo Sirainen if (ret < 0) {
38318f5e82662615cd88e99e398efe4a630ce020Timo Sirainen if (errno != EINTR && errno != EAGAIN) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ctx->outbuf->buf_errno = errno;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen io_buffer_close(ctx->outbuf);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen io_buffer_skip(ctx->inbuf, (size_t)ret);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ctx->outbuf->offset += ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ctx->size -= ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (ctx->outbuf->closed || ctx->size == 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen io_loop_stop(ctx->ioloop);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int io_buffer_sendfile(IOBuffer *outbuf, IOBuffer *inbuf,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen uoff_t long_size)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen IOBufferBlockContext ctx;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen uoff_t offset;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ssize_t ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_assert(inbuf->offset < OFF_T_MAX);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* flush out any data in buffer */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (io_buffer_flush(outbuf) < 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* first try if we can do it with a single sendfile() call */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen offset = inbuf->offset;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = safe_sendfile(outbuf->fd, inbuf->fd, &offset,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen MAX_SSIZE_T(long_size));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (ret < 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (errno != EINTR && errno != EAGAIN) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen outbuf->buf_errno = errno;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen io_buffer_skip(inbuf, (size_t)ret);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen outbuf->offset += ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if ((uoff_t) ret == long_size) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* yes, all sent */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return 1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen memset(&ctx, 0, sizeof(ctx));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ctx.inbuf = inbuf;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ctx.size = long_size - ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
ret = io_buffer_ioloop(outbuf, &ctx, block_loop_sendfile);
if (ret < 0 && outbuf->buf_errno == EINVAL) {
/* this shouldn't happen, must be a bug. It would also
mess up later if we let this pass. */
i_panic("io_buffer_sendfile() failed: %m");
}
return ret;
}
static void block_loop_copy(IOBufferBlockContext *ctx)
{
unsigned char *in_data;
size_t size, full_size, sent_size, data_size;
if (io_buffer_read_data_blocking(ctx->inbuf, &in_data, &size, 0) < 0) {
io_loop_stop(ctx->ioloop);
return;
}
full_size = ctx->size;
data_size = size < full_size ? size : full_size;
/* send the data */
ctx->size = data_size;
ctx->data = (const char *) in_data;
ctx->last_block = data_size == full_size;
block_loop_send(ctx);
/* ctx->size now contains number of bytes unsent */
sent_size = data_size - ctx->size;
ctx->size = full_size - sent_size;
io_buffer_skip(ctx->inbuf, sent_size);
}
int io_buffer_send_iobuffer(IOBuffer *outbuf, IOBuffer *inbuf, uoff_t size)
{
IOBufferBlockContext ctx;
int ret;
i_assert(size < OFF_T_MAX);
i_assert(inbuf->limit > 0 || size <= inbuf->limit - inbuf->offset);
if (inbuf->closed || outbuf->closed)
return -1;
ret = io_buffer_sendfile(outbuf, inbuf, size);
if (ret > 0 || outbuf->buf_errno != EINVAL)
return ret < 0 ? -1 : 1;
/* sendfile() not supported (with this fd), fallback to
regular sending */
/* create blocking send loop */
memset(&ctx, 0, sizeof(ctx));
ctx.inbuf = inbuf;
ctx.size = size;
return io_buffer_ioloop(outbuf, &ctx, block_loop_copy);
}
void io_buffer_send_flush(IOBuffer *buf)
{
i_assert(!buf->receive);
if (buf->closed)
return;
io_buffer_flush(buf);
if (buf->corked) {
/* remove cork */
if (!buf->file)
net_set_cork(buf->fd, FALSE);
buf->corked = FALSE;
}
}
void io_buffer_send_flush_callback(IOBuffer *buf, IOBufferFlushFunc func,
void *context)
{
i_assert(!buf->receive);
if (buf->skip == buf->pos) {
func(context, buf);
return;
}
buf->flush_func = func;
buf->flush_context = context;
/* if we're corked, the io wasn't set */
if (buf->io == NULL) {
buf->io = io_add_priority(buf->fd, buf->priority, IO_WRITE,
(IOFunc) buf_send, buf);
}
}
static ssize_t io_buffer_set_mmaped_pos(IOBuffer *buf)
{
i_assert((uoff_t)buf->mmap_offset <= buf->start_offset + buf->limit);
buf->pos = buf->start_offset + buf->limit - buf->mmap_offset;
if (buf->pos > buf->buffer_size)
buf->pos = buf->buffer_size;
return buf->pos - buf->skip;
}
static ssize_t io_buffer_read_mmaped(IOBuffer *buf)
{
size_t aligned_skip;
if (buf->start_offset + buf->limit <=
(uoff_t)buf->mmap_offset + buf->pos) {
/* end of file */
return -1;
}
if (buf->pos < buf->buffer_size) {
/* more bytes available without needing to mmap() */
return io_buffer_set_mmaped_pos(buf);
}
aligned_skip = buf->skip & ~mmap_pagemask;
if (aligned_skip == 0 && buf->buffer != NULL) {
/* didn't skip enough bytes */
return -2;
}
buf->skip -= aligned_skip;
buf->mmap_offset += aligned_skip;
if (buf->buffer != NULL) {
if (munmap(buf->buffer, buf->buffer_size) < 0)
i_error("io_buffer_read_mmaped(): munmap() failed: %m");
}
buf->buffer_size = buf->start_offset + buf->size - buf->mmap_offset;
if (buf->buffer_size > buf->max_buffer_size)
buf->buffer_size = buf->max_buffer_size;
i_assert((uoff_t)buf->mmap_offset + buf->buffer_size <=
buf->start_offset + buf->size);
buf->buffer = mmap(NULL, buf->buffer_size, PROT_READ, MAP_PRIVATE,
buf->fd, buf->mmap_offset);
if (buf->buffer == MAP_FAILED) {
buf->buf_errno = errno;
buf->buffer = NULL;
buf->buffer_size = 0;
buf->skip = buf->pos;
i_error("io_buffer_read_mmaped(): mmap() failed: %m");
return -1;
}
/* madvise() only if the mmap()ed area was larger than page size */
if (buf->buffer_size > mmap_pagesize) {
(void)madvise((void *) buf->buffer, buf->buffer_size,
MADV_SEQUENTIAL);
}
return io_buffer_set_mmaped_pos(buf);
}
void io_buffer_set_start_offset(IOBuffer *buf, uoff_t offset)
{
off_t diff;
i_assert(offset <= buf->size);
if (offset == buf->start_offset)
return;
diff = (off_t)buf->start_offset - (off_t)offset;
buf->start_offset = offset;
buf->size += diff;
buf->limit += diff;
io_buffer_reset(buf);
buf->skip = buf->pos = buf->start_offset;
}
void io_buffer_set_read_limit(IOBuffer *buf, uoff_t offset)
{
i_assert(offset <= buf->size);
if (offset == 0)
buf->limit = buf->size;
else {
i_assert(offset >= buf->offset);
buf->limit = offset;
if (buf->offset + (buf->pos - buf->skip) > offset)
buf->pos = offset - buf->offset + buf->skip;
}
}
ssize_t io_buffer_read(IOBuffer *buf)
{
size_t size;
ssize_t ret;
i_assert(!buf->transmit);
buf->receive = TRUE;
if (buf->closed)
return -1;
if (buf->mmaped)
return io_buffer_read_mmaped(buf);
if (buf->pos == buf->buffer_size) {
if (buf->skip > 0) {
/* remove the unused bytes from beginning of buffer */
io_buffer_compress(buf);
} else if (buf->max_buffer_size == 0 ||
buf->buffer_size < buf->max_buffer_size) {
/* buffer is full - grow it */
buffer_alloc_more(buf, IO_BUFFER_MIN_SIZE);
}
if (buf->pos == buf->buffer_size)
return -2; /* buffer full */
}
size = buf->buffer_size - buf->pos;
if (buf->limit > 0) {
i_assert(buf->limit >= buf->offset);
if (size >= buf->limit - buf->offset) {
size = buf->limit - buf->offset;
if (size == 0)
return -1;
}
}
/* fill the buffer */
if (!buf->file) {
ret = net_receive(buf->fd, buf->buffer + buf->pos, size);
} else {
ret = read(buf->fd, buf->buffer + buf->pos, size);
if (ret == 0)
ret = -1; /* EOF */
else if (ret < 0 && (errno == EINTR || errno == EAGAIN))
ret = 0;
}
if (ret < 0) {
/* disconnected */
buf->buf_errno = errno;
return -1;
}
buf->pos += ret;
return ret;
}
static void io_read_data(void *context, int fd __attr_unused__,
IO io __attr_unused__)
{
IOBufferBlockContext *ctx = context;
if (io_buffer_read(ctx->inbuf) != 0) {
/* got data / error */
io_loop_stop(ctx->ioloop);
}
}
ssize_t io_buffer_read_blocking(IOBuffer *buf)
{
IOBufferBlockContext ctx;
Pool pool;
Timeout to;
ssize_t ret;
/* first check if we can get some data */
ret = io_buffer_read(buf);
if (ret != 0)
return ret;
/* blocking now */
/* create a new I/O loop */
memset(&ctx, 0, sizeof(ctx));
pool = pool_create("io_buffer_read_blocking", 1024, FALSE);
ctx.ioloop = io_loop_create(pool);
ctx.inbuf = buf;
buf->io = io_add(buf->fd, IO_READ, io_read_data, &ctx);
to = buf->timeout_msecs <= 0 ? NULL :
timeout_add(buf->timeout_msecs, block_loop_timeout, &ctx);
io_loop_run(ctx.ioloop);
if (buf->io != NULL) {
io_remove(buf->io);
buf->io = NULL;
}
if (to != NULL) {
if (ctx.timeout && buf->timeout_func != NULL) {
/* call user-given timeout function */
buf->timeout_func(buf->timeout_context, to);
}
timeout_remove(to);
}
io_loop_destroy(ctx.ioloop);
pool_unref(pool);
return buf->pos > buf->skip ?
(ssize_t) (buf->pos-buf->skip) : -1;
}
void io_buffer_skip(IOBuffer *buf, uoff_t count)
{
uoff_t old_limit;
ssize_t ret;
buf->offset += count;
if (count <= buf->pos - buf->skip) {
buf->skip += count;
return;
}
if (buf->mmaped) {
/* these point outside mmap now, next io_buffer_read_mmaped()
will fix them */
buf->skip += count;
buf->pos = buf->skip;
} else {
if (buf->buffer_size == 0)
buffer_alloc_more(buf, IO_BUFFER_MIN_SIZE);
count -= buf->skip;
old_limit = buf->limit;
io_buffer_set_read_limit(buf, buf->offset + count);
while ((ret = io_buffer_read_blocking(buf)) > 0)
io_buffer_skip(buf, ret);
io_buffer_set_read_limit(buf, old_limit);
}
}
int io_buffer_seek(IOBuffer *buf, uoff_t offset)
{
uoff_t real_offset;
off_t ret;
if (buf->closed) {
errno = EBADF;
return FALSE;
}
real_offset = buf->start_offset + offset;
if (real_offset > OFF_T_MAX) {
errno = EINVAL;
return FALSE;
}
if (buf->mmaped) {
/* first reset everything */
io_buffer_reset(buf);
/* then set the wanted position, next read will
pick up from there */
buf->pos = buf->skip = real_offset;
} else {
ret = lseek(buf->fd, (off_t)real_offset, SEEK_SET);
if (ret < 0)
return FALSE;
if (ret != (off_t)real_offset) {
errno = EINVAL;
return FALSE;
}
}
buf->offset = offset;
return TRUE;
}
/* skip the first LF, if it exists */
static void io_buffer_skip_lf(IOBuffer *buf)
{
if (!buf->last_cr || buf->skip >= buf->pos)
return;
if (buf->buffer[buf->skip] == 10) {
if (buf->skip == buf->cr_lookup_pos)
buf->cr_lookup_pos++;
buf->skip++;
buf->offset++;
}
buf->last_cr = FALSE;
}
char *io_buffer_next_line(IOBuffer *buf)
{
/* FIXME: buf->offset isn't updated right.. (skip_lf thing?) */
char *ret_buf;
size_t i;
i_assert(buf != NULL);
io_buffer_skip_lf(buf);
if (buf->skip >= buf->pos)
return NULL;
ret_buf = NULL;
for (i = buf->cr_lookup_pos; i < buf->pos; i++) {
if (buf->buffer[i] == 13 || buf->buffer[i] == 10) {
/* got it */
buf->last_cr = buf->buffer[i] == 13;
buf->buffer[i] = '\0';
ret_buf = (char *) buf->buffer + buf->skip;
i++;
buf->offset += i - buf->skip;
buf->skip = i;
break;
}
}
buf->cr_lookup_pos = i;
return ret_buf;
}
unsigned char *io_buffer_get_data(IOBuffer *buf, size_t *size)
{
io_buffer_skip_lf(buf);
if (buf->skip >= buf->pos) {
*size = 0;
return NULL;
}
*size = buf->pos - buf->skip;
return buf->buffer + buf->skip;
}
int io_buffer_read_data_blocking(IOBuffer *buf, unsigned char **data,
size_t *size, size_t threshold)
{
ssize_t ret;
while (buf->pos - buf->skip <= threshold) {
/* we need more data */
ret = io_buffer_read_blocking(buf);
if (ret < 0) {
if (ret == -2)
return -2;
else
break;
}
}
*data = io_buffer_get_data(buf, size);
return *size > threshold ? 1 : *size > 0 ? 0 : -1;
}
unsigned char *io_buffer_get_space(IOBuffer *buf, size_t size)
{
i_assert(size > 0);
i_assert(size <= SSIZE_T_MAX);
i_assert(!buf->receive);
buf->transmit = TRUE;
/* make sure we have enough space in buffer */
if (buf->buffer_size - buf->pos < size && buf->skip > 0) {
/* remove the unused bytes from beginning of buffer */
io_buffer_compress(buf);
}
if (buf->buffer_size - buf->pos < size &&
(buf->max_buffer_size == 0 ||
size <= buf->max_buffer_size - buf->pos)) {
/* allocate more space */
buffer_alloc_more(buf, size);
}
if (buf->buffer_size - buf->pos < size)
return NULL;
return buf->buffer + buf->pos;
}
int io_buffer_send_buffer(IOBuffer *buf, size_t size)
{
ssize_t ret;
i_assert(size <= SSIZE_T_MAX);
i_assert(!buf->receive);
if (buf->closed)
return -1;
if (buf->pos == 0 && !buf->corked) {
/* buffer is empty, try to send the data immediately */
ret = io_buffer_write(buf, buf->buffer, size);
if (ret < 0)
return -1;
if ((size_t)ret == size) {
/* all sent */
return 1;
}
buf->skip += ret;
}
buf->pos += size;
if (buf->io == NULL && !buf->corked) {
buf->io = io_add_priority(buf->fd, buf->priority, IO_WRITE,
(IOFunc) buf_send, buf);
}
return 1;
}
int io_buffer_set_data(IOBuffer *buf, const void *data, size_t size)
{
i_assert(!buf->mmaped);
io_buffer_reset(buf);
if (buf->buffer_size < size) {
buffer_alloc_more(buf, size);
if (buf->buffer_size < size)
return -2;
}
buf->offset += size;
buf->offset -= buf->pos - buf->skip;
memcpy(buf->buffer, data, size);
buf->pos = size;
buf->skip = 0;
return 1;
}
int io_buffer_is_empty(IOBuffer *buf)
{
return buf->skip >= buf->pos;
}