/src/dovecot/src/lib/istream.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "ioloop.h" |
5 | | #include "array.h" |
6 | | #include "str.h" |
7 | | #include "memarea.h" |
8 | | #include "istream-private.h" |
9 | | |
10 | | static bool i_stream_is_buffer_invalid(const struct istream_private *stream); |
11 | | |
12 | | void i_stream_set_name(struct istream *stream, const char *name) |
13 | 0 | { |
14 | 0 | i_free(stream->real_stream->iostream.name); |
15 | 0 | stream->real_stream->iostream.name = i_strdup(name); |
16 | 0 | } |
17 | | |
18 | | const char *i_stream_get_name(struct istream *stream) |
19 | 0 | { |
20 | 0 | i_assert(stream != NULL); |
21 | 0 | while (stream->real_stream->iostream.name == NULL) { |
22 | 0 | stream = stream->real_stream->parent; |
23 | 0 | if (stream == NULL) |
24 | 0 | return ""; |
25 | 0 | } |
26 | 0 | return stream->real_stream->iostream.name; |
27 | 0 | } |
28 | | |
29 | | static void i_stream_close_full(struct istream *stream, bool close_parents) |
30 | 0 | { |
31 | 0 | io_stream_close(&stream->real_stream->iostream, close_parents); |
32 | 0 | stream->closed = TRUE; |
33 | |
|
34 | 0 | if (stream->stream_errno == 0) |
35 | 0 | stream->stream_errno = EPIPE; |
36 | 0 | } |
37 | | |
38 | | void i_stream_destroy(struct istream **stream) |
39 | 0 | { |
40 | 0 | if (*stream == NULL) |
41 | 0 | return; |
42 | | |
43 | 0 | i_stream_close_full(*stream, FALSE); |
44 | 0 | i_stream_unref(stream); |
45 | 0 | } |
46 | | |
47 | | void i_stream_ref(struct istream *stream) |
48 | 256k | { |
49 | 256k | io_stream_ref(&stream->real_stream->iostream); |
50 | 256k | } |
51 | | |
52 | | void i_stream_unref(struct istream **_stream) |
53 | 291k | { |
54 | 291k | struct istream *stream = *_stream; |
55 | 291k | struct istream_private *rstream; |
56 | | |
57 | 291k | if (stream == NULL) |
58 | 31.1k | return; |
59 | | |
60 | 260k | *_stream = NULL; |
61 | 260k | rstream = stream->real_stream; |
62 | | |
63 | 260k | if (rstream->iostream.refcount > 1) { |
64 | 256k | if (!io_stream_unref(&rstream->iostream)) |
65 | 0 | i_unreached(); |
66 | 256k | } else { |
67 | | /* The snapshot may contain pointers to the parent istreams. |
68 | | Free it before io_stream_unref() frees the parents. */ |
69 | 4.81k | i_stream_snapshot_free(&rstream->prev_snapshot); |
70 | | |
71 | 4.81k | if (io_stream_unref(&rstream->iostream)) |
72 | 0 | i_unreached(); |
73 | 4.81k | str_free(&rstream->line_str); |
74 | 4.81k | i_stream_unref(&rstream->parent); |
75 | 4.81k | io_stream_free(&rstream->iostream); |
76 | 4.81k | } |
77 | 260k | } |
78 | | |
79 | | #undef i_stream_add_destroy_callback |
80 | | void i_stream_add_destroy_callback(struct istream *stream, |
81 | | istream_callback_t *callback, void *context) |
82 | 0 | { |
83 | 0 | io_stream_add_destroy_callback(&stream->real_stream->iostream, |
84 | 0 | callback, context); |
85 | 0 | } |
86 | | |
87 | | void i_stream_remove_destroy_callback(struct istream *stream, |
88 | | void (*callback)()) |
89 | 0 | { |
90 | 0 | io_stream_remove_destroy_callback(&stream->real_stream->iostream, |
91 | 0 | callback); |
92 | 0 | } |
93 | | |
94 | | int i_stream_get_fd(struct istream *stream) |
95 | 0 | { |
96 | 0 | struct istream_private *_stream = stream->real_stream; |
97 | |
|
98 | 0 | return _stream->fd; |
99 | 0 | } |
100 | | |
101 | | void i_stream_copy_fd(struct istream *dest, struct istream *source) |
102 | 0 | { |
103 | 0 | int fd = i_stream_get_fd(source); |
104 | |
|
105 | 0 | i_assert(fd != -1); |
106 | 0 | i_assert(dest->real_stream->fd == -1); |
107 | 0 | dest->real_stream->fd = fd; |
108 | 0 | dest->readable_fd = source->readable_fd; |
109 | 0 | } |
110 | | |
111 | | void i_stream_set_error(struct istream *stream, int stream_errno, |
112 | | const char *fmt, ...) |
113 | 0 | { |
114 | 0 | va_list args; |
115 | |
|
116 | 0 | va_start(args, fmt); |
117 | 0 | stream->stream_errno = stream_errno; |
118 | 0 | io_stream_set_verror(&stream->real_stream->iostream, fmt, args); |
119 | 0 | va_end(args); |
120 | 0 | } |
121 | | |
122 | | const char *i_stream_get_error(struct istream *stream) |
123 | 0 | { |
124 | 0 | struct istream *s; |
125 | | |
126 | | /* we'll only return errors for streams that have stream_errno set or |
127 | | that have reached EOF. we might be returning unintended error |
128 | | otherwise. */ |
129 | 0 | if (stream->stream_errno == 0) |
130 | 0 | return stream->eof ? "EOF" : "<no error>"; |
131 | | |
132 | 0 | for (s = stream; s != NULL; s = s->real_stream->parent) { |
133 | 0 | if (s->stream_errno == 0) |
134 | 0 | break; |
135 | 0 | if (s->real_stream->iostream.error != NULL) |
136 | 0 | return s->real_stream->iostream.error; |
137 | 0 | } |
138 | 0 | return strerror(stream->stream_errno); |
139 | 0 | } |
140 | | |
141 | | const char *i_stream_get_disconnect_reason(struct istream *stream) |
142 | 0 | { |
143 | 0 | return io_stream_get_disconnect_reason(stream, NULL); |
144 | 0 | } |
145 | | |
146 | | void i_stream_close(struct istream *stream) |
147 | 0 | { |
148 | 0 | if (stream != NULL) |
149 | 0 | i_stream_close_full(stream, TRUE); |
150 | 0 | } |
151 | | |
152 | | void i_stream_set_init_buffer_size(struct istream *stream, size_t size) |
153 | 0 | { |
154 | 0 | stream->real_stream->init_buffer_size = size; |
155 | 0 | } |
156 | | |
157 | | void i_stream_set_max_buffer_size(struct istream *stream, size_t max_size) |
158 | 0 | { |
159 | 0 | io_stream_set_max_buffer_size(&stream->real_stream->iostream, max_size); |
160 | 0 | } |
161 | | |
162 | | size_t i_stream_get_max_buffer_size(struct istream *stream) |
163 | 0 | { |
164 | 0 | size_t max_size = 0; |
165 | |
|
166 | 0 | do { |
167 | 0 | if (max_size < stream->real_stream->max_buffer_size) |
168 | 0 | max_size = stream->real_stream->max_buffer_size; |
169 | 0 | stream = stream->real_stream->parent; |
170 | 0 | } while (stream != NULL); |
171 | 0 | return max_size; |
172 | 0 | } |
173 | | |
174 | | void i_stream_set_return_partial_line(struct istream *stream, bool set) |
175 | 0 | { |
176 | 0 | stream->real_stream->return_nolf_line = set; |
177 | 0 | } |
178 | | |
179 | | void i_stream_set_persistent_buffers(struct istream *stream, bool set) |
180 | 0 | { |
181 | 0 | do { |
182 | 0 | stream->real_stream->nonpersistent_buffers = !set; |
183 | 0 | stream = stream->real_stream->parent; |
184 | 0 | } while (stream != NULL); |
185 | 0 | } |
186 | | |
187 | | void i_stream_set_blocking(struct istream *stream, bool blocking) |
188 | 0 | { |
189 | 0 | int prev_fd = -1; |
190 | |
|
191 | 0 | do { |
192 | 0 | stream->blocking = blocking; |
193 | 0 | if (stream->real_stream->fd != -1 && |
194 | 0 | stream->real_stream->fd != prev_fd) { |
195 | 0 | fd_set_nonblock(stream->real_stream->fd, !blocking); |
196 | 0 | prev_fd = stream->real_stream->fd; |
197 | 0 | } |
198 | 0 | stream = stream->real_stream->parent; |
199 | 0 | } while (stream != NULL); |
200 | 0 | } |
201 | | |
202 | | static void i_stream_update(struct istream_private *stream) |
203 | 27.0k | { |
204 | 27.0k | if (stream->parent == NULL) |
205 | 27.0k | stream->access_counter++; |
206 | 0 | else { |
207 | 0 | stream->access_counter = |
208 | 0 | stream->parent->real_stream->access_counter; |
209 | 0 | stream->parent_expected_offset = stream->parent->v_offset; |
210 | 0 | } |
211 | 27.0k | } |
212 | | |
213 | | static bool snapshot_has_memarea(struct istream_snapshot *snapshot, |
214 | | struct memarea *memarea) |
215 | 42.3k | { |
216 | 42.3k | if (snapshot->old_memarea == memarea) |
217 | 0 | return TRUE; |
218 | 42.3k | if (snapshot->prev_snapshot != NULL) |
219 | 25.2k | return snapshot_has_memarea(snapshot->prev_snapshot, memarea); |
220 | 17.0k | return FALSE; |
221 | 42.3k | } |
222 | | |
223 | | struct istream_snapshot * |
224 | | i_stream_default_snapshot(struct istream_private *stream, |
225 | | struct istream_snapshot *prev_snapshot) |
226 | 21.5k | { |
227 | 21.5k | struct istream_snapshot *snapshot; |
228 | | |
229 | 21.5k | if (stream->memarea != NULL) { |
230 | 21.5k | if (prev_snapshot != NULL) { |
231 | 17.0k | if (snapshot_has_memarea(prev_snapshot, stream->memarea)) |
232 | 0 | return prev_snapshot; |
233 | 17.0k | } |
234 | | /* This stream has a memarea. Reference it, so we can later on |
235 | | rollback if needed. */ |
236 | 21.5k | snapshot = i_new(struct istream_snapshot, 1); |
237 | 21.5k | snapshot->old_memarea = stream->memarea; |
238 | 21.5k | snapshot->prev_snapshot = prev_snapshot; |
239 | 21.5k | memarea_ref(snapshot->old_memarea); |
240 | 21.5k | return snapshot; |
241 | 21.5k | } |
242 | 0 | if (stream->parent == NULL) { |
243 | 0 | if (stream->nonpersistent_buffers) { |
244 | | /* Assume that memarea would be used normally, but |
245 | | now it's NULL because the buffer is empty and |
246 | | empty buffers are freed. */ |
247 | 0 | i_assert(stream->skip == stream->pos); |
248 | 0 | return prev_snapshot; |
249 | 0 | } |
250 | 0 | i_panic("%s is missing istream.snapshot() implementation", |
251 | 0 | i_stream_get_name(&stream->istream)); |
252 | 0 | } |
253 | 0 | struct istream_private *_parent_stream = |
254 | 0 | stream->parent->real_stream; |
255 | 0 | return _parent_stream->snapshot(_parent_stream, prev_snapshot); |
256 | 0 | } |
257 | | |
258 | | void i_stream_snapshot_free(struct istream_snapshot **_snapshot) |
259 | 31.1k | { |
260 | 31.1k | struct istream_snapshot *snapshot = *_snapshot; |
261 | | |
262 | 31.1k | if (*_snapshot == NULL) |
263 | 9.62k | return; |
264 | 21.5k | *_snapshot = NULL; |
265 | | |
266 | 21.5k | i_stream_snapshot_free(&snapshot->prev_snapshot); |
267 | 21.5k | if (snapshot->free != NULL) |
268 | 0 | snapshot->free(snapshot); |
269 | 21.5k | else { |
270 | 21.5k | if (snapshot->old_memarea != NULL) |
271 | 21.5k | memarea_unref(&snapshot->old_memarea); |
272 | 21.5k | i_stream_unref(&snapshot->istream); |
273 | 21.5k | i_free(snapshot); |
274 | 21.5k | } |
275 | 21.5k | } |
276 | | |
277 | | static struct istream_snapshot * |
278 | | i_stream_noop_snapshot(struct istream_private *stream ATTR_UNUSED, |
279 | | struct istream_snapshot *prev_snapshot) |
280 | 0 | { |
281 | 0 | return prev_snapshot; |
282 | 0 | } |
283 | | |
284 | | ssize_t i_stream_read(struct istream *stream) |
285 | 27.0k | { |
286 | 27.0k | struct istream_private *_stream = stream->real_stream; |
287 | 27.0k | ssize_t ret; |
288 | | #ifdef DEBUG |
289 | | unsigned char prev_buf[4]; |
290 | | const unsigned char *prev_data = _stream->buffer; |
291 | | size_t prev_skip = _stream->skip, prev_pos = _stream->pos; |
292 | | bool invalid = i_stream_is_buffer_invalid(_stream); |
293 | | |
294 | | i_assert(prev_skip <= prev_pos); |
295 | | size_t prev_size = prev_pos - prev_skip; |
296 | | if (invalid) |
297 | | ; |
298 | | else if (prev_size > 4) { |
299 | | memcpy(prev_buf, prev_data + prev_skip, 2); |
300 | | memcpy(prev_buf+2, prev_data + prev_pos - 2, 2); |
301 | | } else if (prev_size > 0) { |
302 | | memcpy(prev_buf, prev_data + prev_skip, prev_size); |
303 | | } |
304 | | #endif |
305 | | |
306 | 27.0k | if (_stream->skip != _stream->pos || _stream->prev_snapshot != NULL) { |
307 | 21.5k | _stream->prev_snapshot = |
308 | 21.5k | _stream->snapshot(_stream, _stream->prev_snapshot); |
309 | 21.5k | } |
310 | 27.0k | ret = i_stream_read_memarea(stream); |
311 | 27.0k | if (ret > 0) |
312 | 4.81k | i_stream_snapshot_free(&_stream->prev_snapshot); |
313 | | #ifdef DEBUG |
314 | | else if (!invalid) { |
315 | | i_assert((_stream->pos - _stream->skip) == (prev_pos - prev_skip) || |
316 | | prev_pos == prev_skip); |
317 | | if (prev_data == NULL) |
318 | | i_assert(prev_pos == prev_skip); |
319 | | else if (prev_pos - prev_skip <= 4) |
320 | | i_assert(memcmp(prev_buf, prev_data + prev_skip, prev_pos - prev_skip) == 0); |
321 | | else { |
322 | | i_assert(memcmp(prev_buf, prev_data + prev_skip, 2) == 0); |
323 | | i_assert(memcmp(prev_buf+2, prev_data + prev_pos - 2, 2) == 0); |
324 | | } |
325 | | } |
326 | | #endif |
327 | 27.0k | return ret; |
328 | 27.0k | } |
329 | | |
330 | | ssize_t i_stream_read_memarea(struct istream *stream) |
331 | 27.0k | { |
332 | 27.0k | struct istream_private *_stream = stream->real_stream; |
333 | 27.0k | size_t old_size; |
334 | 27.0k | ssize_t ret; |
335 | | |
336 | 27.0k | if (unlikely(stream->closed || stream->stream_errno != 0)) { |
337 | 0 | stream->eof = TRUE; |
338 | 0 | errno = stream->stream_errno; |
339 | 0 | return -1; |
340 | 0 | } |
341 | | |
342 | 27.0k | stream->eof = FALSE; |
343 | | |
344 | 27.0k | if (_stream->parent != NULL) |
345 | 0 | i_stream_seek(_stream->parent, _stream->parent_expected_offset); |
346 | | |
347 | 27.0k | old_size = _stream->pos - _stream->skip; |
348 | 27.0k | if (_stream->pos < _stream->high_pos) { |
349 | | /* we're here because we seeked back within the read buffer. */ |
350 | 0 | ret = _stream->high_pos - _stream->pos; |
351 | 0 | _stream->pos = _stream->high_pos; |
352 | 0 | _stream->high_pos = 0; |
353 | 27.0k | } else { |
354 | 27.0k | _stream->high_pos = 0; |
355 | 27.0k | ret = _stream->read(_stream); |
356 | 27.0k | } |
357 | 27.0k | i_assert(old_size <= _stream->pos - _stream->skip); |
358 | 27.0k | switch (ret) { |
359 | 0 | case -2: |
360 | 0 | i_assert(_stream->skip != _stream->pos); |
361 | 0 | break; |
362 | 22.1k | case -1: |
363 | 22.1k | if (stream->stream_errno != 0) { |
364 | | /* error handling should be easier if we now just |
365 | | assume the stream is now at EOF */ |
366 | 0 | stream->eof = TRUE; |
367 | 0 | errno = stream->stream_errno; |
368 | 22.1k | } else { |
369 | 22.1k | i_assert(stream->eof); |
370 | 22.1k | i_assert(old_size == _stream->pos - _stream->skip); |
371 | 22.1k | } |
372 | 22.1k | break; |
373 | 22.1k | case 0: |
374 | 0 | i_assert(!stream->blocking); |
375 | 0 | break; |
376 | 4.81k | default: |
377 | 4.81k | i_assert(ret > 0); |
378 | 4.81k | i_assert(_stream->skip < _stream->pos); |
379 | 4.81k | i_assert((size_t)ret+old_size == _stream->pos - _stream->skip); |
380 | 4.81k | _stream->last_read_timeval = ioloop_timeval; |
381 | 4.81k | break; |
382 | 27.0k | } |
383 | | |
384 | 27.0k | if (stream->stream_errno != 0) { |
385 | | /* error handling should be easier if we now just |
386 | | assume the stream is now at EOF. Note that we could get here |
387 | | even if read() didn't return -1, although that's a little |
388 | | bit sloppy istream implementation. */ |
389 | 0 | stream->eof = TRUE; |
390 | 0 | } |
391 | | |
392 | 27.0k | i_stream_update(_stream); |
393 | | /* verify that parents' access_counters are valid. the parent's |
394 | | i_stream_read() should guarantee this. */ |
395 | 27.0k | i_assert(!i_stream_is_buffer_invalid(_stream)); |
396 | 27.0k | return ret; |
397 | 27.0k | } |
398 | | |
399 | | int i_stream_read_more_memarea(struct istream *stream, |
400 | | const unsigned char **data_r, size_t *size_r) |
401 | 0 | { |
402 | 0 | *data_r = i_stream_get_data(stream, size_r); |
403 | 0 | if (*size_r > 0) |
404 | 0 | return 1; |
405 | | |
406 | 0 | int ret = i_stream_read_memarea(stream); |
407 | 0 | *data_r = i_stream_get_data(stream, size_r); |
408 | 0 | return ret; |
409 | 0 | } |
410 | | |
411 | | void i_stream_get_last_read_time(struct istream *stream, struct timeval *tv_r) |
412 | 0 | { |
413 | 0 | *tv_r = stream->real_stream->last_read_timeval; |
414 | 0 | } |
415 | | |
416 | | ssize_t i_stream_read_copy_from_parent(struct istream *istream) |
417 | 0 | { |
418 | 0 | struct istream_private *stream = istream->real_stream; |
419 | 0 | size_t pos; |
420 | 0 | ssize_t ret; |
421 | |
|
422 | 0 | stream->pos -= stream->skip; |
423 | 0 | stream->skip = 0; |
424 | |
|
425 | 0 | stream->buffer = i_stream_get_data(stream->parent, &pos); |
426 | 0 | if (pos > stream->pos) |
427 | 0 | ret = 0; |
428 | 0 | else do { |
429 | 0 | ret = i_stream_read_memarea(stream->parent); |
430 | 0 | stream->istream.stream_errno = stream->parent->stream_errno; |
431 | 0 | stream->istream.eof = stream->parent->eof; |
432 | 0 | stream->buffer = i_stream_get_data(stream->parent, &pos); |
433 | | /* check again, in case the parent stream had been seeked |
434 | | backwards and the previous read() didn't get us far |
435 | | enough. */ |
436 | 0 | } while (pos <= stream->pos && ret > 0); |
437 | 0 | if (ret == -2) { |
438 | 0 | i_stream_update(stream); |
439 | 0 | return -2; |
440 | 0 | } |
441 | | |
442 | 0 | ret = pos > stream->pos ? (ssize_t)(pos - stream->pos) : |
443 | 0 | (ret == 0 ? 0 : -1); |
444 | 0 | stream->pos = pos; |
445 | 0 | i_assert(ret != -1 || stream->istream.eof || |
446 | 0 | stream->istream.stream_errno != 0); |
447 | 0 | i_stream_update(stream); |
448 | 0 | return ret; |
449 | 0 | } |
450 | | |
451 | | void i_stream_free_buffer(struct istream_private *stream) |
452 | 4.81k | { |
453 | 4.81k | if (stream->memarea != NULL) { |
454 | 4.81k | memarea_unref(&stream->memarea); |
455 | 4.81k | stream->w_buffer = NULL; |
456 | 4.81k | } else if (stream->w_buffer != NULL) { |
457 | 0 | i_free_and_null(stream->w_buffer); |
458 | 0 | } else { |
459 | | /* don't know how to free it */ |
460 | 0 | return; |
461 | 0 | } |
462 | 4.81k | stream->buffer_size = 0; |
463 | 4.81k | } |
464 | | |
465 | | void i_stream_skip(struct istream *stream, uoff_t count) |
466 | 1.09M | { |
467 | 1.09M | struct istream_private *_stream = stream->real_stream; |
468 | 1.09M | size_t data_size; |
469 | | |
470 | 1.09M | data_size = _stream->pos - _stream->skip; |
471 | 1.09M | if (count <= data_size) { |
472 | | /* within buffer */ |
473 | 1.09M | stream->v_offset += count; |
474 | 1.09M | _stream->skip += count; |
475 | 1.09M | if (_stream->nonpersistent_buffers && |
476 | 1.09M | _stream->skip == _stream->pos) { |
477 | 0 | _stream->skip = _stream->pos = 0; |
478 | 0 | i_stream_free_buffer(_stream); |
479 | 0 | } |
480 | 1.09M | return; |
481 | 1.09M | } |
482 | | |
483 | | /* have to seek forward */ |
484 | 0 | count -= data_size; |
485 | 0 | _stream->skip = _stream->pos; |
486 | 0 | stream->v_offset += data_size; |
487 | |
|
488 | 0 | if (unlikely(stream->closed || stream->stream_errno != 0)) |
489 | 0 | return; |
490 | | |
491 | 0 | _stream->seek(_stream, stream->v_offset + count, FALSE); |
492 | 0 | } |
493 | | |
494 | | static bool i_stream_can_optimize_seek(struct istream_private *stream) |
495 | 0 | { |
496 | 0 | if (stream->parent == NULL) |
497 | 0 | return TRUE; |
498 | | |
499 | | /* use the fast route only if the parent stream hasn't been changed */ |
500 | 0 | if (stream->access_counter != |
501 | 0 | stream->parent->real_stream->access_counter) |
502 | 0 | return FALSE; |
503 | | |
504 | 0 | return i_stream_can_optimize_seek(stream->parent->real_stream); |
505 | 0 | } |
506 | | |
507 | | void i_stream_seek(struct istream *stream, uoff_t v_offset) |
508 | 0 | { |
509 | 0 | struct istream_private *_stream = stream->real_stream; |
510 | |
|
511 | 0 | if (v_offset >= stream->v_offset && |
512 | 0 | i_stream_can_optimize_seek(_stream)) |
513 | 0 | i_stream_skip(stream, v_offset - stream->v_offset); |
514 | 0 | else { |
515 | 0 | if (unlikely(stream->closed || stream->stream_errno != 0)) { |
516 | 0 | stream->eof = TRUE; |
517 | 0 | return; |
518 | 0 | } |
519 | 0 | stream->eof = FALSE; |
520 | 0 | _stream->seek(_stream, v_offset, FALSE); |
521 | 0 | } |
522 | 0 | i_stream_update(_stream); |
523 | 0 | } |
524 | | |
525 | | void i_stream_seek_mark(struct istream *stream, uoff_t v_offset) |
526 | 0 | { |
527 | 0 | struct istream_private *_stream = stream->real_stream; |
528 | |
|
529 | 0 | if (unlikely(stream->closed || stream->stream_errno != 0)) |
530 | 0 | return; |
531 | | |
532 | 0 | stream->eof = FALSE; |
533 | 0 | _stream->seek(_stream, v_offset, TRUE); |
534 | 0 | i_stream_update(_stream); |
535 | 0 | } |
536 | | |
537 | | void i_stream_sync(struct istream *stream) |
538 | 0 | { |
539 | 0 | struct istream_private *_stream = stream->real_stream; |
540 | |
|
541 | 0 | if (unlikely(stream->closed || stream->stream_errno != 0)) |
542 | 0 | return; |
543 | | |
544 | 0 | if (_stream->sync != NULL) { |
545 | 0 | _stream->sync(_stream); |
546 | 0 | i_stream_update(_stream); |
547 | 0 | } |
548 | 0 | } |
549 | | |
550 | | int i_stream_stat(struct istream *stream, bool exact, const struct stat **st_r) |
551 | 0 | { |
552 | 0 | struct istream_private *_stream = stream->real_stream; |
553 | |
|
554 | 0 | if (unlikely(stream->closed || stream->stream_errno != 0)) |
555 | 0 | return -1; |
556 | | |
557 | 0 | if (_stream->stat(_stream, exact) < 0) { |
558 | 0 | stream->eof = TRUE; |
559 | 0 | return -1; |
560 | 0 | } |
561 | 0 | *st_r = &_stream->statbuf; |
562 | 0 | return 0; |
563 | 0 | } |
564 | | |
565 | | int i_stream_get_size(struct istream *stream, bool exact, uoff_t *size_r) |
566 | 0 | { |
567 | 0 | struct istream_private *_stream = stream->real_stream; |
568 | |
|
569 | 0 | if (unlikely(stream->closed || stream->stream_errno != 0)) |
570 | 0 | return -1; |
571 | | |
572 | 0 | int ret; |
573 | 0 | if ((ret = _stream->get_size(_stream, exact, size_r)) < 0) |
574 | 0 | stream->eof = TRUE; |
575 | 0 | return ret; |
576 | 0 | } |
577 | | |
578 | | bool i_stream_have_bytes_left(struct istream *stream) |
579 | 0 | { |
580 | 0 | return i_stream_get_data_size(stream) > 0 || !stream->eof; |
581 | 0 | } |
582 | | |
583 | | bool i_stream_read_eof(struct istream *stream) |
584 | 0 | { |
585 | 0 | if (i_stream_get_data_size(stream) == 0) |
586 | 0 | (void)i_stream_read(stream); |
587 | 0 | return !i_stream_have_bytes_left(stream); |
588 | 0 | } |
589 | | |
590 | | uoff_t i_stream_get_absolute_offset(struct istream *stream) |
591 | 0 | { |
592 | 0 | uoff_t abs_offset = stream->v_offset; |
593 | 0 | while (stream != NULL) { |
594 | 0 | abs_offset += stream->real_stream->start_offset; |
595 | 0 | stream = stream->real_stream->parent; |
596 | 0 | } |
597 | 0 | return abs_offset; |
598 | 0 | } |
599 | | |
600 | | static char *i_stream_next_line_finish(struct istream_private *stream, size_t i) |
601 | 0 | { |
602 | 0 | char *ret; |
603 | 0 | size_t end; |
604 | |
|
605 | 0 | if (i > stream->skip && stream->buffer[i-1] == '\r') { |
606 | 0 | end = i - 1; |
607 | 0 | stream->line_crlf = TRUE; |
608 | 0 | } else { |
609 | 0 | end = i; |
610 | 0 | stream->line_crlf = FALSE; |
611 | 0 | } |
612 | |
|
613 | 0 | if (stream->buffer == stream->w_buffer && |
614 | 0 | end < stream->buffer_size) { |
615 | | /* modify the buffer directly */ |
616 | 0 | stream->w_buffer[end] = '\0'; |
617 | 0 | ret = (char *)stream->w_buffer + stream->skip; |
618 | 0 | } else { |
619 | | /* use a temporary string to return it */ |
620 | 0 | if (stream->line_str == NULL) |
621 | 0 | stream->line_str = str_new(default_pool, 256); |
622 | 0 | str_truncate(stream->line_str, 0); |
623 | 0 | if (stream->skip < end) |
624 | 0 | str_append_data(stream->line_str, stream->buffer + stream->skip, |
625 | 0 | end - stream->skip); |
626 | 0 | ret = str_c_modifiable(stream->line_str); |
627 | 0 | } |
628 | |
|
629 | 0 | if (i < stream->pos) |
630 | 0 | i++; |
631 | 0 | stream->istream.v_offset += i - stream->skip; |
632 | 0 | stream->skip = i; |
633 | 0 | return ret; |
634 | 0 | } |
635 | | |
636 | | static char *i_stream_last_line(struct istream_private *_stream) |
637 | 0 | { |
638 | 0 | if (_stream->istream.eof && _stream->skip != _stream->pos && |
639 | 0 | _stream->return_nolf_line) { |
640 | | /* the last line is missing LF and we want to return it. */ |
641 | 0 | return i_stream_next_line_finish(_stream, _stream->pos); |
642 | 0 | } |
643 | 0 | return NULL; |
644 | 0 | } |
645 | | |
646 | | char *i_stream_next_line(struct istream *stream) |
647 | 0 | { |
648 | 0 | struct istream_private *_stream = stream->real_stream; |
649 | 0 | const unsigned char *pos; |
650 | |
|
651 | 0 | if (_stream->skip >= _stream->pos) |
652 | 0 | return NULL; |
653 | | |
654 | 0 | pos = memchr(_stream->buffer + _stream->skip, '\n', |
655 | 0 | _stream->pos - _stream->skip); |
656 | 0 | if (pos != NULL) { |
657 | 0 | return i_stream_next_line_finish(_stream, |
658 | 0 | pos - _stream->buffer); |
659 | 0 | } else { |
660 | 0 | return i_stream_last_line(_stream); |
661 | 0 | } |
662 | 0 | } |
663 | | |
664 | | char *i_stream_read_next_line(struct istream *stream) |
665 | 0 | { |
666 | 0 | char *line; |
667 | |
|
668 | 0 | for (;;) { |
669 | 0 | line = i_stream_next_line(stream); |
670 | 0 | if (line != NULL) |
671 | 0 | break; |
672 | | |
673 | 0 | switch (i_stream_read(stream)) { |
674 | 0 | case -2: |
675 | 0 | io_stream_set_error(&stream->real_stream->iostream, |
676 | 0 | "Line is too long (over %zu" |
677 | 0 | " bytes at offset %"PRIuUOFF_T")", |
678 | 0 | i_stream_get_data_size(stream), stream->v_offset); |
679 | 0 | stream->stream_errno = errno = ENOBUFS; |
680 | 0 | stream->eof = TRUE; |
681 | 0 | return NULL; |
682 | 0 | case -1: |
683 | 0 | return i_stream_last_line(stream->real_stream); |
684 | 0 | case 0: |
685 | 0 | return NULL; |
686 | 0 | } |
687 | 0 | } |
688 | 0 | return line; |
689 | 0 | } |
690 | | |
691 | | bool i_stream_last_line_crlf(struct istream *stream) |
692 | 0 | { |
693 | 0 | return stream->real_stream->line_crlf; |
694 | 0 | } |
695 | | |
696 | | static bool i_stream_is_buffer_invalid(const struct istream_private *stream) |
697 | 2.28M | { |
698 | 2.28M | if (stream->parent == NULL) { |
699 | | /* the buffer can't point to parent, because it doesn't exist */ |
700 | 2.28M | return FALSE; |
701 | 2.28M | } |
702 | 0 | if (stream->w_buffer != NULL) { |
703 | | /* we can pretty safely assume that the stream is using its |
704 | | own private buffer, so it can never become invalid. */ |
705 | 0 | return FALSE; |
706 | 0 | } |
707 | 0 | if (stream->access_counter != |
708 | 0 | stream->parent->real_stream->access_counter) { |
709 | | /* parent has been modified behind this stream, we can't trust |
710 | | that our buffer is valid */ |
711 | 0 | return TRUE; |
712 | 0 | } |
713 | 0 | return i_stream_is_buffer_invalid(stream->parent->real_stream); |
714 | 0 | } |
715 | | |
716 | | const unsigned char * |
717 | | i_stream_get_data(struct istream *stream, size_t *size_r) |
718 | 2.29M | { |
719 | 2.29M | struct istream_private *_stream = stream->real_stream; |
720 | | |
721 | 2.29M | if (_stream->skip >= _stream->pos) { |
722 | 31.3k | *size_r = 0; |
723 | 31.3k | return uchar_empty_ptr; |
724 | 31.3k | } |
725 | | |
726 | 2.26M | if (unlikely(i_stream_is_buffer_invalid(_stream))) { |
727 | | /* This stream may be using parent's buffer directly as |
728 | | _stream->buffer, but the parent stream has already been |
729 | | modified indirectly. This means that the buffer might no |
730 | | longer point to where we assume it points to. So we'll |
731 | | just return the stream as empty until it's read again. |
732 | | |
733 | | It's a bit ugly to suddenly drop data from the stream that |
734 | | was already read, but since this happens only with shared |
735 | | parent istreams the caller is hopefully aware enough that |
736 | | something like this might happen. The other solutions would |
737 | | be to a) try to automatically read the data back (but we |
738 | | can't handle errors..) or b) always copy data to stream's |
739 | | own buffer instead of pointing to parent's buffer (but this |
740 | | causes data copying that is nearly always unnecessary). */ |
741 | 0 | *size_r = 0; |
742 | | /* if we had already read until EOF, mark the stream again as |
743 | | not being at the end of file. */ |
744 | 0 | if (stream->stream_errno == 0) { |
745 | 0 | _stream->skip = _stream->pos = 0; |
746 | 0 | stream->eof = FALSE; |
747 | 0 | } |
748 | 0 | return uchar_empty_ptr; |
749 | 0 | } |
750 | | |
751 | 2.26M | *size_r = _stream->pos - _stream->skip; |
752 | 2.26M | return _stream->buffer + _stream->skip; |
753 | 2.26M | } |
754 | | |
755 | | size_t i_stream_get_data_size(struct istream *stream) |
756 | 0 | { |
757 | 0 | size_t size; |
758 | |
|
759 | 0 | (void)i_stream_get_data(stream, &size); |
760 | 0 | return size; |
761 | 0 | } |
762 | | |
763 | | unsigned char *i_stream_get_modifiable_data(struct istream *stream, |
764 | | size_t *size_r) |
765 | 0 | { |
766 | 0 | struct istream_private *_stream = stream->real_stream; |
767 | |
|
768 | 0 | if (_stream->skip >= _stream->pos || _stream->w_buffer == NULL) { |
769 | 0 | *size_r = 0; |
770 | 0 | return NULL; |
771 | 0 | } |
772 | | |
773 | 0 | *size_r = _stream->pos - _stream->skip; |
774 | 0 | return _stream->w_buffer + _stream->skip; |
775 | 0 | } |
776 | | |
777 | | int i_stream_read_data(struct istream *stream, const unsigned char **data_r, |
778 | | size_t *size_r, size_t threshold) |
779 | 2.09M | { |
780 | 2.09M | ssize_t ret = 0; |
781 | 2.09M | bool read_more = FALSE; |
782 | | |
783 | 2.09M | do { |
784 | 2.09M | *data_r = i_stream_get_data(stream, size_r); |
785 | 2.09M | if (*size_r > threshold) |
786 | 2.06M | return 1; |
787 | | |
788 | | /* we need more data */ |
789 | 27.0k | ret = i_stream_read(stream); |
790 | 27.0k | if (ret > 0) |
791 | 4.81k | read_more = TRUE; |
792 | 27.0k | } while (ret > 0); |
793 | | |
794 | 22.1k | *data_r = i_stream_get_data(stream, size_r); |
795 | 22.1k | if (ret == -2) |
796 | 0 | return -2; |
797 | | |
798 | 22.1k | if (ret == 0) { |
799 | | /* need to read more */ |
800 | 0 | i_assert(!stream->blocking); |
801 | 0 | return 0; |
802 | 0 | } |
803 | 22.1k | if (stream->stream_errno == 0 && read_more) { |
804 | | /* we read at least some new data */ |
805 | 0 | return 0; |
806 | 0 | } |
807 | 22.1k | return -1; |
808 | 22.1k | } |
809 | | |
810 | | int i_stream_read_limited(struct istream *stream, const unsigned char **data_r, |
811 | | size_t *size_r, size_t limit) |
812 | 0 | { |
813 | 0 | struct istream_private *_stream = stream->real_stream; |
814 | 0 | int ret; |
815 | |
|
816 | 0 | *data_r = i_stream_get_data(stream, size_r); |
817 | 0 | if (*size_r >= limit) { |
818 | 0 | *size_r = limit; |
819 | 0 | return 1; |
820 | 0 | } |
821 | | |
822 | 0 | _stream->data_limit = limit; |
823 | 0 | ret = i_stream_read_more(stream, data_r, size_r); |
824 | 0 | _stream->data_limit = 0; |
825 | |
|
826 | 0 | if (*size_r >= limit) |
827 | 0 | *size_r = limit; |
828 | 0 | return ret; |
829 | 0 | } |
830 | | |
831 | | void i_stream_compress(struct istream_private *stream) |
832 | 0 | { |
833 | 0 | i_assert(stream->memarea == NULL || |
834 | 0 | memarea_get_refcount(stream->memarea) == 1); |
835 | | |
836 | 0 | if (stream->skip != stream->pos) { |
837 | 0 | memmove(stream->w_buffer, stream->w_buffer + stream->skip, |
838 | 0 | stream->pos - stream->skip); |
839 | 0 | } |
840 | 0 | stream->pos -= stream->skip; |
841 | |
|
842 | 0 | stream->skip = 0; |
843 | 0 | } |
844 | | |
845 | | static void i_stream_w_buffer_free(void *buf) |
846 | 0 | { |
847 | 0 | i_free(buf); |
848 | 0 | } |
849 | | |
850 | | static void |
851 | | i_stream_w_buffer_realloc(struct istream_private *stream, size_t old_size) |
852 | 0 | { |
853 | 0 | void *new_buffer; |
854 | |
|
855 | 0 | if (stream->memarea != NULL && |
856 | 0 | memarea_get_refcount(stream->memarea) == 1) { |
857 | | /* Nobody else is referencing the memarea. |
858 | | We can just reallocate it. */ |
859 | 0 | memarea_free_without_callback(&stream->memarea); |
860 | 0 | new_buffer = i_realloc(stream->w_buffer, old_size, |
861 | 0 | stream->buffer_size); |
862 | 0 | } else { |
863 | 0 | new_buffer = i_malloc(stream->buffer_size); |
864 | 0 | if (old_size > 0) { |
865 | 0 | i_assert(stream->w_buffer != NULL); |
866 | 0 | memcpy(new_buffer, stream->w_buffer, old_size); |
867 | 0 | } |
868 | 0 | if (stream->memarea != NULL) |
869 | 0 | memarea_unref(&stream->memarea); |
870 | 0 | } |
871 | | |
872 | 0 | stream->w_buffer = new_buffer; |
873 | 0 | stream->buffer = new_buffer; |
874 | |
|
875 | 0 | stream->memarea = memarea_init(stream->w_buffer, stream->buffer_size, |
876 | 0 | i_stream_w_buffer_free, new_buffer); |
877 | 0 | } |
878 | | |
879 | | void i_stream_grow_buffer(struct istream_private *stream, size_t bytes) |
880 | 0 | { |
881 | 0 | size_t old_size, max_size; |
882 | |
|
883 | 0 | old_size = stream->buffer_size; |
884 | |
|
885 | 0 | stream->buffer_size = stream->pos + bytes; |
886 | 0 | if (stream->buffer_size <= stream->init_buffer_size) |
887 | 0 | stream->buffer_size = stream->init_buffer_size; |
888 | 0 | else |
889 | 0 | stream->buffer_size = nearest_power(stream->buffer_size); |
890 | |
|
891 | 0 | max_size = i_stream_get_max_buffer_size(&stream->istream); |
892 | 0 | i_assert(max_size > 0); |
893 | 0 | if (stream->buffer_size > max_size) |
894 | 0 | stream->buffer_size = max_size; |
895 | |
|
896 | 0 | if (stream->buffer_size <= old_size) |
897 | 0 | stream->buffer_size = old_size; |
898 | 0 | else |
899 | 0 | i_stream_w_buffer_realloc(stream, old_size); |
900 | 0 | } |
901 | | |
902 | | bool i_stream_try_alloc(struct istream_private *stream, |
903 | | size_t wanted_size, size_t *size_r) |
904 | 0 | { |
905 | 0 | i_assert(wanted_size > 0); |
906 | 0 | i_assert(stream->buffer_size >= stream->pos); |
907 | | |
908 | 0 | if (wanted_size > stream->buffer_size - stream->pos) { |
909 | 0 | if (stream->skip > 0) { |
910 | | /* remove the unused bytes from beginning of buffer */ |
911 | 0 | if (stream->memarea != NULL && |
912 | 0 | memarea_get_refcount(stream->memarea) > 1) { |
913 | | /* The memarea is still referenced. We can't |
914 | | overwrite data until extra references are |
915 | | gone. */ |
916 | 0 | i_stream_w_buffer_realloc(stream, stream->buffer_size); |
917 | 0 | } |
918 | 0 | i_stream_compress(stream); |
919 | 0 | } else if (stream->buffer_size < i_stream_get_max_buffer_size(&stream->istream)) { |
920 | | /* buffer is full - grow it */ |
921 | 0 | i_stream_grow_buffer(stream, I_STREAM_MIN_SIZE); |
922 | 0 | } |
923 | 0 | } |
924 | |
|
925 | 0 | if (stream->data_limit == 0 || |
926 | 0 | (stream->buffer_size - stream->skip) < stream->data_limit) |
927 | 0 | *size_r = stream->buffer_size - stream->pos; |
928 | 0 | else { |
929 | 0 | size_t buffered = (stream->pos - stream->skip); |
930 | |
|
931 | 0 | if (buffered >= stream->data_limit) |
932 | 0 | *size_r = 0; |
933 | 0 | else |
934 | 0 | *size_r = stream->data_limit - buffered; |
935 | 0 | } |
936 | 0 | i_assert(stream->w_buffer != NULL || *size_r == 0); |
937 | 0 | return *size_r > 0; |
938 | 0 | } |
939 | | |
940 | | bool ATTR_NOWARN_UNUSED_RESULT |
941 | | i_stream_try_alloc_avoid_compress(struct istream_private *stream, |
942 | | size_t wanted_size, size_t *size_r) |
943 | 0 | { |
944 | 0 | size_t old_skip = stream->skip; |
945 | | |
946 | | /* try first with skip=0, so no compression is done */ |
947 | 0 | stream->skip = 0; |
948 | 0 | bool ret = i_stream_try_alloc(stream, wanted_size, size_r); |
949 | 0 | stream->skip = old_skip; |
950 | 0 | if (ret || old_skip == 0) |
951 | 0 | return ret; |
952 | | /* it's full. try with compression. */ |
953 | 0 | return i_stream_try_alloc(stream, wanted_size, size_r); |
954 | 0 | } |
955 | | |
956 | | void *i_stream_alloc(struct istream_private *stream, size_t size) |
957 | 0 | { |
958 | 0 | size_t old_size, avail_size; |
959 | |
|
960 | 0 | (void)i_stream_try_alloc(stream, size, &avail_size); |
961 | 0 | if (avail_size < size) { |
962 | 0 | old_size = stream->buffer_size; |
963 | 0 | stream->buffer_size = nearest_power(stream->pos + size); |
964 | 0 | i_stream_w_buffer_realloc(stream, old_size); |
965 | |
|
966 | 0 | (void)i_stream_try_alloc(stream, size, &avail_size); |
967 | 0 | i_assert(avail_size >= size); |
968 | 0 | } |
969 | 0 | return stream->w_buffer + stream->pos; |
970 | 0 | } |
971 | | |
972 | | void i_stream_memarea_detach(struct istream_private *stream) |
973 | 0 | { |
974 | 0 | if (stream->memarea != NULL) { |
975 | | /* Don't overwrite data in a snapshot. Allocate a new |
976 | | buffer instead. */ |
977 | 0 | memarea_unref(&stream->memarea); |
978 | 0 | stream->buffer_size = 0; |
979 | 0 | stream->buffer = NULL; |
980 | 0 | stream->w_buffer = NULL; |
981 | 0 | } |
982 | 0 | } |
983 | | |
984 | | bool i_stream_add_data(struct istream *_stream, const unsigned char *data, |
985 | | size_t size) |
986 | 0 | { |
987 | 0 | struct istream_private *stream = _stream->real_stream; |
988 | 0 | size_t size2; |
989 | |
|
990 | 0 | if (size == 0) |
991 | 0 | return TRUE; |
992 | 0 | (void)i_stream_try_alloc(stream, size, &size2); |
993 | 0 | if (size > size2) |
994 | 0 | return FALSE; |
995 | | |
996 | 0 | memcpy(stream->w_buffer + stream->pos, data, size); |
997 | 0 | stream->pos += size; |
998 | 0 | return TRUE; |
999 | 0 | } |
1000 | | |
1001 | | struct istream *i_stream_get_root_io(struct istream *stream) |
1002 | 0 | { |
1003 | 0 | while (stream->real_stream->parent != NULL) { |
1004 | 0 | i_assert(stream->real_stream->io == NULL); |
1005 | 0 | stream = stream->real_stream->parent; |
1006 | 0 | } |
1007 | 0 | return stream; |
1008 | 0 | } |
1009 | | |
1010 | | void i_stream_set_input_pending(struct istream *stream, bool pending) |
1011 | 0 | { |
1012 | 0 | if (!pending) |
1013 | 0 | return; |
1014 | | |
1015 | 0 | stream = i_stream_get_root_io(stream); |
1016 | 0 | if (stream->real_stream->io != NULL) |
1017 | 0 | io_set_pending(stream->real_stream->io); |
1018 | 0 | else |
1019 | 0 | stream->real_stream->io_pending = TRUE; |
1020 | 0 | } |
1021 | | |
1022 | | void i_stream_switch_ioloop_to(struct istream *stream, struct ioloop *ioloop) |
1023 | 0 | { |
1024 | 0 | io_stream_switch_ioloop_to(&stream->real_stream->iostream, ioloop); |
1025 | |
|
1026 | 0 | do { |
1027 | 0 | if (stream->real_stream->switch_ioloop_to != NULL) { |
1028 | 0 | stream->real_stream->switch_ioloop_to( |
1029 | 0 | stream->real_stream, ioloop); |
1030 | 0 | } |
1031 | 0 | stream = stream->real_stream->parent; |
1032 | 0 | } while (stream != NULL); |
1033 | 0 | } |
1034 | | |
1035 | | void i_stream_switch_ioloop(struct istream *stream) |
1036 | 0 | { |
1037 | 0 | i_stream_switch_ioloop_to(stream, current_ioloop); |
1038 | 0 | } |
1039 | | |
1040 | | void i_stream_set_io(struct istream *stream, struct io *io) |
1041 | 0 | { |
1042 | 0 | stream = i_stream_get_root_io(stream); |
1043 | |
|
1044 | 0 | i_assert(stream->real_stream->io == NULL); |
1045 | 0 | stream->real_stream->io = io; |
1046 | 0 | if (stream->real_stream->io_pending) { |
1047 | 0 | io_set_pending(io); |
1048 | 0 | stream->real_stream->io_pending = FALSE; |
1049 | 0 | } |
1050 | 0 | } |
1051 | | |
1052 | | void i_stream_unset_io(struct istream *stream, struct io *io) |
1053 | 0 | { |
1054 | 0 | stream = i_stream_get_root_io(stream); |
1055 | |
|
1056 | 0 | i_assert(stream->real_stream->io == io); |
1057 | 0 | if (io_is_pending(io)) |
1058 | 0 | stream->real_stream->io_pending = TRUE; |
1059 | 0 | stream->real_stream->io = NULL; |
1060 | 0 | } |
1061 | | |
1062 | | static void |
1063 | | i_stream_default_set_max_buffer_size(struct iostream_private *stream, |
1064 | | size_t max_size) |
1065 | 0 | { |
1066 | 0 | struct istream_private *_stream = |
1067 | 0 | container_of(stream, struct istream_private, iostream); |
1068 | |
|
1069 | 0 | _stream->max_buffer_size = max_size; |
1070 | 0 | if (_stream->parent != NULL) |
1071 | 0 | i_stream_set_max_buffer_size(_stream->parent, max_size); |
1072 | 0 | } |
1073 | | |
1074 | | static void i_stream_default_close(struct iostream_private *stream, |
1075 | | bool close_parent) |
1076 | 4.81k | { |
1077 | 4.81k | struct istream_private *_stream = |
1078 | 4.81k | container_of(stream, struct istream_private, iostream); |
1079 | | |
1080 | 4.81k | if (close_parent) |
1081 | 0 | i_stream_close(_stream->parent); |
1082 | 4.81k | } |
1083 | | |
1084 | | static void i_stream_default_destroy(struct iostream_private *stream) |
1085 | 4.81k | { |
1086 | 4.81k | struct istream_private *_stream = |
1087 | 4.81k | container_of(stream, struct istream_private, iostream); |
1088 | | |
1089 | 4.81k | i_stream_free_buffer(_stream); |
1090 | 4.81k | i_stream_unref(&_stream->parent); |
1091 | 4.81k | } |
1092 | | |
1093 | | static void |
1094 | | i_stream_default_seek_seekable(struct istream_private *stream, |
1095 | | uoff_t v_offset, bool mark ATTR_UNUSED) |
1096 | 0 | { |
1097 | 0 | stream->istream.v_offset = v_offset; |
1098 | 0 | stream->skip = stream->pos = 0; |
1099 | 0 | } |
1100 | | |
1101 | | void i_stream_default_seek_nonseekable(struct istream_private *stream, |
1102 | | uoff_t v_offset, bool mark ATTR_UNUSED) |
1103 | 0 | { |
1104 | 0 | size_t available; |
1105 | |
|
1106 | 0 | if (stream->istream.v_offset > v_offset) |
1107 | 0 | i_panic("stream %s doesn't support seeking backwards", |
1108 | 0 | i_stream_get_name(&stream->istream)); |
1109 | | |
1110 | 0 | while (stream->istream.v_offset < v_offset) { |
1111 | 0 | (void)i_stream_read(&stream->istream); |
1112 | |
|
1113 | 0 | available = stream->pos - stream->skip; |
1114 | 0 | if (available == 0) { |
1115 | 0 | if (stream->istream.stream_errno != 0) { |
1116 | | /* read failed */ |
1117 | 0 | return; |
1118 | 0 | } |
1119 | 0 | io_stream_set_error(&stream->iostream, |
1120 | 0 | "Can't seek to offset %"PRIuUOFF_T |
1121 | 0 | ", because we have data only up to offset %" |
1122 | 0 | PRIuUOFF_T" (eof=%d)", v_offset, |
1123 | 0 | stream->istream.v_offset, stream->istream.eof ? 1 : 0); |
1124 | 0 | stream->istream.stream_errno = ESPIPE; |
1125 | 0 | return; |
1126 | 0 | } |
1127 | 0 | if (available <= v_offset - stream->istream.v_offset) |
1128 | 0 | i_stream_skip(&stream->istream, available); |
1129 | 0 | else { |
1130 | 0 | i_stream_skip(&stream->istream, |
1131 | 0 | v_offset - stream->istream.v_offset); |
1132 | 0 | } |
1133 | 0 | } |
1134 | 0 | } |
1135 | | |
1136 | | bool i_stream_nonseekable_try_seek(struct istream_private *stream, |
1137 | | uoff_t v_offset) |
1138 | 0 | { |
1139 | 0 | uoff_t start_offset = stream->istream.v_offset - stream->skip; |
1140 | |
|
1141 | 0 | if (v_offset < start_offset) { |
1142 | | /* have to seek backwards */ |
1143 | 0 | i_stream_seek(stream->parent, stream->parent_start_offset); |
1144 | 0 | stream->parent_expected_offset = stream->parent_start_offset; |
1145 | 0 | stream->skip = stream->pos = 0; |
1146 | 0 | stream->istream.v_offset = 0; |
1147 | 0 | stream->high_pos = 0; |
1148 | 0 | return FALSE; |
1149 | 0 | } |
1150 | | |
1151 | 0 | if (v_offset <= start_offset + stream->pos) { |
1152 | | /* seeking backwards within what's already cached */ |
1153 | 0 | stream->skip = v_offset - start_offset; |
1154 | 0 | stream->istream.v_offset = v_offset; |
1155 | 0 | if (stream->high_pos == 0) |
1156 | 0 | stream->high_pos = stream->pos; |
1157 | 0 | stream->pos = stream->skip; |
1158 | 0 | } else { |
1159 | | /* read forward */ |
1160 | 0 | i_stream_default_seek_nonseekable(stream, v_offset, FALSE); |
1161 | 0 | } |
1162 | 0 | return TRUE; |
1163 | 0 | } |
1164 | | |
1165 | | static int |
1166 | | seekable_i_stream_get_size(struct istream_private *stream) |
1167 | 0 | { |
1168 | 0 | if (stream->cached_stream_size == UOFF_T_MAX) { |
1169 | 0 | uoff_t old_offset = stream->istream.v_offset; |
1170 | 0 | ssize_t ret; |
1171 | |
|
1172 | 0 | do { |
1173 | 0 | i_stream_skip(&stream->istream, |
1174 | 0 | i_stream_get_data_size(&stream->istream)); |
1175 | 0 | } while ((ret = i_stream_read(&stream->istream)) > 0); |
1176 | 0 | i_assert(ret == -1); |
1177 | 0 | if (stream->istream.stream_errno != 0) |
1178 | 0 | return -1; |
1179 | | |
1180 | 0 | stream->cached_stream_size = stream->istream.v_offset; |
1181 | 0 | i_stream_seek(&stream->istream, old_offset); |
1182 | 0 | } |
1183 | 0 | stream->statbuf.st_size = stream->cached_stream_size; |
1184 | 0 | return 0; |
1185 | 0 | } |
1186 | | |
1187 | | static int |
1188 | | i_stream_default_stat(struct istream_private *stream, bool exact) |
1189 | 0 | { |
1190 | 0 | const struct stat *st; |
1191 | |
|
1192 | 0 | if (stream->parent == NULL) |
1193 | 0 | return stream->istream.stream_errno == 0 ? 0 : -1; |
1194 | | |
1195 | 0 | if (i_stream_stat(stream->parent, exact, &st) < 0) { |
1196 | 0 | stream->istream.stream_errno = stream->parent->stream_errno; |
1197 | 0 | return -1; |
1198 | 0 | } |
1199 | 0 | stream->statbuf = *st; |
1200 | 0 | if (exact && !stream->stream_size_passthrough) { |
1201 | | /* exact size is not known, even if parent returned something */ |
1202 | 0 | stream->statbuf.st_size = -1; |
1203 | 0 | if (stream->istream.seekable) { |
1204 | 0 | if (seekable_i_stream_get_size(stream) < 0) |
1205 | 0 | return -1; |
1206 | 0 | } |
1207 | 0 | } else { |
1208 | | /* When exact=FALSE always return the parent stat's size, even |
1209 | | if we know the exact value. This is necessary because |
1210 | | otherwise e.g. mbox code can see two different values and |
1211 | | think that the mbox file keeps changing. */ |
1212 | 0 | } |
1213 | 0 | return 0; |
1214 | 0 | } |
1215 | | |
1216 | | static int |
1217 | | i_stream_default_get_size(struct istream_private *stream, |
1218 | | bool exact, uoff_t *size_r) |
1219 | 0 | { |
1220 | 0 | if (stream->stat(stream, exact) < 0) |
1221 | 0 | return -1; |
1222 | 0 | if (stream->statbuf.st_size == -1) |
1223 | 0 | return 0; |
1224 | | |
1225 | 0 | *size_r = stream->statbuf.st_size; |
1226 | 0 | return 1; |
1227 | 0 | } |
1228 | | |
1229 | | void i_stream_init_parent(struct istream_private *_stream, |
1230 | | struct istream *parent) |
1231 | 0 | { |
1232 | 0 | _stream->access_counter = parent->real_stream->access_counter; |
1233 | 0 | _stream->parent = parent; |
1234 | 0 | _stream->parent_start_offset = parent->v_offset; |
1235 | 0 | _stream->parent_expected_offset = parent->v_offset; |
1236 | 0 | _stream->start_offset = parent->v_offset; |
1237 | | /* if parent stream is an istream-error, copy the error */ |
1238 | 0 | _stream->istream.stream_errno = parent->stream_errno; |
1239 | 0 | _stream->istream.eof = parent->eof; |
1240 | 0 | i_stream_ref(parent); |
1241 | 0 | } |
1242 | | |
1243 | | struct istream * |
1244 | | i_stream_create(struct istream_private *_stream, struct istream *parent, int fd, |
1245 | | enum istream_create_flag flags) |
1246 | 4.81k | { |
1247 | 4.81k | bool noop_snapshot = (flags & ISTREAM_CREATE_FLAG_NOOP_SNAPSHOT) != 0; |
1248 | | |
1249 | 4.81k | _stream->fd = fd; |
1250 | 4.81k | if (parent != NULL) |
1251 | 0 | i_stream_init_parent(_stream, parent); |
1252 | 4.81k | else if (_stream->memarea == NULL && !noop_snapshot) { |
1253 | | /* The stream has no parent and no memarea yet. We'll assume |
1254 | | that it wants to be using memareas for the reads. */ |
1255 | 4.81k | _stream->memarea = memarea_init_empty(); |
1256 | 4.81k | } |
1257 | 4.81k | _stream->istream.real_stream = _stream; |
1258 | | |
1259 | 4.81k | if (_stream->iostream.close == NULL) |
1260 | 4.81k | _stream->iostream.close = i_stream_default_close; |
1261 | 4.81k | if (_stream->iostream.destroy == NULL) |
1262 | 4.81k | _stream->iostream.destroy = i_stream_default_destroy; |
1263 | 4.81k | if (_stream->seek == NULL) { |
1264 | 0 | _stream->seek = _stream->istream.seekable ? |
1265 | 0 | i_stream_default_seek_seekable : |
1266 | 0 | i_stream_default_seek_nonseekable; |
1267 | 0 | } |
1268 | 4.81k | if (_stream->stat == NULL) |
1269 | 4.81k | _stream->stat = i_stream_default_stat; |
1270 | 4.81k | if (_stream->get_size == NULL) |
1271 | 4.81k | _stream->get_size = i_stream_default_get_size; |
1272 | 4.81k | if (_stream->snapshot == NULL) { |
1273 | 4.81k | _stream->snapshot = noop_snapshot ? |
1274 | 0 | i_stream_noop_snapshot : |
1275 | 4.81k | i_stream_default_snapshot; |
1276 | 4.81k | } |
1277 | 4.81k | if (_stream->iostream.set_max_buffer_size == NULL) { |
1278 | 4.81k | _stream->iostream.set_max_buffer_size = |
1279 | 4.81k | i_stream_default_set_max_buffer_size; |
1280 | 4.81k | } |
1281 | 4.81k | if (_stream->init_buffer_size == 0) |
1282 | 4.81k | _stream->init_buffer_size = I_STREAM_MIN_SIZE; |
1283 | | |
1284 | 4.81k | i_zero(&_stream->statbuf); |
1285 | 4.81k | _stream->statbuf.st_size = -1; |
1286 | 4.81k | _stream->statbuf.st_atime = |
1287 | 4.81k | _stream->statbuf.st_mtime = |
1288 | 4.81k | _stream->statbuf.st_ctime = ioloop_time; |
1289 | 4.81k | _stream->cached_stream_size = UOFF_T_MAX; |
1290 | | |
1291 | 4.81k | io_stream_init(&_stream->iostream); |
1292 | | |
1293 | 4.81k | if (_stream->istream.stream_errno != 0) |
1294 | 0 | _stream->istream.eof = TRUE; |
1295 | | |
1296 | 4.81k | return &_stream->istream; |
1297 | 4.81k | } |
1298 | | |
1299 | | struct istream *i_stream_create_error(int stream_errno) |
1300 | 0 | { |
1301 | 0 | struct istream_private *stream; |
1302 | |
|
1303 | 0 | stream = i_new(struct istream_private, 1); |
1304 | 0 | stream->istream.closed = TRUE; |
1305 | 0 | stream->istream.readable_fd = FALSE; |
1306 | 0 | stream->istream.blocking = TRUE; |
1307 | 0 | stream->istream.seekable = TRUE; |
1308 | 0 | stream->istream.eof = TRUE; |
1309 | 0 | stream->istream.stream_errno = stream_errno; |
1310 | | /* Nothing can ever actually be read from this stream, but set a |
1311 | | reasonable max_buffer_size anyway since some filter istreams don't |
1312 | | behave properly otherwise. */ |
1313 | 0 | stream->max_buffer_size = IO_BLOCK_SIZE; |
1314 | 0 | i_stream_create(stream, NULL, -1, 0); |
1315 | 0 | i_stream_set_name(&stream->istream, "(error)"); |
1316 | 0 | return &stream->istream; |
1317 | 0 | } |
1318 | | |
1319 | | struct istream * |
1320 | | i_stream_create_error_str(int stream_errno, const char *fmt, ...) |
1321 | 0 | { |
1322 | 0 | struct istream *input; |
1323 | 0 | va_list args; |
1324 | |
|
1325 | 0 | va_start(args, fmt); |
1326 | 0 | input = i_stream_create_error(stream_errno); |
1327 | 0 | io_stream_set_verror(&input->real_stream->iostream, fmt, args); |
1328 | 0 | va_end(args); |
1329 | 0 | return input; |
1330 | 0 | } |