/src/dovecot/src/lib/istream-sized.c
Line | Count | Source |
1 | | /* Copyright (c) Dovecot authors, see top-level COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "istream-private.h" |
5 | | #include "istream-sized.h" |
6 | | |
7 | | struct sized_istream { |
8 | | struct istream_private istream; |
9 | | |
10 | | istream_sized_callback_t *error_callback; |
11 | | void *error_context; |
12 | | |
13 | | uoff_t size; |
14 | | bool min_size_only; |
15 | | }; |
16 | | |
17 | | static void i_stream_sized_destroy(struct iostream_private *stream) |
18 | 17.2k | { |
19 | 17.2k | struct sized_istream *sstream = |
20 | 17.2k | container_of(stream, struct sized_istream, istream.iostream); |
21 | 17.2k | uoff_t v_offset; |
22 | | |
23 | 17.2k | v_offset = sstream->istream.parent_start_offset + |
24 | 17.2k | sstream->istream.istream.v_offset; |
25 | 17.2k | if (sstream->istream.parent->seekable || |
26 | 13.2k | v_offset > sstream->istream.parent->v_offset) { |
27 | | /* get to same position in parent stream */ |
28 | 3.99k | i_stream_seek(sstream->istream.parent, v_offset); |
29 | 3.99k | } |
30 | 17.2k | } |
31 | | |
32 | | static const char * |
33 | | i_stream_create_sized_default_error_callback( |
34 | | const struct istream_sized_error_data *data, void *context ATTR_UNUSED) |
35 | 352 | { |
36 | 352 | if (data->v_offset + data->new_bytes < data->wanted_size) { |
37 | 352 | return t_strdup_printf("Stream is smaller than expected " |
38 | 352 | "(%"PRIuUOFF_T" < %"PRIuUOFF_T")", |
39 | 352 | data->v_offset + data->new_bytes, data->wanted_size); |
40 | 352 | } else { |
41 | 0 | return t_strdup_printf("Stream is larger than expected " |
42 | 0 | "(%"PRIuUOFF_T" > %"PRIuUOFF_T", eof=%d)", |
43 | 0 | data->v_offset + data->new_bytes, data->wanted_size, |
44 | 0 | data->eof ? 1 : 0); |
45 | 0 | } |
46 | 352 | } |
47 | | |
48 | | static ssize_t |
49 | | i_stream_sized_parent_read(struct istream_private *stream, size_t *pos_r) |
50 | 38.7k | { |
51 | 38.7k | ssize_t ret; |
52 | | |
53 | 38.7k | do { |
54 | 38.7k | ret = i_stream_read_memarea(stream->parent); |
55 | 38.7k | stream->istream.stream_errno = stream->parent->stream_errno; |
56 | 38.7k | stream->istream.eof = stream->parent->eof; |
57 | 38.7k | stream->buffer = i_stream_get_data(stream->parent, pos_r); |
58 | 38.7k | } while (*pos_r <= stream->pos && ret > 0); |
59 | 38.7k | return ret; |
60 | 38.7k | } |
61 | | |
62 | | static ssize_t i_stream_sized_read(struct istream_private *stream) |
63 | 31.3k | { |
64 | 31.3k | struct sized_istream *sstream = |
65 | 31.3k | container_of(stream, struct sized_istream, istream); |
66 | 31.3k | struct istream_sized_error_data data; |
67 | 31.3k | const char *error; |
68 | 31.3k | uoff_t left; |
69 | 31.3k | ssize_t ret; |
70 | 31.3k | size_t pos; |
71 | | |
72 | 31.3k | i_stream_seek(stream->parent, sstream->istream.parent_start_offset + |
73 | 31.3k | stream->istream.v_offset); |
74 | | |
75 | 31.3k | stream->pos -= stream->skip; |
76 | 31.3k | stream->skip = 0; |
77 | | |
78 | 31.3k | stream->buffer = i_stream_get_data(stream->parent, &pos); |
79 | 31.3k | if (pos > stream->pos) |
80 | 0 | ret = 0; |
81 | 31.3k | else { |
82 | 31.3k | if ((ret = i_stream_sized_parent_read(stream, &pos)) == -2) |
83 | 0 | return -2; |
84 | 31.3k | } |
85 | | |
86 | 31.3k | left = sstream->size - stream->istream.v_offset; |
87 | 31.3k | if (pos == left && ret != -1) { |
88 | | /* we have exactly the wanted amount of data left, but we |
89 | | don't know yet if there is more data in parent. */ |
90 | 7.39k | ret = i_stream_sized_parent_read(stream, &pos); |
91 | 7.39k | } |
92 | | |
93 | 31.3k | i_zero(&data); |
94 | 31.3k | data.v_offset = stream->istream.v_offset; |
95 | 31.3k | data.new_bytes = pos; |
96 | 31.3k | data.wanted_size = sstream->size; |
97 | 31.3k | data.eof = stream->istream.eof; |
98 | | |
99 | 31.3k | if (pos == left) { |
100 | | /* we may or may not be finished, depending on whether |
101 | | parent is at EOF. */ |
102 | 23.9k | } else if (pos > left) { |
103 | | /* parent has more data available than expected */ |
104 | 0 | if (!sstream->min_size_only) { |
105 | 0 | error = sstream->error_callback(&data, sstream->error_context); |
106 | 0 | io_stream_set_error(&stream->iostream, "%s", error); |
107 | 0 | stream->istream.stream_errno = EINVAL; |
108 | 0 | return -1; |
109 | 0 | } |
110 | 0 | pos = left; |
111 | 0 | if (pos <= stream->pos) { |
112 | 0 | stream->istream.eof = TRUE; |
113 | 0 | ret = -1; |
114 | 0 | } |
115 | 7.39k | } else if (!stream->istream.eof) { |
116 | | /* still more to read */ |
117 | 6.77k | } else if (stream->istream.stream_errno != 0) { |
118 | | /* parent stream read() failed - preserve the error */ |
119 | 626 | } else { |
120 | | /* EOF before we reached the wanted size */ |
121 | 626 | error = sstream->error_callback(&data, sstream->error_context); |
122 | 626 | io_stream_set_error(&stream->iostream, "%s", error); |
123 | 626 | stream->istream.stream_errno = EPIPE; |
124 | 626 | } |
125 | | |
126 | 31.3k | ret = pos > stream->pos ? (ssize_t)(pos - stream->pos) : |
127 | 31.3k | (ret == 0 ? 0 : -1); |
128 | 31.3k | stream->pos = pos; |
129 | 31.3k | i_assert(ret != -1 || stream->istream.eof || |
130 | 31.3k | stream->istream.stream_errno != 0); |
131 | 31.3k | return ret; |
132 | 31.3k | } |
133 | | |
134 | | static int |
135 | | i_stream_sized_stat(struct istream_private *stream, bool exact ATTR_UNUSED) |
136 | 0 | { |
137 | 0 | struct sized_istream *sstream = |
138 | 0 | container_of(stream, struct sized_istream, istream); |
139 | 0 | const struct stat *st; |
140 | | |
141 | | /* parent stream may be base64-decoder. don't waste time decoding the |
142 | | entire stream, since we already know what the size is supposed |
143 | | to be. */ |
144 | 0 | if (i_stream_stat(stream->parent, FALSE, &st) < 0) { |
145 | 0 | stream->istream.stream_errno = stream->parent->stream_errno; |
146 | 0 | return -1; |
147 | 0 | } |
148 | | |
149 | 0 | stream->statbuf = *st; |
150 | 0 | stream->statbuf.st_size = sstream->size; |
151 | 0 | return 0; |
152 | 0 | } |
153 | | |
154 | | static struct sized_istream * |
155 | | i_stream_create_sized_common(struct istream *input, uoff_t size) |
156 | 17.2k | { |
157 | 17.2k | struct sized_istream *sstream; |
158 | | |
159 | 17.2k | sstream = i_new(struct sized_istream, 1); |
160 | 17.2k | sstream->size = size; |
161 | 17.2k | sstream->istream.max_buffer_size = input->real_stream->max_buffer_size; |
162 | | |
163 | 17.2k | sstream->istream.iostream.destroy = i_stream_sized_destroy; |
164 | 17.2k | sstream->istream.read = i_stream_sized_read; |
165 | 17.2k | sstream->istream.stat = i_stream_sized_stat; |
166 | | |
167 | 17.2k | sstream->istream.istream.readable_fd = input->readable_fd; |
168 | 17.2k | sstream->istream.istream.blocking = input->blocking; |
169 | 17.2k | sstream->istream.istream.seekable = input->seekable; |
170 | 17.2k | (void)i_stream_create(&sstream->istream, input, |
171 | 17.2k | i_stream_get_fd(input), |
172 | 17.2k | ISTREAM_HIDDEN_INPUTS_NONE, 0); |
173 | 17.2k | return sstream; |
174 | 17.2k | } |
175 | | |
176 | | struct istream *i_stream_create_sized(struct istream *input, uoff_t size) |
177 | 13.2k | { |
178 | 13.2k | struct sized_istream *sstream; |
179 | | |
180 | 13.2k | sstream = i_stream_create_sized_common(input, size); |
181 | 13.2k | sstream->error_callback = i_stream_create_sized_default_error_callback; |
182 | 13.2k | sstream->error_context = sstream; |
183 | 13.2k | return &sstream->istream.istream; |
184 | 13.2k | } |
185 | | |
186 | | struct istream *i_stream_create_sized_range(struct istream *input, |
187 | | uoff_t offset, uoff_t size) |
188 | 0 | { |
189 | 0 | uoff_t orig_offset = input->v_offset; |
190 | 0 | struct istream *ret; |
191 | |
|
192 | 0 | input->v_offset = offset; |
193 | 0 | ret = i_stream_create_sized(input, size); |
194 | 0 | input->v_offset = orig_offset; |
195 | 0 | return ret; |
196 | 0 | } |
197 | | |
198 | | struct istream *i_stream_create_min_sized(struct istream *input, uoff_t min_size) |
199 | 13.2k | { |
200 | 13.2k | struct istream *ret; |
201 | | |
202 | 13.2k | ret= i_stream_create_sized(input, min_size); |
203 | 13.2k | struct sized_istream *ret_sstream = |
204 | 13.2k | container_of(ret->real_stream, struct sized_istream, istream); |
205 | 13.2k | ret_sstream->min_size_only = TRUE; |
206 | 13.2k | return ret; |
207 | 13.2k | } |
208 | | |
209 | | struct istream *i_stream_create_min_sized_range(struct istream *input, |
210 | | uoff_t offset, uoff_t min_size) |
211 | 0 | { |
212 | 0 | struct istream *ret; |
213 | |
|
214 | 0 | ret = i_stream_create_sized_range(input, offset, min_size); |
215 | 0 | struct sized_istream *ret_sstream = |
216 | 0 | container_of(ret->real_stream, struct sized_istream, istream); |
217 | 0 | ret_sstream->min_size_only = TRUE; |
218 | 0 | return ret; |
219 | 0 | } |
220 | | |
221 | | #undef i_stream_create_sized_with_callback |
222 | | struct istream * |
223 | | i_stream_create_sized_with_callback(struct istream *input, uoff_t size, |
224 | | istream_sized_callback_t *error_callback, |
225 | | void *context) |
226 | 3.99k | { |
227 | 3.99k | struct sized_istream *sstream; |
228 | | |
229 | 3.99k | sstream = i_stream_create_sized_common(input, size); |
230 | 3.99k | sstream->error_callback = error_callback; |
231 | 3.99k | sstream->error_context = context; |
232 | 3.99k | return &sstream->istream.istream; |
233 | 3.99k | } |