1 /* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "memarea.h"
5 #include "istream-private.h"
6 #include "test-common.h"
7 
8 struct test_istream {
9 	struct istream_private istream;
10 	const void *orig_buffer;
11 	unsigned int skip_diff;
12 	size_t max_pos;
13 	bool allow_eof;
14 };
15 
test_buffer_free(unsigned char * buf)16 static void test_buffer_free(unsigned char *buf)
17 {
18 	i_free(buf);
19 }
20 
test_read(struct istream_private * stream)21 static ssize_t test_read(struct istream_private *stream)
22 {
23 	struct test_istream *tstream = (struct test_istream *)stream;
24 	unsigned int new_skip_diff;
25 	size_t cur_max;
26 	ssize_t ret;
27 
28 	i_assert(stream->skip <= stream->pos);
29 
30 	if (stream->pos - stream->skip >= tstream->istream.max_buffer_size) {
31 		i_assert(stream->skip != stream->pos);
32 		return -2;
33 	}
34 
35 	if (tstream->max_pos < stream->pos) {
36 		/* we seeked past the end of file. */
37 		ret = 0;
38 	} else {
39 		/* copy data to a buffer in somewhat random place. this could
40 		   help catch bugs. */
41 		new_skip_diff = i_rand_limit(128);
42 		stream->skip = (stream->skip - tstream->skip_diff) +
43 			new_skip_diff;
44 		stream->pos = (stream->pos - tstream->skip_diff) +
45 			new_skip_diff;
46 		tstream->max_pos = (tstream->max_pos - tstream->skip_diff) +
47 			new_skip_diff;
48 		tstream->skip_diff = new_skip_diff;
49 
50 		cur_max = tstream->max_pos;
51 		if (stream->max_buffer_size < SIZE_MAX - stream->skip &&
52 		    cur_max > stream->skip + stream->max_buffer_size)
53 			cur_max = stream->skip + stream->max_buffer_size;
54 
55 		/* Reallocate the memory area if needed. Use exactly correct
56 		   buffer size so valgrind can catch read overflows. If a
57 		   correctly sized memarea already exists, use it only if
58 		   its refcount is 1. Otherwise with refcount>1 we could be
59 		   moving data within an existing memarea, which breaks
60 		   snapshots. */
61 		if (cur_max > 0 && (stream->buffer_size != cur_max ||
62 				    stream->memarea == NULL ||
63 				    memarea_get_refcount(stream->memarea) > 1)) {
64 			void *old_w_buffer = stream->w_buffer;
65 			stream->w_buffer = i_malloc(cur_max);
66 			if (stream->buffer_size != 0) {
67 				memcpy(stream->w_buffer, old_w_buffer,
68 				       I_MIN(stream->buffer_size, cur_max));
69 			}
70 			stream->buffer = stream->w_buffer;
71 			stream->buffer_size = cur_max;
72 
73 			if (stream->memarea != NULL)
74 				memarea_unref(&stream->memarea);
75 			stream->memarea = memarea_init(stream->w_buffer,
76 						       stream->buffer_size,
77 						       test_buffer_free,
78 						       stream->w_buffer);
79 		}
80 		ssize_t size = cur_max - new_skip_diff;
81 		if (size > 0)
82 			memcpy(stream->w_buffer + new_skip_diff,
83 				tstream->orig_buffer, (size_t)size);
84 
85 		ret = cur_max - stream->pos;
86 		stream->pos = cur_max;
87 	}
88 
89 	if (ret > 0)
90 		return ret;
91 	else if (!tstream->allow_eof ||
92 		 stream->pos - tstream->skip_diff < (uoff_t)stream->statbuf.st_size)
93 		return 0;
94 	else {
95 		stream->istream.eof = TRUE;
96 		return -1;
97 	}
98 }
99 
test_seek(struct istream_private * stream,uoff_t v_offset,bool mark ATTR_UNUSED)100 static void test_seek(struct istream_private *stream, uoff_t v_offset,
101 		      bool mark ATTR_UNUSED)
102 {
103 	struct test_istream *tstream = (struct test_istream *)stream;
104 
105 	stream->istream.v_offset = v_offset;
106 	stream->skip = v_offset + tstream->skip_diff;
107 	stream->pos = stream->skip;
108 }
109 
test_istream_create_data(const void * data,size_t size)110 struct istream *test_istream_create_data(const void *data, size_t size)
111 {
112 	struct test_istream *tstream;
113 
114 	tstream = i_new(struct test_istream, 1);
115 	tstream->orig_buffer = data;
116 
117 	tstream->istream.read = test_read;
118 	tstream->istream.seek = test_seek;
119 
120 	tstream->istream.istream.blocking = FALSE;
121 	tstream->istream.istream.seekable = TRUE;
122 	i_stream_create(&tstream->istream, NULL, -1, 0);
123 	tstream->istream.statbuf.st_size = tstream->max_pos = size;
124 	tstream->allow_eof = TRUE;
125 	tstream->istream.max_buffer_size = SIZE_MAX;
126 	return &tstream->istream.istream;
127 }
128 
test_istream_create(const char * data)129 struct istream *test_istream_create(const char *data)
130 {
131 	return test_istream_create_data(data, strlen(data));
132 }
133 
test_istream_find(struct istream * input)134 static struct test_istream *test_istream_find(struct istream *input)
135 {
136 	struct istream *in;
137 
138 	for (in = input; in != NULL; in = in->real_stream->parent) {
139 		if (in->real_stream->read == test_read)
140 			return (struct test_istream *)in->real_stream;
141 	}
142 	i_panic("%s isn't test-istream", i_stream_get_name(input));
143 }
144 
test_istream_set_allow_eof(struct istream * input,bool allow)145 void test_istream_set_allow_eof(struct istream *input, bool allow)
146 {
147 	struct test_istream *tstream = test_istream_find(input);
148 
149 	tstream->allow_eof = allow;
150 }
151 
test_istream_set_max_buffer_size(struct istream * input,size_t size)152 void test_istream_set_max_buffer_size(struct istream *input, size_t size)
153 {
154 	struct test_istream *tstream = test_istream_find(input);
155 
156 	tstream->istream.max_buffer_size = size;
157 }
158 
test_istream_set_size(struct istream * input,uoff_t size)159 void test_istream_set_size(struct istream *input, uoff_t size)
160 {
161 	struct test_istream *tstream = test_istream_find(input);
162 
163 	if (size > (uoff_t)tstream->istream.statbuf.st_size)
164 		size = (uoff_t)tstream->istream.statbuf.st_size;
165 	tstream->max_pos = size + tstream->skip_diff;
166 }
167