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