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