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