bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen#include "lib.h"
5181e190e968c25b19daa4489b278730326eec62Timo Sirainen#include "memarea.h"
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen#include "istream-private.h"
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen#include "test-common.h"
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainenstruct test_istream {
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen struct istream_private istream;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen const void *orig_buffer;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen unsigned int skip_diff;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen size_t max_pos;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen bool allow_eof;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen};
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen
5181e190e968c25b19daa4489b278730326eec62Timo Sirainenstatic void test_buffer_free(unsigned char *buf)
5181e190e968c25b19daa4489b278730326eec62Timo Sirainen{
5181e190e968c25b19daa4489b278730326eec62Timo Sirainen i_free(buf);
5181e190e968c25b19daa4489b278730326eec62Timo Sirainen}
5181e190e968c25b19daa4489b278730326eec62Timo Sirainen
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainenstatic ssize_t test_read(struct istream_private *stream)
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen{
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen struct test_istream *tstream = (struct test_istream *)stream;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen unsigned int new_skip_diff;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen size_t cur_max;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen ssize_t ret;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen i_assert(stream->skip <= stream->pos);
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen if (stream->pos - stream->skip >= tstream->istream.max_buffer_size) {
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen i_assert(stream->skip != stream->pos);
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen return -2;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen }
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen if (tstream->max_pos < stream->pos) {
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen /* we seeked past the end of file. */
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen ret = 0;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen } else {
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen /* copy data to a buffer in somewhat random place. this could
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen help catch bugs. */
191153d1a5b0eb0c129139570e3aa5212f28d2acJosef 'Jeff' Sipek new_skip_diff = i_rand_limit(128);
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen stream->skip = (stream->skip - tstream->skip_diff) +
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen new_skip_diff;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen stream->pos = (stream->pos - tstream->skip_diff) +
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen new_skip_diff;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen tstream->max_pos = (tstream->max_pos - tstream->skip_diff) +
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen new_skip_diff;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen tstream->skip_diff = new_skip_diff;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen cur_max = tstream->max_pos;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen if (stream->max_buffer_size < (size_t)-1 - stream->skip &&
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen cur_max > stream->skip + stream->max_buffer_size)
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen cur_max = stream->skip + stream->max_buffer_size;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen
21138be4989503314641025ab07a7010a380a159Timo Sirainen /* Reallocate the memory area if needed. Use exactly correct
21138be4989503314641025ab07a7010a380a159Timo Sirainen buffer size so valgrind can catch read overflows. If a
21138be4989503314641025ab07a7010a380a159Timo Sirainen correctly sized memarea already exists, use it only if
21138be4989503314641025ab07a7010a380a159Timo Sirainen its refcount is 1. Otherwise with refcount>1 we could be
21138be4989503314641025ab07a7010a380a159Timo Sirainen moving data within an existing memarea, which breaks
21138be4989503314641025ab07a7010a380a159Timo Sirainen snapshots. */
21138be4989503314641025ab07a7010a380a159Timo Sirainen if (cur_max > 0 && (stream->buffer_size != cur_max ||
21138be4989503314641025ab07a7010a380a159Timo Sirainen stream->memarea == NULL ||
21138be4989503314641025ab07a7010a380a159Timo Sirainen memarea_get_refcount(stream->memarea) > 1)) {
5181e190e968c25b19daa4489b278730326eec62Timo Sirainen void *old_w_buffer = stream->w_buffer;
5181e190e968c25b19daa4489b278730326eec62Timo Sirainen stream->w_buffer = i_malloc(cur_max);
5181e190e968c25b19daa4489b278730326eec62Timo Sirainen memcpy(stream->w_buffer, old_w_buffer,
5181e190e968c25b19daa4489b278730326eec62Timo Sirainen I_MIN(stream->buffer_size, cur_max));
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen stream->buffer = stream->w_buffer;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen stream->buffer_size = cur_max;
5181e190e968c25b19daa4489b278730326eec62Timo Sirainen
5181e190e968c25b19daa4489b278730326eec62Timo Sirainen if (stream->memarea != NULL)
5181e190e968c25b19daa4489b278730326eec62Timo Sirainen memarea_unref(&stream->memarea);
5181e190e968c25b19daa4489b278730326eec62Timo Sirainen stream->memarea = memarea_init(stream->w_buffer,
5181e190e968c25b19daa4489b278730326eec62Timo Sirainen stream->buffer_size,
5181e190e968c25b19daa4489b278730326eec62Timo Sirainen test_buffer_free,
5181e190e968c25b19daa4489b278730326eec62Timo Sirainen stream->w_buffer);
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen }
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen ssize_t size = cur_max - new_skip_diff;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen if (size > 0)
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen memcpy(stream->w_buffer + new_skip_diff,
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen tstream->orig_buffer, (size_t)size);
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen ret = cur_max - stream->pos;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen stream->pos = cur_max;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen }
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen if (ret > 0)
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen return ret;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen else if (!tstream->allow_eof ||
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen stream->pos - tstream->skip_diff < (uoff_t)stream->statbuf.st_size)
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen return 0;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen else {
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen stream->istream.eof = TRUE;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen return -1;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen }
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen}
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainenstatic void test_seek(struct istream_private *stream, uoff_t v_offset,
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen bool mark ATTR_UNUSED)
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen{
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen struct test_istream *tstream = (struct test_istream *)stream;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen stream->istream.v_offset = v_offset;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen stream->skip = v_offset + tstream->skip_diff;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen stream->pos = stream->skip;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen}
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainenstruct istream *test_istream_create_data(const void *data, size_t size)
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen{
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen struct test_istream *tstream;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen tstream = i_new(struct test_istream, 1);
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen tstream->orig_buffer = data;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen tstream->istream.read = test_read;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen tstream->istream.seek = test_seek;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen tstream->istream.istream.blocking = FALSE;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen tstream->istream.istream.seekable = TRUE;
2974dca6be5120e49279f06c8aa952e5fac56048Timo Sirainen i_stream_create(&tstream->istream, NULL, -1, 0);
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen tstream->istream.statbuf.st_size = tstream->max_pos = size;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen tstream->allow_eof = TRUE;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen tstream->istream.max_buffer_size = (size_t)-1;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen return &tstream->istream.istream;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen}
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainenstruct istream *test_istream_create(const char *data)
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen{
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen return test_istream_create_data(data, strlen(data));
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen}
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainenstatic struct test_istream *test_istream_find(struct istream *input)
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen{
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen struct istream *in;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen for (in = input; in != NULL; in = in->real_stream->parent) {
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen if (in->real_stream->read == test_read)
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen return (struct test_istream *)in->real_stream;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen }
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen i_panic("%s isn't test-istream", i_stream_get_name(input));
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen}
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainenvoid test_istream_set_allow_eof(struct istream *input, bool allow)
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen{
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen struct test_istream *tstream = test_istream_find(input);
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen tstream->allow_eof = allow;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen}
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainenvoid test_istream_set_max_buffer_size(struct istream *input, size_t size)
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen{
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen struct test_istream *tstream = test_istream_find(input);
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen tstream->istream.max_buffer_size = size;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen}
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainenvoid test_istream_set_size(struct istream *input, uoff_t size)
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen{
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen struct test_istream *tstream = test_istream_find(input);
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen if (size > (uoff_t)tstream->istream.statbuf.st_size)
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen size = (uoff_t)tstream->istream.statbuf.st_size;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen tstream->max_pos = size + tstream->skip_diff;
b3987cf9620e7aba403dfd914fe76b19290713ccTimo Sirainen}