Coverage Report

Created: 2023-06-07 06:18

/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
}