istream.c revision fa39c427573ada77265b833db687ca7e854794a1
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include "lib.h"
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include "ioloop.h"
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include "array.h"
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include "str.h"
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include "memarea.h"
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include "istream-private.h"
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstatic bool i_stream_is_buffer_invalid(const struct istream_private *stream);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenvoid i_stream_set_name(struct istream *stream, const char *name)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_free(stream->real_stream->iostream.name);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->real_stream->iostream.name = i_strdup(name);
0db42260be85e797aa9909a29b20296996f52e75Timo Sirainen}
0db42260be85e797aa9909a29b20296996f52e75Timo Sirainen
0db42260be85e797aa9909a29b20296996f52e75Timo Sirainenconst char *i_stream_get_name(struct istream *stream)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen while (stream->real_stream->iostream.name == NULL) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream = stream->real_stream->parent;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (stream == NULL)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return "";
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return stream->real_stream->iostream.name;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstatic void i_stream_close_full(struct istream *stream, bool close_parents)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen io_stream_close(&stream->real_stream->iostream, close_parents);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->closed = TRUE;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
46e917c9fa05cbe7bddf805d3a9838b61e3960e1Timo Sirainen if (stream->stream_errno == 0)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->stream_errno = EPIPE;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
95d62f8d6d281cc488dc4f488d4388701e559012Josef 'Jeff' Sipek
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenvoid i_stream_destroy(struct istream **stream)
46e917c9fa05cbe7bddf805d3a9838b61e3960e1Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (*stream == NULL)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_stream_close_full(*stream, FALSE);
b49aa341d28c0eec1229e30baa2f89d5bae52ff8Phil Carmody i_stream_unref(stream);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenvoid i_stream_ref(struct istream *stream)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen io_stream_ref(&stream->real_stream->iostream);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenvoid i_stream_unref(struct istream **stream)
16cb5d65265dd0b216542803fd80c4b999ae118eTimo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen struct istream_private *_stream;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (*stream == NULL)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch _stream = (*stream)->real_stream;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (_stream->iostream.refcount == 1) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (_stream->line_str != NULL)
0ca3b9cb0f2a322a25ce7f229dc3d3a0b46be17bTimo Sirainen str_free(&_stream->line_str);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_stream_snapshot_free(&_stream->prev_snapshot);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
7a88e726e7300fb0273cb4e55b43c27fbd90bdbdTimo Sirainen if (!io_stream_unref(&(*stream)->real_stream->iostream)) {
16cb5d65265dd0b216542803fd80c4b999ae118eTimo Sirainen i_stream_unref(&(*stream)->real_stream->parent);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen io_stream_free(&(*stream)->real_stream->iostream);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
16cb5d65265dd0b216542803fd80c4b999ae118eTimo Sirainen *stream = NULL;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
16cb5d65265dd0b216542803fd80c4b999ae118eTimo Sirainen#undef i_stream_add_destroy_callback
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenvoid i_stream_add_destroy_callback(struct istream *stream,
16cb5d65265dd0b216542803fd80c4b999ae118eTimo Sirainen istream_callback_t *callback, void *context)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
16cb5d65265dd0b216542803fd80c4b999ae118eTimo Sirainen io_stream_add_destroy_callback(&stream->real_stream->iostream,
56d1345c43bbd28c36b7faa85e4163bd9e874290Timo Sirainen callback, context);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenvoid i_stream_remove_destroy_callback(struct istream *stream,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen void (*callback)())
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen io_stream_remove_destroy_callback(&stream->real_stream->iostream,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen callback);
16cb5d65265dd0b216542803fd80c4b999ae118eTimo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenint i_stream_get_fd(struct istream *stream)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen struct istream_private *_stream = stream->real_stream;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return _stream->fd;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
56aa97d74071f3a2987140c2ff1cfd5a59cb35aaTimo Sirainenconst char *i_stream_get_error(struct istream *stream)
56aa97d74071f3a2987140c2ff1cfd5a59cb35aaTimo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen struct istream *s;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* we'll only return errors for streams that have stream_errno set or
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen that have reached EOF. we might be returning unintended error
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen otherwise. */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (stream->stream_errno == 0)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return stream->eof ? "EOF" : "<no error>";
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen for (s = stream; s != NULL; s = s->real_stream->parent) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (s->stream_errno == 0)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen break;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (s->real_stream->iostream.error != NULL)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return s->real_stream->iostream.error;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return strerror(stream->stream_errno);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenconst char *i_stream_get_disconnect_reason(struct istream *stream)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return io_stream_get_disconnect_reason(stream, NULL);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenvoid i_stream_close(struct istream *stream)
95d62f8d6d281cc488dc4f488d4388701e559012Josef 'Jeff' Sipek{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_stream_close_full(stream, TRUE);
8c909e451d14075c05d90382cf8eebc4e354f569Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenvoid i_stream_set_init_buffer_size(struct istream *stream, size_t size)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->real_stream->init_buffer_size = size;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenvoid i_stream_set_max_buffer_size(struct istream *stream, size_t max_size)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen io_stream_set_max_buffer_size(&stream->real_stream->iostream, max_size);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainensize_t i_stream_get_max_buffer_size(struct istream *stream)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen size_t max_size = 0;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen do {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (max_size < stream->real_stream->max_buffer_size)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen max_size = stream->real_stream->max_buffer_size;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream = stream->real_stream->parent;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen } while (stream != NULL);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return max_size;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenvoid i_stream_set_return_partial_line(struct istream *stream, bool set)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->real_stream->return_nolf_line = set;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenvoid i_stream_set_persistent_buffers(struct istream *stream, bool set)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen do {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->real_stream->nonpersistent_buffers = !set;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream = stream->real_stream->parent;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen } while (stream != NULL);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenvoid i_stream_set_blocking(struct istream *stream, bool blocking)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen int prev_fd = -1;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen do {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->blocking = blocking;
95d62f8d6d281cc488dc4f488d4388701e559012Josef 'Jeff' Sipek if (stream->real_stream->fd != -1 &&
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->real_stream->fd != prev_fd) {
9a1f68e5ab08eabd352d533315cba1c69006e2c1Timo Sirainen fd_set_nonblock(stream->real_stream->fd, !blocking);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen prev_fd = stream->real_stream->fd;
bdb026e2dc8a7c77585ed5ba489f0056df8074d4Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream = stream->real_stream->parent;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen } while (stream != NULL);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstatic void i_stream_update(struct istream_private *stream)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
16cb5d65265dd0b216542803fd80c4b999ae118eTimo Sirainen if (stream->parent == NULL)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->access_counter++;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen else {
60d1fdf2c17fd0c7020234590dbd73da81c3ce8fTimo Sirainen stream->access_counter =
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->parent->real_stream->access_counter;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->parent_expected_offset = stream->parent->v_offset;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
95d62f8d6d281cc488dc4f488d4388701e559012Josef 'Jeff' Sipekstatic bool snapshot_has_memarea(struct istream_snapshot *snapshot,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen struct memarea *memarea)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (snapshot->old_memarea == memarea)
5cdb246858f37469fe61351dbc147dabbdde342cTimo Sirainen return TRUE;
5cdb246858f37469fe61351dbc147dabbdde342cTimo Sirainen if (snapshot->prev_snapshot != NULL)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return snapshot_has_memarea(snapshot->prev_snapshot, memarea);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return FALSE;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstruct istream_snapshot *
01435c38e7d671d5a892c4b802cfb204881cd454Timo Siraineni_stream_default_snapshot(struct istream_private *stream,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen struct istream_snapshot *prev_snapshot)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen struct istream_snapshot *snapshot;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (stream->memarea != NULL) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (prev_snapshot != NULL) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (snapshot_has_memarea(prev_snapshot, stream->memarea))
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return prev_snapshot;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* This stream has a memarea. Reference it, so we can later on
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen rollback if needed. */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen snapshot = i_new(struct istream_snapshot, 1);
5d4a0dac041964a04405bc6b94de51315ca917afTimo Sirainen snapshot->old_memarea = stream->memarea;
5d4a0dac041964a04405bc6b94de51315ca917afTimo Sirainen snapshot->prev_snapshot = prev_snapshot;
5d4a0dac041964a04405bc6b94de51315ca917afTimo Sirainen memarea_ref(snapshot->old_memarea);
5d4a0dac041964a04405bc6b94de51315ca917afTimo Sirainen return snapshot;
5d4a0dac041964a04405bc6b94de51315ca917afTimo Sirainen }
5d4a0dac041964a04405bc6b94de51315ca917afTimo Sirainen if (stream->parent == NULL) {
5d4a0dac041964a04405bc6b94de51315ca917afTimo Sirainen i_panic("%s is missing istream.snapshot() implementation",
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_stream_get_name(&stream->istream));
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
050acc3755ccc5bcf261a6de3250164a54a0b6f3Timo Sirainen struct istream_private *_parent_stream =
050acc3755ccc5bcf261a6de3250164a54a0b6f3Timo Sirainen stream->parent->real_stream;
050acc3755ccc5bcf261a6de3250164a54a0b6f3Timo Sirainen return _parent_stream->snapshot(_parent_stream, prev_snapshot);
050acc3755ccc5bcf261a6de3250164a54a0b6f3Timo Sirainen}
95d62f8d6d281cc488dc4f488d4388701e559012Josef 'Jeff' Sipek
050acc3755ccc5bcf261a6de3250164a54a0b6f3Timo Sirainenvoid i_stream_snapshot_free(struct istream_snapshot **_snapshot)
050acc3755ccc5bcf261a6de3250164a54a0b6f3Timo Sirainen{
050acc3755ccc5bcf261a6de3250164a54a0b6f3Timo Sirainen struct istream_snapshot *snapshot = *_snapshot;
050acc3755ccc5bcf261a6de3250164a54a0b6f3Timo Sirainen
050acc3755ccc5bcf261a6de3250164a54a0b6f3Timo Sirainen if (*_snapshot == NULL)
050acc3755ccc5bcf261a6de3250164a54a0b6f3Timo Sirainen return;
050acc3755ccc5bcf261a6de3250164a54a0b6f3Timo Sirainen *_snapshot = NULL;
050acc3755ccc5bcf261a6de3250164a54a0b6f3Timo Sirainen
050acc3755ccc5bcf261a6de3250164a54a0b6f3Timo Sirainen i_stream_snapshot_free(&snapshot->prev_snapshot);
050acc3755ccc5bcf261a6de3250164a54a0b6f3Timo Sirainen if (snapshot->old_memarea != NULL)
cf35bb040d505e2fc333470850c65029992778a4Timo Sirainen memarea_unref(&snapshot->old_memarea);
cf35bb040d505e2fc333470850c65029992778a4Timo Sirainen i_free(snapshot);
cf35bb040d505e2fc333470850c65029992778a4Timo Sirainen}
cf35bb040d505e2fc333470850c65029992778a4Timo Sirainen
cf35bb040d505e2fc333470850c65029992778a4Timo Sirainenstatic struct istream_snapshot *
cf35bb040d505e2fc333470850c65029992778a4Timo Siraineni_stream_noop_snapshot(struct istream_private *stream ATTR_UNUSED,
cf35bb040d505e2fc333470850c65029992778a4Timo Sirainen struct istream_snapshot *prev_snapshot)
cf35bb040d505e2fc333470850c65029992778a4Timo Sirainen{
cf35bb040d505e2fc333470850c65029992778a4Timo Sirainen return prev_snapshot;
cf35bb040d505e2fc333470850c65029992778a4Timo Sirainen}
cf35bb040d505e2fc333470850c65029992778a4Timo Sirainen
cf35bb040d505e2fc333470850c65029992778a4Timo Sirainenssize_t i_stream_read(struct istream *stream)
cf35bb040d505e2fc333470850c65029992778a4Timo Sirainen{
cf35bb040d505e2fc333470850c65029992778a4Timo Sirainen struct istream_private *_stream = stream->real_stream;
cf35bb040d505e2fc333470850c65029992778a4Timo Sirainen ssize_t ret;
cf35bb040d505e2fc333470850c65029992778a4Timo Sirainen#ifdef DEBUG
cf35bb040d505e2fc333470850c65029992778a4Timo Sirainen unsigned char prev_buf[4];
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen const unsigned char *prev_data = _stream->buffer;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen size_t prev_skip = _stream->skip, prev_pos = _stream->pos;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen bool invalid = i_stream_is_buffer_invalid(_stream);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen i_assert(prev_skip <= prev_pos);
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen if (invalid)
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen ;
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen else if (prev_pos - prev_skip <= 4)
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen memcpy(prev_buf, prev_data + prev_skip, prev_pos - prev_skip);
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen else {
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen memcpy(prev_buf, prev_data + prev_skip, 2);
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen memcpy(prev_buf+2, prev_data + prev_pos - 2, 2);
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen }
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen#endif
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen
1ee74d70bb758637e560d556c7240563967d22c8Timo Sirainen _stream->prev_snapshot =
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen _stream->snapshot(_stream, _stream->prev_snapshot);
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen ret = i_stream_read_memarea(stream);
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen if (ret > 0)
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen i_stream_snapshot_free(&_stream->prev_snapshot);
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen#ifdef DEBUG
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen else if (!invalid) {
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen i_assert((_stream->pos - _stream->skip) == (prev_pos - prev_skip));
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen if (prev_pos - prev_skip <= 4)
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen i_assert(memcmp(prev_buf, prev_data + prev_skip, prev_pos - prev_skip) == 0);
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen else {
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen i_assert(memcmp(prev_buf, prev_data + prev_skip, 2) == 0);
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen i_assert(memcmp(prev_buf+2, prev_data + prev_pos - 2, 2) == 0);
1ee74d70bb758637e560d556c7240563967d22c8Timo Sirainen }
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen }
1ee74d70bb758637e560d556c7240563967d22c8Timo Sirainen#endif
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen return ret;
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen}
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainenssize_t i_stream_read_memarea(struct istream *stream)
1ee74d70bb758637e560d556c7240563967d22c8Timo Sirainen{
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen struct istream_private *_stream = stream->real_stream;
1ee74d70bb758637e560d556c7240563967d22c8Timo Sirainen size_t old_size;
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen ssize_t ret;
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (unlikely(stream->closed || stream->stream_errno != 0)) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->eof = TRUE;
95d62f8d6d281cc488dc4f488d4388701e559012Josef 'Jeff' Sipek errno = stream->stream_errno;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return -1;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->eof = FALSE;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (_stream->parent != NULL)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_stream_seek(_stream->parent, _stream->parent_expected_offset);
e130bb802c8bfb6c6cc44e5c8bc098b4fa5af789Timo Sirainen
e130bb802c8bfb6c6cc44e5c8bc098b4fa5af789Timo Sirainen old_size = _stream->pos - _stream->skip;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen ret = _stream->read(_stream);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_assert(old_size <= _stream->pos - _stream->skip);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen switch (ret) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen case -2:
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_assert(_stream->skip != _stream->pos);
c6afd726060aae56b6622c6c52aec10231c4bf1cTimo Sirainen break;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen case -1:
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (stream->stream_errno != 0) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* error handling should be easier if we now just
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen assume the stream is now at EOF */
857c471c13ca215f4be9dd4b336b742b8d434e31Timo Sirainen stream->eof = TRUE;
857c471c13ca215f4be9dd4b336b742b8d434e31Timo Sirainen errno = stream->stream_errno;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen } else {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_assert(stream->eof);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_assert(old_size == _stream->pos - _stream->skip);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen break;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen case 0:
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_assert(!stream->blocking);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen break;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen default:
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_assert(ret > 0);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_assert(_stream->skip < _stream->pos);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_assert((size_t)ret+old_size == _stream->pos - _stream->skip);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen break;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
050acc3755ccc5bcf261a6de3250164a54a0b6f3Timo Sirainen if (stream->stream_errno != 0) {
cf35bb040d505e2fc333470850c65029992778a4Timo Sirainen /* error handling should be easier if we now just
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen assume the stream is now at EOF. Note that we could get here
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen even if read() didn't return -1, although that's a little
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen bit sloppy istream implementation. */
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen stream->eof = TRUE;
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen }
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_stream_update(_stream);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* verify that parents' access_counters are valid. the parent's
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_stream_read() should guarantee this. */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_assert(!i_stream_is_buffer_invalid(_stream));
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return ret;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenint i_stream_read_more_memarea(struct istream *stream,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen const unsigned char **data_r, size_t *size_r)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen *data_r = i_stream_get_data(stream, size_r);
afc77c5375cdb8f2bf0ab6280d9229ac27c933c6Timo Sirainen if (*size_r > 0)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return 1;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen int ret = i_stream_read_memarea(stream);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen *data_r = i_stream_get_data(stream, size_r);
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen return ret;
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen}
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainenssize_t i_stream_read_copy_from_parent(struct istream *istream)
2e708f348f20caa47210cf46cf7654f167fa47bbTimo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen struct istream_private *stream = istream->real_stream;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen size_t pos;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen ssize_t ret;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->pos -= stream->skip;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen stream->skip = 0;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
stream->buffer = i_stream_get_data(stream->parent, &pos);
if (pos > stream->pos)
ret = 0;
else do {
if ((ret = i_stream_read(stream->parent)) == -2) {
i_stream_update(stream);
return -2;
}
stream->istream.stream_errno = stream->parent->stream_errno;
stream->istream.eof = stream->parent->eof;
stream->buffer = i_stream_get_data(stream->parent, &pos);
/* check again, in case the parent stream had been seeked
backwards and the previous read() didn't get us far
enough. */
} while (pos <= stream->pos && ret > 0);
ret = pos > stream->pos ? (ssize_t)(pos - stream->pos) :
(ret == 0 ? 0 : -1);
stream->pos = pos;
i_assert(ret != -1 || stream->istream.eof ||
stream->istream.stream_errno != 0);
i_stream_update(stream);
return ret;
}
void i_stream_free_buffer(struct istream_private *stream)
{
if (stream->memarea != NULL) {
memarea_unref(&stream->memarea);
stream->w_buffer = NULL;
} else if (stream->w_buffer != NULL) {
i_free_and_null(stream->w_buffer);
} else {
/* don't know how to free it */
return;
}
stream->buffer_size = 0;
}
void i_stream_skip(struct istream *stream, uoff_t count)
{
struct istream_private *_stream = stream->real_stream;
size_t data_size;
data_size = _stream->pos - _stream->skip;
if (count <= data_size) {
/* within buffer */
stream->v_offset += count;
_stream->skip += count;
if (_stream->nonpersistent_buffers &&
_stream->skip == _stream->pos) {
_stream->skip = _stream->pos = 0;
i_stream_free_buffer(_stream);
}
return;
}
/* have to seek forward */
count -= data_size;
_stream->skip = _stream->pos;
stream->v_offset += data_size;
if (unlikely(stream->closed || stream->stream_errno != 0))
return;
_stream->seek(_stream, stream->v_offset + count, FALSE);
}
static bool i_stream_can_optimize_seek(struct istream_private *stream)
{
if (stream->parent == NULL)
return TRUE;
/* use the fast route only if the parent stream hasn't been changed */
if (stream->access_counter !=
stream->parent->real_stream->access_counter)
return FALSE;
return i_stream_can_optimize_seek(stream->parent->real_stream);
}
void i_stream_seek(struct istream *stream, uoff_t v_offset)
{
struct istream_private *_stream = stream->real_stream;
if (v_offset >= stream->v_offset &&
i_stream_can_optimize_seek(_stream))
i_stream_skip(stream, v_offset - stream->v_offset);
else {
if (unlikely(stream->closed || stream->stream_errno != 0)) {
stream->eof = TRUE;
return;
}
stream->eof = FALSE;
_stream->seek(_stream, v_offset, FALSE);
}
i_stream_update(_stream);
}
void i_stream_seek_mark(struct istream *stream, uoff_t v_offset)
{
struct istream_private *_stream = stream->real_stream;
if (unlikely(stream->closed || stream->stream_errno != 0))
return;
stream->eof = FALSE;
_stream->seek(_stream, v_offset, TRUE);
i_stream_update(_stream);
}
void i_stream_sync(struct istream *stream)
{
struct istream_private *_stream = stream->real_stream;
if (unlikely(stream->closed || stream->stream_errno != 0))
return;
if (_stream->sync != NULL) {
_stream->sync(_stream);
i_stream_update(_stream);
}
}
int i_stream_stat(struct istream *stream, bool exact, const struct stat **st_r)
{
struct istream_private *_stream = stream->real_stream;
if (unlikely(stream->closed || stream->stream_errno != 0))
return -1;
if (_stream->stat(_stream, exact) < 0) {
stream->eof = TRUE;
return -1;
}
*st_r = &_stream->statbuf;
return 0;
}
int i_stream_get_size(struct istream *stream, bool exact, uoff_t *size_r)
{
struct istream_private *_stream = stream->real_stream;
if (unlikely(stream->closed || stream->stream_errno != 0))
return -1;
int ret;
if ((ret = _stream->get_size(_stream, exact, size_r)) < 0)
stream->eof = TRUE;
return ret;
}
bool i_stream_have_bytes_left(struct istream *stream)
{
return i_stream_get_data_size(stream) > 0 || !stream->eof;
}
bool i_stream_read_eof(struct istream *stream)
{
if (i_stream_get_data_size(stream) == 0)
(void)i_stream_read(stream);
return !i_stream_have_bytes_left(stream);
}
uoff_t i_stream_get_absolute_offset(struct istream *stream)
{
uoff_t abs_offset = stream->v_offset;
while (stream != NULL) {
abs_offset += stream->real_stream->start_offset;
stream = stream->real_stream->parent;
}
return abs_offset;
}
static char *i_stream_next_line_finish(struct istream_private *stream, size_t i)
{
char *ret;
size_t end;
if (i > 0 && stream->buffer[i-1] == '\r') {
end = i - 1;
stream->line_crlf = TRUE;
} else {
end = i;
stream->line_crlf = FALSE;
}
if (stream->buffer == stream->w_buffer) {
/* modify the buffer directly */
stream->w_buffer[end] = '\0';
ret = (char *)stream->w_buffer + stream->skip;
} else {
/* use a temporary string to return it */
if (stream->line_str == NULL)
stream->line_str = str_new(default_pool, 256);
str_truncate(stream->line_str, 0);
str_append_n(stream->line_str, stream->buffer + stream->skip,
end - stream->skip);
ret = str_c_modifiable(stream->line_str);
}
if (i < stream->pos)
i++;
stream->istream.v_offset += i - stream->skip;
stream->skip = i;
return ret;
}
static char *i_stream_last_line(struct istream_private *_stream)
{
if (_stream->istream.eof && _stream->skip != _stream->pos &&
_stream->return_nolf_line) {
/* the last line is missing LF and we want to return it. */
return i_stream_next_line_finish(_stream, _stream->pos);
}
return NULL;
}
char *i_stream_next_line(struct istream *stream)
{
struct istream_private *_stream = stream->real_stream;
const unsigned char *pos;
if (_stream->skip >= _stream->pos)
return NULL;
pos = memchr(_stream->buffer + _stream->skip, '\n',
_stream->pos - _stream->skip);
if (pos != NULL) {
return i_stream_next_line_finish(_stream,
pos - _stream->buffer);
} else {
return i_stream_last_line(_stream);
}
}
char *i_stream_read_next_line(struct istream *stream)
{
char *line;
for (;;) {
line = i_stream_next_line(stream);
if (line != NULL)
break;
switch (i_stream_read(stream)) {
case -2:
io_stream_set_error(&stream->real_stream->iostream,
"Line is too long (over %"PRIuSIZE_T
" bytes at offset %"PRIuUOFF_T")",
i_stream_get_data_size(stream), stream->v_offset);
stream->stream_errno = errno = ENOBUFS;
stream->eof = TRUE;
return NULL;
case -1:
return i_stream_last_line(stream->real_stream);
case 0:
return NULL;
}
}
return line;
}
bool i_stream_last_line_crlf(struct istream *stream)
{
return stream->real_stream->line_crlf;
}
static bool i_stream_is_buffer_invalid(const struct istream_private *stream)
{
if (stream->parent == NULL) {
/* the buffer can't point to parent, because it doesn't exist */
return FALSE;
}
if (stream->w_buffer != NULL) {
/* we can pretty safely assume that the stream is using its
own private buffer, so it can never become invalid. */
return FALSE;
}
if (stream->access_counter !=
stream->parent->real_stream->access_counter) {
/* parent has been modified behind this stream, we can't trust
that our buffer is valid */
return TRUE;
}
return i_stream_is_buffer_invalid(stream->parent->real_stream);
}
const unsigned char *
i_stream_get_data(struct istream *stream, size_t *size_r)
{
struct istream_private *_stream = stream->real_stream;
if (_stream->skip >= _stream->pos) {
*size_r = 0;
return uchar_empty_ptr;
}
if (i_stream_is_buffer_invalid(_stream)) {
/* This stream may be using parent's buffer directly as
_stream->buffer, but the parent stream has already been
modified indirectly. This means that the buffer might no
longer point to where we assume it points to. So we'll
just return the stream as empty until it's read again.
It's a bit ugly to suddenly drop data from the stream that
was already read, but since this happens only with shared
parent istreams the caller is hopefully aware enough that
something like this might happen. The other solutions would
be to a) try to automatically read the data back (but we
can't handle errors..) or b) always copy data to stream's
own buffer instead of pointing to parent's buffer (but this
causes data copying that is nearly always unnecessary). */
*size_r = 0;
/* if we had already read until EOF, mark the stream again as
not being at the end of file. */
if (stream->stream_errno == 0) {
_stream->skip = _stream->pos = 0;
stream->eof = FALSE;
}
return uchar_empty_ptr;
}
*size_r = _stream->pos - _stream->skip;
return _stream->buffer + _stream->skip;
}
size_t i_stream_get_data_size(struct istream *stream)
{
size_t size;
(void)i_stream_get_data(stream, &size);
return size;
}
unsigned char *i_stream_get_modifiable_data(struct istream *stream,
size_t *size_r)
{
struct istream_private *_stream = stream->real_stream;
if (_stream->skip >= _stream->pos || _stream->w_buffer == NULL) {
*size_r = 0;
return NULL;
}
*size_r = _stream->pos - _stream->skip;
return _stream->w_buffer + _stream->skip;
}
int i_stream_read_data(struct istream *stream, const unsigned char **data_r,
size_t *size_r, size_t threshold)
{
ssize_t ret = 0;
bool read_more = FALSE;
do {
*data_r = i_stream_get_data(stream, size_r);
if (*size_r > threshold)
return 1;
/* we need more data */
ret = i_stream_read(stream);
if (ret > 0)
read_more = TRUE;
} while (ret > 0);
*data_r = i_stream_get_data(stream, size_r);
if (ret == -2)
return -2;
if (ret == 0) {
/* need to read more */
i_assert(!stream->blocking);
return 0;
}
if (stream->eof) {
if (read_more) {
/* we read at least some new data */
return 0;
}
} else {
i_assert(stream->stream_errno != 0);
}
return -1;
}
void i_stream_compress(struct istream_private *stream)
{
i_assert(stream->memarea == NULL ||
memarea_get_refcount(stream->memarea) == 1);
if (stream->skip != stream->pos) {
memmove(stream->w_buffer, stream->w_buffer + stream->skip,
stream->pos - stream->skip);
}
stream->pos -= stream->skip;
stream->skip = 0;
}
static void i_stream_w_buffer_free(void *buf)
{
i_free(buf);
}
static void
i_stream_w_buffer_realloc(struct istream_private *stream, size_t old_size)
{
void *new_buffer;
if (stream->memarea != NULL &&
memarea_get_refcount(stream->memarea) == 1) {
/* Nobody else is referencing the memarea.
We can just reallocate it. */
memarea_free_without_callback(&stream->memarea);
new_buffer = i_realloc(stream->w_buffer, old_size,
stream->buffer_size);
} else {
new_buffer = i_malloc(stream->buffer_size);
memcpy(new_buffer, stream->w_buffer, old_size);
if (stream->memarea != NULL)
memarea_unref(&stream->memarea);
}
stream->w_buffer = new_buffer;
stream->buffer = new_buffer;
stream->memarea = memarea_init(stream->w_buffer, stream->buffer_size,
i_stream_w_buffer_free, new_buffer);
}
void i_stream_grow_buffer(struct istream_private *stream, size_t bytes)
{
size_t old_size, max_size;
old_size = stream->buffer_size;
stream->buffer_size = stream->pos + bytes;
if (stream->buffer_size <= stream->init_buffer_size)
stream->buffer_size = stream->init_buffer_size;
else
stream->buffer_size = nearest_power(stream->buffer_size);
max_size = i_stream_get_max_buffer_size(&stream->istream);
i_assert(max_size > 0);
if (stream->buffer_size > max_size)
stream->buffer_size = max_size;
if (stream->buffer_size <= old_size)
stream->buffer_size = old_size;
else
i_stream_w_buffer_realloc(stream, old_size);
}
bool i_stream_try_alloc(struct istream_private *stream,
size_t wanted_size, size_t *size_r)
{
i_assert(wanted_size > 0);
if (wanted_size > stream->buffer_size - stream->pos) {
if (stream->skip > 0) {
/* remove the unused bytes from beginning of buffer */
if (stream->memarea != NULL &&
memarea_get_refcount(stream->memarea) > 1) {
/* The memarea is still referenced. We can't
overwrite data until extra references are
gone. */
i_stream_w_buffer_realloc(stream, stream->buffer_size);
}
i_stream_compress(stream);
} else if (stream->buffer_size < i_stream_get_max_buffer_size(&stream->istream)) {
/* buffer is full - grow it */
i_stream_grow_buffer(stream, I_STREAM_MIN_SIZE);
}
}
*size_r = stream->buffer_size - stream->pos;
if (stream->try_alloc_limit > 0 &&
*size_r > stream->try_alloc_limit)
*size_r = stream->try_alloc_limit;
return *size_r > 0;
}
bool ATTR_NOWARN_UNUSED_RESULT
i_stream_try_alloc_avoid_compress(struct istream_private *stream,
size_t wanted_size, size_t *size_r)
{
size_t old_skip = stream->skip;
/* try first with skip=0, so no compression is done */
stream->skip = 0;
bool ret = i_stream_try_alloc(stream, wanted_size, size_r);
stream->skip = old_skip;
if (ret || old_skip == 0)
return ret;
/* it's full. try with compression. */
return i_stream_try_alloc(stream, wanted_size, size_r);
}
void *i_stream_alloc(struct istream_private *stream, size_t size)
{
size_t old_size, avail_size;
i_stream_try_alloc(stream, size, &avail_size);
if (avail_size < size) {
old_size = stream->buffer_size;
stream->buffer_size = nearest_power(stream->pos + size);
i_stream_w_buffer_realloc(stream, old_size);
i_stream_try_alloc(stream, size, &avail_size);
i_assert(avail_size >= size);
}
return stream->w_buffer + stream->pos;
}
bool i_stream_add_data(struct istream *_stream, const unsigned char *data,
size_t size)
{
struct istream_private *stream = _stream->real_stream;
size_t size2;
i_stream_try_alloc(stream, size, &size2);
if (size > size2)
return FALSE;
memcpy(stream->w_buffer + stream->pos, data, size);
stream->pos += size;
return TRUE;
}
void i_stream_set_input_pending(struct istream *stream, bool pending)
{
if (!pending)
return;
while (stream->real_stream->parent != NULL) {
i_assert(stream->real_stream->io == NULL);
stream = stream->real_stream->parent;
}
if (stream->real_stream->io != NULL)
io_set_pending(stream->real_stream->io);
}
void i_stream_switch_ioloop(struct istream *stream)
{
do {
if (stream->real_stream->switch_ioloop != NULL)
stream->real_stream->switch_ioloop(stream->real_stream);
stream = stream->real_stream->parent;
} while (stream != NULL);
}
void i_stream_set_io(struct istream *stream, struct io *io)
{
while (stream->real_stream->parent != NULL) {
i_assert(stream->real_stream->io == NULL);
stream = stream->real_stream->parent;
}
i_assert(stream->real_stream->io == NULL);
stream->real_stream->io = io;
}
void i_stream_unset_io(struct istream *stream, struct io *io)
{
while (stream->real_stream->parent != NULL) {
i_assert(stream->real_stream->io == NULL);
stream = stream->real_stream->parent;
}
i_assert(stream->real_stream->io == io);
stream->real_stream->io = NULL;
}
static void
i_stream_default_set_max_buffer_size(struct iostream_private *stream,
size_t max_size)
{
struct istream_private *_stream = (struct istream_private *)stream;
_stream->max_buffer_size = max_size;
if (_stream->parent != NULL)
i_stream_set_max_buffer_size(_stream->parent, max_size);
}
static void i_stream_default_close(struct iostream_private *stream,
bool close_parent)
{
struct istream_private *_stream = (struct istream_private *)stream;
if (close_parent && _stream->parent != NULL)
i_stream_close(_stream->parent);
}
static void i_stream_default_destroy(struct iostream_private *stream)
{
struct istream_private *_stream = (struct istream_private *)stream;
i_stream_free_buffer(_stream);
i_stream_unref(&_stream->parent);
}
static void
i_stream_default_seek_seekable(struct istream_private *stream,
uoff_t v_offset, bool mark ATTR_UNUSED)
{
stream->istream.v_offset = v_offset;
stream->skip = stream->pos = 0;
}
void i_stream_default_seek_nonseekable(struct istream_private *stream,
uoff_t v_offset, bool mark ATTR_UNUSED)
{
size_t available;
if (stream->istream.v_offset > v_offset)
i_panic("stream %s doesn't support seeking backwards",
i_stream_get_name(&stream->istream));
while (stream->istream.v_offset < v_offset) {
(void)i_stream_read(&stream->istream);
available = stream->pos - stream->skip;
if (available == 0) {
if (stream->istream.stream_errno != 0) {
/* read failed */
return;
}
io_stream_set_error(&stream->iostream,
"Can't seek to offset %"PRIuUOFF_T
", because we have data only up to offset %"
PRIuUOFF_T" (eof=%d)", v_offset,
stream->istream.v_offset, stream->istream.eof ? 1 : 0);
stream->istream.stream_errno = ESPIPE;
return;
}
if (available <= v_offset - stream->istream.v_offset)
i_stream_skip(&stream->istream, available);
else {
i_stream_skip(&stream->istream,
v_offset - stream->istream.v_offset);
}
}
}
static int
i_stream_default_stat(struct istream_private *stream, bool exact)
{
const struct stat *st;
if (stream->parent == NULL)
return stream->istream.stream_errno == 0 ? 0 : -1;
if (i_stream_stat(stream->parent, exact, &st) < 0) {
stream->istream.stream_errno = stream->parent->stream_errno;
return -1;
}
stream->statbuf = *st;
if (exact && !stream->stream_size_passthrough) {
/* exact size is not known, even if parent returned something */
stream->statbuf.st_size = -1;
}
return 0;
}
static int
i_stream_default_get_size(struct istream_private *stream,
bool exact, uoff_t *size_r)
{
if (stream->stat(stream, exact) < 0)
return -1;
if (stream->statbuf.st_size == -1)
return 0;
*size_r = stream->statbuf.st_size;
return 1;
}
void i_stream_init_parent(struct istream_private *_stream,
struct istream *parent)
{
_stream->access_counter = parent->real_stream->access_counter;
_stream->parent = parent;
_stream->parent_start_offset = parent->v_offset;
_stream->parent_expected_offset = parent->v_offset;
_stream->start_offset = parent->v_offset;
/* if parent stream is an istream-error, copy the error */
_stream->istream.stream_errno = parent->stream_errno;
_stream->istream.eof = parent->eof;
i_stream_ref(parent);
}
struct istream *
i_stream_create(struct istream_private *_stream, struct istream *parent, int fd,
enum istream_create_flag flags)
{
bool noop_snapshot = (flags & ISTREAM_CREATE_FLAG_NOOP_SNAPSHOT) != 0;
_stream->fd = fd;
if (parent != NULL)
i_stream_init_parent(_stream, parent);
else if (_stream->memarea == NULL && !noop_snapshot) {
/* The stream has no parent and no memarea yet. We'll assume
that it wants to be using memareas for the reads. */
_stream->memarea = memarea_init_empty();
}
_stream->istream.real_stream = _stream;
if (_stream->iostream.close == NULL)
_stream->iostream.close = i_stream_default_close;
if (_stream->iostream.destroy == NULL)
_stream->iostream.destroy = i_stream_default_destroy;
if (_stream->seek == NULL) {
_stream->seek = _stream->istream.seekable ?
i_stream_default_seek_seekable :
i_stream_default_seek_nonseekable;
}
if (_stream->stat == NULL)
_stream->stat = i_stream_default_stat;
if (_stream->get_size == NULL)
_stream->get_size = i_stream_default_get_size;
if (_stream->snapshot == NULL) {
_stream->snapshot = noop_snapshot ?
i_stream_noop_snapshot :
i_stream_default_snapshot;
}
if (_stream->iostream.set_max_buffer_size == NULL) {
_stream->iostream.set_max_buffer_size =
i_stream_default_set_max_buffer_size;
}
if (_stream->init_buffer_size == 0)
_stream->init_buffer_size = I_STREAM_MIN_SIZE;
i_zero(&_stream->statbuf);
_stream->statbuf.st_size = -1;
_stream->statbuf.st_atime =
_stream->statbuf.st_mtime =
_stream->statbuf.st_ctime = ioloop_time;
io_stream_init(&_stream->iostream);
if (_stream->istream.stream_errno != 0)
_stream->istream.eof = TRUE;
return &_stream->istream;
}
struct istream *i_stream_create_error(int stream_errno)
{
struct istream_private *stream;
stream = i_new(struct istream_private, 1);
stream->istream.closed = TRUE;
stream->istream.readable_fd = FALSE;
stream->istream.blocking = TRUE;
stream->istream.seekable = TRUE;
stream->istream.eof = TRUE;
stream->istream.stream_errno = stream_errno;
i_stream_create(stream, NULL, -1, 0);
i_stream_set_name(&stream->istream, "(error)");
return &stream->istream;
}
struct istream *
i_stream_create_error_str(int stream_errno, const char *fmt, ...)
{
struct istream *input;
va_list args;
va_start(args, fmt);
input = i_stream_create_error(stream_errno);
io_stream_set_verror(&input->real_stream->iostream, fmt, args);
va_end(args);
return input;
}