iobuffer.c revision 62505210a7e6d1b2e35fac335a6c875a7c98ccfb
1d4f710106fb498750456724628da6063e012e6dTimo Sirainen iobuffer.c : Input/output transmit buffer handling
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen Copyright (c) 2002 Timo 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:
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 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.
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#define MAX_SSIZE_T(size) ((size) < SSIZE_T_MAX ? (size_t)(size) : SSIZE_T_MAX)
baebb412a9a5a44b1756e01cfa3b99f5d8a846b6Timo Sirainentypedef struct {
e248fe370c4047cee921a91b48edc37944ab0526Timo SirainenIOBuffer *io_buffer_create(int fd, Pool pool, int priority,
e248fe370c4047cee921a91b48edc37944ab0526Timo SirainenIOBuffer *io_buffer_create_file(int fd, Pool pool, size_t max_buffer_size,
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen buf = io_buffer_create(fd, pool, IO_PRIORITY_DEFAULT, max_buffer_size);
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen buf->close_file = (flags & IOBUFFER_FLAG_AUTOCLOSE) != 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo SirainenIOBuffer *io_buffer_create_mmap(int fd, Pool pool, size_t block_size,
54533aa265f5c87730022cc7576090bc51370f97Timo Sirainen /* block size must be page aligned, and at least two pages long */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen buf = io_buffer_create_file(fd, pool, block_size, flags);
d3d769026fae5d21c2d29614d3bc4579e8d79e81Timo Sirainen i_error("io_buffer_create_mmap(): lseek() failed: %m");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_warning("Trying to create IOBuffer with size "
05e55893a799de645fc8cd2203d6013f0e0f1b79Timo Sirainen if (munmap(buf->buffer, buf->buffer_size) < 0) {
4de2a17e0a2aed3b57a6c1057329b6a132b56ae2Timo Sirainen "munmap() failed: %m");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_error("io_buffer_close(): close() failed: %m");
bdb9f7f7fbf828fb85a393bd2803167b1bb8ff0dTimo Sirainen buf->pos = buf->skip = buf->cr_lookup_pos = 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (munmap(buf->buffer, buf->buffer_size) < 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_error("io_buffer_reset(): munmap() failed: %m");
e248fe370c4047cee921a91b48edc37944ab0526Timo SirainenIOBuffer *io_buffer_set_pool(IOBuffer *buf, Pool pool)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen newbuf->buffer = p_malloc(pool, buf->buffer_size);
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen memcpy(newbuf->buffer, buf->buffer + buf->skip,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid io_buffer_set_max_size(IOBuffer *buf, size_t max_size)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid io_buffer_set_blocking(IOBuffer *buf, size_t max_size,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic ssize_t my_write(int fd, const void *buf, size_t size)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (ret < 0 && (errno == EINTR || errno == EAGAIN))
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic ssize_t io_buffer_write(IOBuffer *buf, const void *data, size_t size)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = buf->file ? my_write(buf->fd, data, size) :
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* disconnected */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = io_buffer_write(buf, buf->buffer + buf->skip,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* everything sent */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* call flush function */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* remove cork */
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 buffer_space_left = ctx->outbuf->buffer_size - ctx->outbuf->pos;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (ctx->outbuf->pos != 0 || ctx->size < buffer_space_left) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* we have space in the buffer, fill it before
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen memcpy(ctx->outbuf->buffer + ctx->outbuf->pos,
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen ret = io_buffer_write(ctx->outbuf, ctx->data, size);
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen if (ctx->outbuf->closed || (ctx->size == 0 && ctx->last_block &&
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen/* write out all data from buffer */
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainenstatic void block_loop_flush(IOBufferBlockContext *ctx)
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen if (ctx->outbuf->closed || ctx->outbuf->skip == ctx->outbuf->pos)
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__)
45155bb1250cf5a120278f349465aded513a100fTimo Sirainenstatic int io_buffer_ioloop(IOBuffer *buf, IOBufferBlockContext *ctx,
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen /* close old IO */
12cf3d0e03fc70fb0c8b91bc8fd83b4e14d7cdefTimo Sirainen /* create a new I/O loop */
4ead43ecc06d10047998966c4dc0b142ecce4b66Timo Sirainen ctx->ioloop = io_loop_create(data_stack_pool);
4ead43ecc06d10047998966c4dc0b142ecce4b66Timo Sirainen buf->io = io_add(buf->fd, IO_WRITE, (IOFunc) send_func, ctx);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen timeout_add(buf->timeout_msecs, block_loop_timeout, ctx);
506e41a4efbc7f4bba93cd295ca4dba18ed3cf09Timo Sirainen if (ctx->timeout && buf->timeout_func != NULL) {
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen /* call user-given timeout function */
88c92ce2caa8f9fa34708471c6ed4e974d5a7953Timo Sirainenstatic int io_buffer_send_blocking(IOBuffer *buf, const void *data,
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen return io_buffer_ioloop(buf, &ctx, block_loop_send);
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen ret = io_buffer_write(buf, buf->buffer + buf->skip,
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen return io_buffer_ioloop(buf, &ctx, block_loop_flush);
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainenstatic void buffer_alloc_more(IOBuffer *buf, size_t size)
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen buf->buffer_size <= IO_BUFFER_MIN_SIZE ? IO_BUFFER_MIN_SIZE :
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen if (buf->max_buffer_size > 0 && buf->buffer_size > buf->max_buffer_size)
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen buf->buffer = p_realloc(buf->pool, buf->buffer, buf->buffer_size);
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen /* pool limit exceeded */
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainenint io_buffer_send(IOBuffer *buf, const void *data, size_t size)
38318f5e82662615cd88e99e398efe4a630ce020Timo Sirainen /* if we're corked, first try adding it to buffer. if it's larger
38318f5e82662615cd88e99e398efe4a630ce020Timo Sirainen than the buffer, send it immediately. */
28dae6a0064e79f86da091625b0f2b92336a2a91Timo Sirainen for (i = 0; i < 2; i++) {
28dae6a0064e79f86da091625b0f2b92336a2a91Timo Sirainen /* buffer is empty, try to send the data immediately */
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen /* all sent */
38318f5e82662615cd88e99e398efe4a630ce020Timo Sirainen /* if we don't have space, we block */
b4b87fa19d26aadb2ea9e8a9ae7af6cfdaab4cfbTimo Sirainen return io_buffer_send_blocking(buf, data, size);
6998ca95b4947c90647ac5d4794ebd6311acada2Timo Sirainen i_assert(buf->pos + size <= buf->buffer_size);
6998ca95b4947c90647ac5d4794ebd6311acada2Timo Sirainen /* add to buffer */
28dae6a0064e79f86da091625b0f2b92336a2a91Timo Sirainen buf->io = io_add_priority(buf->fd, buf->priority, IO_WRITE,
7c849dbc7be089175c1a83a84ee7249ed695810dTimo Sirainenstatic void block_loop_sendfile(IOBufferBlockContext *ctx)
843640f0ca224bb9999acb290bca5f76037ab984Timo Sirainen ret = safe_sendfile(ctx->outbuf->fd, ctx->inbuf->fd, &offset,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int io_buffer_sendfile(IOBuffer *outbuf, IOBuffer *inbuf,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* flush out any data in buffer */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* first try if we can do it with a single sendfile() call */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = safe_sendfile(outbuf->fd, inbuf->fd, &offset,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* yes, all sent */
return ret;
unsigned char *in_data;
int ret;
void *context)
if (offset == 0)
if (size == 0)
if (ret == 0)
ret = 0;
if (ret < 0) {
return ret;
if (ret != 0)
return ret;
return FALSE;
return FALSE;
if (ret < 0)
return FALSE;
return FALSE;
return TRUE;
char *ret_buf;
size_t i;
return NULL;
return ret_buf;
*size = 0;
return NULL;
if (ret < 0) {
return NULL;
if (ret < 0)