Coverage Report

Created: 2025-12-14 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib/ostream-file.c
Line
Count
Source
1
/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2
3
/* @UNSAFE: whole file */
4
5
#include "lib.h"
6
#include "ioloop.h"
7
#include "write-full.h"
8
#include "net.h"
9
#include "sendfile-util.h"
10
#include "istream.h"
11
#include "istream-private.h"
12
#include "ostream-file-private.h"
13
14
#include <unistd.h>
15
#include <sys/stat.h>
16
#ifdef HAVE_SYS_UIO_H
17
#  include <sys/uio.h>
18
#endif
19
#include <fcntl.h>
20
21
/* try to keep the buffer size within 4k..128k. ReiserFS may actually return
22
   128k as optimal size. */
23
12.1k
#define DEFAULT_OPTIMAL_BLOCK_SIZE IO_BLOCK_SIZE
24
#define MAX_OPTIMAL_BLOCK_SIZE (128*1024)
25
26
#define IS_STREAM_EMPTY(fstream) \
27
588k
  ((fstream)->head == (fstream)->tail && !(fstream)->full)
28
29
#define MAX_SSIZE_T(size) \
30
0
  ((size) < SSIZE_T_MAX ? (size_t)(size) : SSIZE_T_MAX)
31
32
static void stream_send_io(struct file_ostream *fstream);
33
34
static void stream_closed(struct file_ostream *fstream)
35
18.2k
{
36
18.2k
  io_remove(&fstream->io);
37
38
18.2k
  bool refs_left = fstream->fd_ref != NULL &&
39
0
    iostream_fd_unref(&fstream->fd_ref);
40
18.2k
  if (fstream->autoclose_fd && fstream->fd != -1 && !refs_left) {
41
    /* Ignore ECONNRESET because we don't really care about it here,
42
       as we are closing the socket down in any case. There might be
43
       unsent data but nothing we can do about that. */
44
0
    if (unlikely(close(fstream->fd) < 0 && errno != ECONNRESET)) {
45
0
      i_error("file_ostream.close(%s) failed: %m",
46
0
        o_stream_get_name(&fstream->ostream.ostream));
47
0
    }
48
0
  }
49
18.2k
  fstream->fd = -1;
50
51
18.2k
  fstream->ostream.ostream.closed = TRUE;
52
18.2k
}
53
54
void o_stream_file_close(struct iostream_private *stream,
55
        bool close_parent ATTR_UNUSED)
56
18.2k
{
57
18.2k
  struct file_ostream *fstream =
58
18.2k
    container_of(stream, struct file_ostream, ostream.iostream);
59
60
18.2k
  stream_closed(fstream);
61
18.2k
}
62
63
static void o_stream_file_destroy(struct iostream_private *stream)
64
12.1k
{
65
12.1k
  struct file_ostream *fstream =
66
12.1k
    container_of(stream, struct file_ostream, ostream.iostream);
67
68
12.1k
  i_free(fstream->buffer);
69
12.1k
}
70
71
static size_t file_buffer_get_used_size(struct file_ostream *fstream)
72
0
{
73
0
  if (fstream->head == fstream->tail)
74
0
    return fstream->full ? fstream->buffer_size : 0;
75
0
  else if (fstream->head < fstream->tail) {
76
    /* ...HXXXT... */
77
0
    return fstream->tail - fstream->head;
78
0
  } else {
79
    /* XXXT...HXXX */
80
0
    return fstream->tail +
81
0
      (fstream->buffer_size - fstream->head);
82
0
  }
83
0
}
84
85
static void update_buffer(struct file_ostream *fstream, size_t size)
86
38.0k
{
87
38.0k
  size_t used;
88
89
38.0k
  if (IS_STREAM_EMPTY(fstream) || size == 0)
90
674
    return;
91
92
37.3k
  if (fstream->head < fstream->tail) {
93
    /* ...HXXXT... */
94
37.1k
    used = fstream->tail - fstream->head;
95
37.1k
    i_assert(size <= used);
96
37.1k
    fstream->head += size;
97
37.1k
  } else {
98
    /* XXXT...HXXX */
99
245
    used = fstream->buffer_size - fstream->head;
100
245
    if (size > used) {
101
0
      size -= used;
102
0
      i_assert(size <= fstream->tail);
103
0
      fstream->head = size;
104
245
    } else {
105
245
      fstream->head += size;
106
245
    }
107
108
245
    fstream->full = FALSE;
109
245
  }
110
111
37.3k
  if (fstream->head == fstream->tail)
112
37.1k
    fstream->head = fstream->tail = 0;
113
114
37.3k
  if (fstream->head == fstream->buffer_size)
115
245
    fstream->head = 0;
116
37.3k
}
117
118
static void o_stream_socket_cork(struct file_ostream *fstream)
119
38.9k
{
120
38.9k
  if (fstream->ostream.corked && !fstream->socket_cork_set) {
121
38.5k
    if (!fstream->no_socket_cork) {
122
0
      if (net_set_cork(fstream->fd, TRUE) < 0)
123
0
        fstream->no_socket_cork = TRUE;
124
0
      else
125
0
        fstream->socket_cork_set = TRUE;
126
0
    }
127
38.5k
  }
128
38.9k
}
129
130
static int o_stream_lseek(struct file_ostream *fstream)
131
0
{
132
0
  off_t ret;
133
134
0
  if (fstream->real_offset == fstream->buffer_offset)
135
0
    return 0;
136
137
0
  ret = lseek(fstream->fd, (off_t)fstream->buffer_offset, SEEK_SET);
138
0
  if (ret < 0) {
139
0
    io_stream_set_error(&fstream->ostream.iostream,
140
0
            "lseek() failed: %m");
141
0
    fstream->ostream.ostream.stream_errno = errno;
142
0
    return -1;
143
0
  }
144
145
0
  if (ret != (off_t)fstream->buffer_offset) {
146
0
    io_stream_set_error(&fstream->ostream.iostream,
147
0
            "lseek() returned wrong value");
148
0
    fstream->ostream.ostream.stream_errno = EINVAL;
149
0
    return -1;
150
0
  }
151
0
  fstream->real_offset = fstream->buffer_offset;
152
0
  return 0;
153
0
}
154
155
ssize_t o_stream_file_writev(struct file_ostream *fstream,
156
           const struct const_iovec *iov,
157
           unsigned int iov_count,
158
           const char **error_r)
159
38.9k
{
160
38.9k
  const char *syscall = NULL;
161
38.9k
  ssize_t ret;
162
38.9k
  size_t size, sent;
163
38.9k
  unsigned int i;
164
165
38.9k
  if (iov_count == 1) {
166
38.9k
    i_assert(iov->iov_len > 0);
167
168
38.9k
    if (!fstream->file ||
169
38.9k
        fstream->real_offset == fstream->buffer_offset) {
170
38.9k
      syscall = "write";
171
38.9k
      ret = write(fstream->fd, iov->iov_base, iov->iov_len);
172
38.9k
      if (ret > 0)
173
38.2k
        fstream->real_offset += ret;
174
38.9k
    } else {
175
0
      syscall = "pwrite";
176
0
      ret = pwrite(fstream->fd, iov->iov_base, iov->iov_len,
177
0
             fstream->buffer_offset);
178
0
    }
179
38.9k
  } else {
180
0
    if (o_stream_lseek(fstream) < 0) {
181
0
      *error_r = t_strdup(o_stream_get_error(&fstream->ostream.ostream));
182
0
      return -1;
183
0
    }
184
185
0
    syscall = "writev";
186
0
    sent = 0;
187
0
    while (iov_count > IOV_MAX) {
188
0
      size = 0;
189
0
      for (i = 0; i < IOV_MAX; i++)
190
0
        size += iov[i].iov_len;
191
192
0
      ret = writev(fstream->fd, (const struct iovec *)iov,
193
0
             IOV_MAX);
194
0
      if (ret != (ssize_t)size) {
195
0
        break;
196
0
      }
197
198
0
      fstream->real_offset += ret;
199
0
      sent += ret;
200
0
      iov += IOV_MAX;
201
0
      iov_count -= IOV_MAX;
202
0
    }
203
204
0
    if (iov_count <= IOV_MAX) {
205
0
      size = 0;
206
0
      for (i = 0; i < iov_count; i++)
207
0
        size += iov[i].iov_len;
208
209
0
      ret = writev(fstream->fd, (const struct iovec *)iov,
210
0
             iov_count);
211
0
      if (ret > 0)
212
0
        fstream->real_offset += ret;
213
0
    }
214
0
    if (ret > 0)
215
0
      ret += sent;
216
0
    else if (!fstream->file && sent > 0) {
217
      /* return what we managed to get sent */
218
0
      ret = sent;
219
0
    }
220
0
  }
221
38.9k
  if (ret < 0) {
222
674
    i_assert(syscall != NULL);
223
674
    *error_r = t_strdup_printf("%s() failed: %m", syscall);
224
674
  }
225
38.9k
  return ret;
226
38.9k
}
227
228
static ssize_t
229
o_stream_file_writev_full(struct file_ostream *fstream,
230
           const struct const_iovec *iov,
231
           unsigned int iov_count)
232
38.9k
{
233
38.9k
  const char *error = NULL;
234
38.9k
  ssize_t ret, ret2;
235
38.9k
  size_t size, total_size;
236
38.9k
  bool partial;
237
38.9k
  unsigned int i;
238
239
77.8k
  for (i = 0, total_size = 0; i < iov_count; i++)
240
38.9k
    total_size += iov[i].iov_len;
241
242
38.9k
  o_stream_socket_cork(fstream);
243
38.9k
  if (fstream->no_delay_enabled && !fstream->ostream.corked) {
244
    /* TCP_NODELAY is currently set, but stream isn't corked.
245
       Unset TCP_NODELAY to add delays. */
246
0
    if (net_set_tcp_nodelay(fstream->fd, FALSE) < 0) {
247
      /* We already successfully enabled TCP_NODELAY, so there
248
         shouldn't really be errors. Except ECONNRESET can
249
         possibly still happen between these two calls, so
250
         again don't log errors. */
251
0
      fstream->no_socket_nodelay = TRUE;
252
0
    }
253
0
    fstream->no_delay_enabled = FALSE;
254
0
  }
255
256
38.9k
  ret = fstream->writev(fstream, iov, iov_count, &error);
257
38.9k
  partial = ret != (ssize_t)total_size;
258
259
38.9k
  if (ret < 0) {
260
674
    i_assert(error != NULL);
261
674
    if (fstream->file) {
262
0
      if (errno == EINTR) {
263
        /* automatically retry */
264
0
        return o_stream_file_writev_full(fstream, iov, iov_count);
265
0
      }
266
674
    } else if (errno == EAGAIN || errno == EINTR) {
267
      /* try again later */
268
674
      return 0;
269
674
    }
270
0
    io_stream_set_error(&fstream->ostream.iostream, "%s", error);
271
0
    fstream->ostream.ostream.stream_errno = errno;
272
0
    stream_closed(fstream);
273
0
    return -1;
274
674
  }
275
38.2k
  if (unlikely(ret == 0 && fstream->file)) {
276
    /* assume out of disk space */
277
0
    fstream->ostream.ostream.stream_errno = ENOSPC;
278
0
    stream_closed(fstream);
279
0
    return -1;
280
0
  }
281
38.2k
  fstream->buffer_offset += ret;
282
38.2k
  if (partial && fstream->file) {
283
    /* we failed to write everything to a file. either we ran out
284
       of disk space or we're writing to NFS. try to write the
285
       rest to resolve this. */
286
0
    size = ret;
287
0
    while (iov_count > 0 && size >= iov->iov_len) {
288
0
      size -= iov->iov_len;
289
0
      iov++;
290
0
      iov_count--;
291
0
    }
292
0
    i_assert(iov_count > 0);
293
0
    if (size == 0)
294
0
      ret2 = o_stream_file_writev_full(fstream, iov, iov_count);
295
0
    else {
296
      /* write the first iov separately */
297
0
      struct const_iovec new_iov;
298
299
0
      new_iov.iov_base =
300
0
        CONST_PTR_OFFSET(iov->iov_base, size);
301
0
      new_iov.iov_len = iov->iov_len - size;
302
0
      ret2 = o_stream_file_writev_full(fstream, &new_iov, 1);
303
0
      if (ret2 > 0) {
304
0
        i_assert((size_t)ret2 == new_iov.iov_len);
305
        /* write the rest */
306
0
        if (iov_count > 1) {
307
0
          ret += ret2;
308
0
          ret2 = o_stream_file_writev_full(fstream, iov + 1,
309
0
                     iov_count - 1);
310
0
        }
311
0
      }
312
0
    }
313
0
    i_assert(ret2 != 0);
314
0
    if (ret2 < 0)
315
0
      ret = ret2;
316
0
    else
317
0
      ret += ret2;
318
0
  }
319
38.2k
  i_assert(ret < 0 || !fstream->file ||
320
38.2k
     (size_t)ret == total_size);
321
38.2k
  return ret;
322
38.2k
}
323
324
/* returns how much of vector was used */
325
static int o_stream_fill_iovec(struct file_ostream *fstream,
326
             struct const_iovec iov[2])
327
116k
{
328
116k
  if (IS_STREAM_EMPTY(fstream))
329
78.0k
    return 0;
330
331
38.0k
  if (fstream->head < fstream->tail) {
332
37.2k
    iov[0].iov_base = fstream->buffer + fstream->head;
333
37.2k
    iov[0].iov_len = fstream->tail - fstream->head;
334
37.2k
    return 1;
335
37.2k
  } else {
336
769
    iov[0].iov_base = fstream->buffer + fstream->head;
337
769
    iov[0].iov_len = fstream->buffer_size - fstream->head;
338
769
    if (fstream->tail == 0)
339
769
      return 1;
340
0
    else {
341
0
      iov[1].iov_base = fstream->buffer;
342
0
      iov[1].iov_len = fstream->tail;
343
0
      return 2;
344
0
    }
345
769
  }
346
38.0k
}
347
348
static int buffer_flush(struct file_ostream *fstream)
349
116k
{
350
116k
  struct const_iovec iov[2];
351
116k
  int iov_len;
352
116k
  ssize_t ret;
353
354
116k
  iov_len = o_stream_fill_iovec(fstream, iov);
355
116k
  if (iov_len > 0) {
356
38.0k
    ret = o_stream_file_writev_full(fstream, iov, iov_len);
357
38.0k
    if (ret < 0)
358
0
      return -1;
359
360
38.0k
    update_buffer(fstream, ret);
361
38.0k
  }
362
363
116k
  return IS_STREAM_EMPTY(fstream) ? 1 : 0;
364
116k
}
365
366
static void o_stream_tcp_flush_via_nodelay(struct file_ostream *fstream)
367
0
{
368
0
  if (net_set_tcp_nodelay(fstream->fd, TRUE) < 0) {
369
    /* Don't bother logging errors. There are quite a lot of
370
       different errors that need to be ignored, and it differs
371
       between OSes. At least:
372
       Linux: ENOTSUP, ENOTSOCK, ENOPROTOOPT
373
       FreeBSD: EINVAL, ECONNRESET */
374
0
    fstream->no_socket_nodelay = TRUE;
375
0
  } else {
376
0
    fstream->no_delay_enabled = TRUE;
377
0
  }
378
0
}
379
380
static void o_stream_file_cork(struct ostream_private *stream, bool set)
381
157k
{
382
157k
  struct file_ostream *fstream =
383
157k
    container_of(stream, struct file_ostream, ostream);
384
157k
  struct iostream_private *iostream = &fstream->ostream.iostream;
385
157k
  int ret;
386
387
157k
  if (stream->corked != set && !stream->ostream.closed) {
388
83.5k
    if (set && fstream->io != NULL)
389
35.6k
      io_remove(&fstream->io);
390
47.8k
    else if (!set) {
391
      /* buffer flushing might close the stream */
392
41.7k
      ret = buffer_flush(fstream);
393
41.7k
      stream->last_errors_not_checked = TRUE;
394
41.7k
      if (fstream->io == NULL &&
395
41.7k
          (ret == 0 || fstream->flush_pending) &&
396
25.8k
          !stream->ostream.closed) {
397
25.8k
        fstream->io = io_add_to(
398
25.8k
          io_stream_get_ioloop(iostream),
399
25.8k
          fstream->fd, IO_WRITE,
400
25.8k
          stream_send_io, fstream);
401
25.8k
      }
402
41.7k
    }
403
83.5k
    if (stream->ostream.closed) {
404
      /* flushing may have closed the stream already */
405
0
      return;
406
0
    }
407
408
83.5k
    if (fstream->socket_cork_set) {
409
0
      i_assert(!set);
410
0
      if (net_set_cork(fstream->fd, FALSE) < 0)
411
0
        fstream->no_socket_cork = TRUE;
412
0
      fstream->socket_cork_set = FALSE;
413
0
    }
414
83.5k
    if (!set && !fstream->no_socket_nodelay &&
415
0
        !fstream->no_delay_enabled) {
416
      /* Uncorking - send all the pending data immediately. */
417
0
      o_stream_tcp_flush_via_nodelay(fstream);
418
0
    }
419
83.5k
    if (!set && !fstream->no_socket_quickack) {
420
      /* Uncorking - disable delayed ACKs to reduce latency.
421
         Note that this needs to be set repeatedly. */
422
0
      if (net_set_tcp_quickack(fstream->fd, TRUE) < 0)
423
0
        fstream->no_socket_quickack = TRUE;
424
0
    }
425
83.5k
    stream->corked = set;
426
83.5k
  }
427
157k
}
428
429
static int o_stream_file_flush(struct ostream_private *stream)
430
74.3k
{
431
74.3k
  struct file_ostream *fstream =
432
74.3k
    container_of(stream, struct file_ostream, ostream);
433
434
74.3k
  return buffer_flush(fstream);
435
74.3k
}
436
437
static void
438
o_stream_file_flush_pending(struct ostream_private *stream, bool set)
439
226k
{
440
226k
  struct file_ostream *fstream =
441
226k
    container_of(stream, struct file_ostream, ostream);
442
226k
  struct iostream_private *iostream = &fstream->ostream.iostream;
443
444
226k
  fstream->flush_pending = set;
445
226k
  if (set && !stream->corked && fstream->io == NULL) {
446
12.0k
    fstream->io = io_add_to(io_stream_get_ioloop(iostream),
447
12.0k
          fstream->fd, IO_WRITE,
448
12.0k
          stream_send_io, fstream);
449
12.0k
  }
450
226k
}
451
452
static size_t get_unused_space(const struct file_ostream *fstream)
453
245k
{
454
245k
  if (fstream->head > fstream->tail) {
455
    /* XXXT...HXXX */
456
0
    return fstream->head - fstream->tail;
457
245k
  } else if (fstream->head < fstream->tail) {
458
    /* ...HXXXT... */
459
169k
    return (fstream->buffer_size - fstream->tail) + fstream->head;
460
169k
  } else {
461
    /* either fully unused or fully used */
462
75.8k
    return fstream->full ? 0 : fstream->buffer_size;
463
75.8k
  }
464
245k
}
465
466
static size_t
467
o_stream_file_get_buffer_used_size(const struct ostream_private *stream)
468
0
{
469
0
  const struct file_ostream *fstream =
470
0
    container_of(stream, const struct file_ostream, ostream);
471
472
0
  return fstream->buffer_size - get_unused_space(fstream);
473
0
}
474
475
static int o_stream_file_seek(struct ostream_private *stream, uoff_t offset)
476
0
{
477
0
  struct file_ostream *fstream =
478
0
    container_of(stream, struct file_ostream, ostream);
479
480
0
  if (offset > OFF_T_MAX) {
481
0
    stream->ostream.stream_errno = EINVAL;
482
0
    return -1;
483
0
  }
484
0
  if (!fstream->file) {
485
0
    stream->ostream.stream_errno = ESPIPE;
486
0
    return -1;
487
0
  }
488
489
0
  if (buffer_flush(fstream) < 0)
490
0
    return -1;
491
492
0
  stream->ostream.offset = offset;
493
0
  fstream->buffer_offset = offset;
494
0
  return 1;
495
0
}
496
497
static void o_stream_grow_buffer(struct file_ostream *fstream, size_t bytes)
498
12.3k
{
499
12.3k
  size_t size, new_size, end_size;
500
501
12.3k
  size = nearest_power(fstream->buffer_size + bytes);
502
12.3k
  if (size > fstream->ostream.max_buffer_size) {
503
    /* limit the size */
504
514
    size = fstream->ostream.max_buffer_size;
505
11.8k
  } else if (fstream->ostream.corked) {
506
    /* try to use optimal buffer size with corking */
507
11.8k
    new_size = I_MIN(fstream->optimal_block_size,
508
11.8k
         fstream->ostream.max_buffer_size);
509
11.8k
    if (new_size > size)
510
11.7k
      size = new_size;
511
11.8k
  }
512
513
12.3k
  if (size <= fstream->buffer_size)
514
367
    return;
515
516
11.9k
  fstream->buffer = i_realloc(fstream->buffer,
517
11.9k
            fstream->buffer_size, size);
518
519
11.9k
  if (fstream->tail <= fstream->head && !IS_STREAM_EMPTY(fstream)) {
520
    /* move head forward to end of buffer */
521
0
    end_size = fstream->buffer_size - fstream->head;
522
0
    memmove(fstream->buffer + size - end_size,
523
0
      fstream->buffer + fstream->head, end_size);
524
0
    fstream->head = size - end_size;
525
0
  }
526
527
11.9k
  fstream->full = FALSE;
528
11.9k
  fstream->buffer_size = size;
529
11.9k
}
530
531
static void stream_send_io(struct file_ostream *fstream)
532
34.3k
{
533
34.3k
  struct ostream *ostream = &fstream->ostream.ostream;
534
34.3k
  struct iostream_private *iostream = &fstream->ostream.iostream;
535
34.3k
  bool use_cork = !fstream->ostream.corked;
536
34.3k
  int ret;
537
538
  /* Set flush_pending = FALSE first before calling the flush callback,
539
     and change it to TRUE only if callback returns 0. That way the
540
     callback can call o_stream_set_flush_pending() again and we don't
541
     forget it even if flush callback returns 1. */
542
34.3k
  fstream->flush_pending = FALSE;
543
544
34.3k
  o_stream_ref(ostream);
545
34.3k
  if (use_cork)
546
34.3k
    o_stream_cork(ostream);
547
34.3k
  if (fstream->ostream.callback != NULL)
548
34.3k
    ret = fstream->ostream.callback(fstream->ostream.context);
549
0
  else
550
0
    ret = o_stream_file_flush(&fstream->ostream);
551
34.3k
  if (use_cork)
552
34.3k
    o_stream_uncork(ostream);
553
554
34.3k
  if (ret == 0)
555
0
    fstream->flush_pending = TRUE;
556
557
34.3k
  if (!fstream->flush_pending && IS_STREAM_EMPTY(fstream)) {
558
9.81k
    io_remove(&fstream->io);
559
24.5k
  } else if (!fstream->ostream.ostream.closed) {
560
    /* Add the IO handler if it's not there already. Callback
561
       might have just returned 0 without there being any data
562
       to be sent. */
563
24.1k
    if (fstream->io == NULL) {
564
0
      fstream->io = io_add_to(io_stream_get_ioloop(iostream),
565
0
            fstream->fd, IO_WRITE,
566
0
            stream_send_io, fstream);
567
0
    }
568
24.1k
  }
569
570
34.3k
  o_stream_unref(&ostream);
571
34.3k
}
572
573
static size_t o_stream_add(struct file_ostream *fstream,
574
         const void *data, size_t size)
575
122k
{
576
122k
  struct iostream_private *iostream = &fstream->ostream.iostream;
577
122k
  size_t unused, sent;
578
122k
  int i;
579
580
122k
  unused = get_unused_space(fstream);
581
122k
  if (unused < size)
582
12.3k
    o_stream_grow_buffer(fstream, size-unused);
583
584
122k
  sent = 0;
585
244k
  for (i = 0; i < 2 && sent < size && !fstream->full; i++) {
586
122k
    unused = fstream->tail >= fstream->head ?
587
122k
      fstream->buffer_size - fstream->tail :
588
122k
      fstream->head - fstream->tail;
589
590
122k
    if (unused > size-sent)
591
121k
      unused = size-sent;
592
122k
    memcpy(fstream->buffer + fstream->tail,
593
122k
           CONST_PTR_OFFSET(data, sent), unused);
594
122k
    sent += unused;
595
596
122k
    fstream->tail += unused;
597
122k
    if (fstream->tail == fstream->buffer_size)
598
262
      fstream->tail = 0;
599
600
122k
    if (fstream->head == fstream->tail &&
601
262
        fstream->buffer_size > 0)
602
262
      fstream->full = TRUE;
603
122k
  }
604
605
122k
  if (sent != 0 && fstream->io == NULL &&
606
122k
      !fstream->ostream.corked && !fstream->file) {
607
0
    fstream->io = io_add_to(io_stream_get_ioloop(iostream),
608
0
          fstream->fd, IO_WRITE, stream_send_io,
609
0
              fstream);
610
0
  }
611
612
122k
  return sent;
613
122k
}
614
615
ssize_t o_stream_file_sendv(struct ostream_private *stream,
616
           const struct const_iovec *iov,
617
           unsigned int iov_count)
618
122k
{
619
122k
  struct file_ostream *fstream =
620
122k
    container_of(stream, struct file_ostream, ostream);
621
122k
  size_t size, total_size, added, optimal_size;
622
122k
  unsigned int i;
623
122k
  ssize_t ret = 0;
624
625
245k
  for (i = 0, size = 0; i < iov_count; i++)
626
122k
    size += iov[i].iov_len;
627
122k
  total_size = size;
628
629
122k
  if (size > get_unused_space(fstream) && !IS_STREAM_EMPTY(fstream)) {
630
257
    if (o_stream_file_flush(stream) < 0)
631
0
      return -1;
632
257
  }
633
634
122k
  optimal_size = I_MIN(fstream->optimal_block_size,
635
122k
           fstream->ostream.max_buffer_size);
636
122k
  if (IS_STREAM_EMPTY(fstream) &&
637
37.9k
      (!stream->corked || size >= optimal_size)) {
638
    /* send immediately */
639
874
    ret = o_stream_file_writev_full(fstream, iov, iov_count);
640
874
    if (ret < 0)
641
0
      return -1;
642
643
874
    size = ret;
644
1.41k
    while (size > 0 && iov_count > 0 && size >= iov[0].iov_len) {
645
537
      size -= iov[0].iov_len;
646
537
      iov++;
647
537
      iov_count--;
648
537
    }
649
650
874
    if (iov_count == 0)
651
874
      i_assert(size == 0);
652
337
    else {
653
337
      added = o_stream_add(fstream,
654
337
          CONST_PTR_OFFSET(iov[0].iov_base, size),
655
337
          iov[0].iov_len - size);
656
337
      ret += added;
657
658
337
      if (added != iov[0].iov_len - size) {
659
        /* buffer full */
660
257
        stream->ostream.offset += ret;
661
257
        return ret;
662
257
      }
663
664
80
      iov++;
665
80
      iov_count--;
666
80
    }
667
874
  }
668
669
  /* buffer it, at least partly */
670
244k
  for (i = 0; i < iov_count; i++) {
671
122k
    added = o_stream_add(fstream, iov[i].iov_base, iov[i].iov_len);
672
122k
    ret += added;
673
122k
    if (added != iov[i].iov_len)
674
257
      break;
675
122k
  }
676
122k
  stream->ostream.offset += ret;
677
122k
  i_assert((size_t)ret <= total_size);
678
122k
  i_assert((size_t)ret == total_size || !fstream->file);
679
122k
  return ret;
680
122k
}
681
682
static size_t
683
o_stream_file_update_buffer(struct file_ostream *fstream,
684
          const void *data, size_t size, size_t pos)
685
0
{
686
0
  size_t avail, copy_size;
687
688
0
  if (fstream->head < fstream->tail) {
689
    /* ...HXXXT... */
690
0
    i_assert(pos < fstream->tail);
691
0
    avail = fstream->tail - pos;
692
0
  } else {
693
    /* XXXT...HXXX */
694
0
    avail = fstream->buffer_size - pos;
695
0
  }
696
0
  copy_size = I_MIN(size, avail);
697
0
  memcpy(fstream->buffer + pos, data, copy_size);
698
0
  data = CONST_PTR_OFFSET(data, copy_size);
699
0
  size -= copy_size;
700
701
0
  if (size > 0 && fstream->head >= fstream->tail) {
702
    /* wraps to beginning of the buffer */
703
0
    copy_size = I_MIN(size, fstream->tail);
704
0
    memcpy(fstream->buffer, data, copy_size);
705
0
    size -= copy_size;
706
0
  }
707
0
  return size;
708
0
}
709
710
static int
711
o_stream_file_write_at(struct ostream_private *stream,
712
           const void *data, size_t size, uoff_t offset)
713
0
{
714
0
  struct file_ostream *fstream =
715
0
    container_of(stream, struct file_ostream, ostream);
716
0
  size_t used, pos, skip, left;
717
718
  /* update buffer if the write overlaps it */
719
0
  used = file_buffer_get_used_size(fstream);
720
0
  if (used > 0 &&
721
0
      fstream->buffer_offset < offset + size &&
722
0
      fstream->buffer_offset + used > offset) {
723
0
    if (fstream->buffer_offset <= offset) {
724
      /* updating from the beginning */
725
0
      skip = 0;
726
0
    } else {
727
0
      skip = fstream->buffer_offset - offset;
728
0
    }
729
0
    pos = (fstream->head + offset + skip - fstream->buffer_offset) %
730
0
      fstream->buffer_size;
731
0
    left = o_stream_file_update_buffer(fstream,
732
0
        CONST_PTR_OFFSET(data, skip), size - skip, pos);
733
0
    if (left > 0) {
734
      /* didn't write all of it */
735
0
      if (skip > 0) {
736
        /* we also have to write a prefix. don't
737
           bother with two syscalls, just write all
738
           of it in one pwrite(). */
739
0
      } else {
740
        /* write only the suffix */
741
0
        size_t update_count = size - left;
742
743
0
        data = CONST_PTR_OFFSET(data, update_count);
744
0
        size -= update_count;
745
0
        offset += update_count;
746
0
      }
747
0
    } else if (skip == 0) {
748
      /* everything done */
749
0
      return 0;
750
0
    } else {
751
      /* still have to write prefix */
752
0
      size = skip;
753
0
    }
754
0
  }
755
756
  /* we couldn't write everything to the buffer. flush the buffer
757
     and pwrite() the rest. */
758
0
  if (o_stream_file_flush(stream) < 0)
759
0
    return -1;
760
761
0
  if (pwrite_full(fstream->fd, data, size, offset) < 0) {
762
0
    stream->ostream.stream_errno = errno;
763
0
    stream_closed(fstream);
764
0
    return -1;
765
0
  }
766
0
  return 0;
767
0
}
768
769
static bool
770
io_stream_sendfile(struct ostream_private *outstream,
771
       struct istream *instream, int in_fd,
772
       enum ostream_send_istream_result *res_r)
773
0
{
774
0
  struct file_ostream *foutstream =
775
0
    container_of(outstream, struct file_ostream, ostream);
776
0
  uoff_t in_size, offset, send_size, v_offset, abs_start_offset;
777
0
  ssize_t ret;
778
0
  bool sendfile_not_supported = FALSE;
779
780
0
  if ((ret = i_stream_get_size(instream, TRUE, &in_size)) < 0) {
781
0
    *res_r = OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT;
782
0
    return TRUE;
783
0
  }
784
0
  if (ret == 0) {
785
    /* size unknown. we can't use sendfile(). */
786
0
    return FALSE;
787
0
  }
788
789
0
  o_stream_socket_cork(foutstream);
790
791
  /* flush out any data in buffer */
792
0
  if ((ret = buffer_flush(foutstream)) < 0) {
793
0
    *res_r = OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT;
794
0
    return TRUE;
795
0
  } else if (ret == 0) {
796
0
    *res_r = OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT;
797
0
    return TRUE;
798
0
  }
799
800
0
  if (o_stream_lseek(foutstream) < 0) {
801
0
    *res_r = OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT;
802
0
    return TRUE;
803
0
  }
804
805
0
  v_offset = instream->v_offset;
806
0
  abs_start_offset = i_stream_get_absolute_offset(instream) - v_offset;
807
0
  while (v_offset < in_size) {
808
0
    offset = abs_start_offset + v_offset;
809
0
    send_size = in_size - v_offset;
810
811
0
    ret = safe_sendfile(foutstream->fd, in_fd, &offset,
812
0
            MAX_SSIZE_T(send_size));
813
0
    if (ret <= 0) {
814
0
      if (ret == 0) {
815
        /* Unexpectedly early EOF at input */
816
0
        i_stream_seek(instream, v_offset);
817
0
        instream->eof = TRUE;
818
0
        *res_r = OSTREAM_SEND_ISTREAM_RESULT_FINISHED;
819
0
        return TRUE;
820
0
      }
821
0
      if (foutstream->file) {
822
0
        if (errno == EINTR) {
823
          /* automatically retry */
824
0
          continue;
825
0
        }
826
0
      } else {
827
0
        if (errno == EINTR || errno == EAGAIN) {
828
0
          ret = 0;
829
0
          break;
830
0
        }
831
0
      }
832
0
      if (errno == EINVAL)
833
0
        sendfile_not_supported = TRUE;
834
0
      else {
835
0
        io_stream_set_error(&outstream->iostream,
836
0
                "sendfile() failed: %m");
837
0
        outstream->ostream.stream_errno = errno;
838
        /* close only if error wasn't because
839
           sendfile() isn't supported */
840
0
        stream_closed(foutstream);
841
0
      }
842
0
      break;
843
0
    }
844
845
0
    v_offset += ret;
846
0
    foutstream->real_offset += ret;
847
0
    foutstream->buffer_offset += ret;
848
0
    outstream->ostream.offset += ret;
849
0
  }
850
851
0
  i_stream_seek(instream, v_offset);
852
0
  if (v_offset == in_size) {
853
0
    instream->eof = TRUE;
854
0
    *res_r = OSTREAM_SEND_ISTREAM_RESULT_FINISHED;
855
0
    return TRUE;
856
0
  }
857
0
  i_assert(ret <= 0);
858
0
  if (sendfile_not_supported)
859
0
    return FALSE;
860
0
  if (ret < 0)
861
0
    *res_r = OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT;
862
0
  else
863
0
    *res_r = OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT;
864
0
  return TRUE;
865
0
}
866
867
static enum ostream_send_istream_result
868
io_stream_copy_backwards(struct ostream_private *outstream,
869
       struct istream *instream, uoff_t in_size)
870
0
{
871
0
  struct file_ostream *foutstream =
872
0
    container_of(outstream, struct file_ostream, ostream);
873
0
  uoff_t in_start_offset, in_offset, in_limit, out_offset;
874
0
  const unsigned char *data;
875
0
  size_t buffer_size, size, read_size;
876
0
  ssize_t ret;
877
878
0
  i_assert(IS_STREAM_EMPTY(foutstream));
879
880
  /* figure out optimal buffer size */
881
0
  buffer_size = instream->real_stream->buffer_size;
882
0
  if (buffer_size == 0 || buffer_size > foutstream->buffer_size) {
883
0
    if (foutstream->optimal_block_size > foutstream->buffer_size) {
884
0
      o_stream_grow_buffer(foutstream,
885
0
               foutstream->optimal_block_size -
886
0
               foutstream->buffer_size);
887
0
    }
888
889
0
    buffer_size = foutstream->buffer_size;
890
0
  }
891
892
0
  in_start_offset = instream->v_offset;
893
0
  in_offset = in_limit = in_size;
894
0
  out_offset = outstream->ostream.offset + (in_offset - in_start_offset);
895
896
0
  while (in_offset > in_start_offset) {
897
0
    if (in_offset - in_start_offset <= buffer_size)
898
0
      read_size = in_offset - in_start_offset;
899
0
    else
900
0
      read_size = buffer_size;
901
0
    in_offset -= read_size;
902
0
    out_offset -= read_size;
903
904
0
    for (;;) {
905
0
      i_assert(in_offset < in_limit);
906
907
0
      i_stream_seek(instream, in_offset);
908
0
      read_size = in_limit - in_offset;
909
910
      /* FIXME: something's wrong here */
911
0
      if (i_stream_read_bytes(instream, &data, &size,
912
0
            read_size) == 0)
913
0
        i_unreached();
914
0
      if (size >= read_size) {
915
0
        size = read_size;
916
0
        if (instream->mmaped) {
917
          /* we'll have to write it through
918
             buffer or the file gets corrupted */
919
0
          i_assert(size <=
920
0
             foutstream->buffer_size);
921
0
          memcpy(foutstream->buffer, data, size);
922
0
          data = foutstream->buffer;
923
0
        }
924
0
        break;
925
0
      }
926
927
      /* buffer too large probably, try with smaller */
928
0
      read_size -= size;
929
0
      in_offset += read_size;
930
0
      out_offset += read_size;
931
0
      buffer_size -= read_size;
932
0
    }
933
0
    in_limit -= size;
934
935
0
    ret = pwrite_full(foutstream->fd, data, size, out_offset);
936
0
    if (ret < 0) {
937
      /* error */
938
0
      outstream->ostream.stream_errno = errno;
939
0
      return OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT;
940
0
    }
941
0
    i_stream_skip(instream, size);
942
0
  }
943
  /* make it visible that we're at instream's EOF */
944
0
  i_stream_seek(instream, in_size);
945
0
  instream->eof = TRUE;
946
947
0
  outstream->ostream.offset += in_size - in_start_offset;
948
0
  return OSTREAM_SEND_ISTREAM_RESULT_FINISHED;
949
0
}
950
951
static enum ostream_send_istream_result
952
io_stream_copy_same_stream(struct ostream_private *outstream,
953
         struct istream *instream)
954
0
{
955
0
  uoff_t in_size;
956
0
  off_t in_abs_offset, ret = 0;
957
958
  /* copying data within same fd. we'll have to be careful with
959
     seeks and overlapping writes. */
960
0
  if ((ret = i_stream_get_size(instream, TRUE, &in_size)) < 0)
961
0
    return OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT;
962
0
  if (ret == 0) {
963
    /* if we couldn't find out the size, it means that instream
964
       isn't a regular file_istream. we can be reasonably sure that
965
       we can copy it safely the regular way. (there's really no
966
       other possibility, other than failing completely.) */
967
0
    return io_stream_copy(&outstream->ostream, instream);
968
0
  }
969
0
  i_assert(instream->v_offset <= in_size);
970
971
0
  in_abs_offset = i_stream_get_absolute_offset(instream);
972
0
  ret = (off_t)outstream->ostream.offset - in_abs_offset;
973
0
  if (ret == 0) {
974
    /* copying data over itself. we don't really
975
       need to do that, just fake it. */
976
0
    return OSTREAM_SEND_ISTREAM_RESULT_FINISHED;
977
0
  }
978
0
  if (ret > 0 && in_size > (uoff_t)ret) {
979
    /* overlapping */
980
0
    i_assert(instream->seekable);
981
0
    return io_stream_copy_backwards(outstream, instream, in_size);
982
0
  } else {
983
    /* non-overlapping */
984
0
    return io_stream_copy(&outstream->ostream, instream);
985
0
  }
986
0
}
987
988
static enum ostream_send_istream_result
989
o_stream_file_send_istream(struct ostream_private *outstream,
990
         struct istream *instream)
991
6.33k
{
992
6.33k
  struct file_ostream *foutstream =
993
6.33k
    container_of(outstream, struct file_ostream, ostream);
994
6.33k
  bool same_stream;
995
6.33k
  int in_fd;
996
6.33k
  enum ostream_send_istream_result res;
997
998
6.33k
  in_fd = !instream->readable_fd ? -1 : i_stream_get_fd(instream);
999
6.33k
  if (!foutstream->no_sendfile && in_fd != -1 &&
1000
0
      in_fd != foutstream->fd && instream->seekable) {
1001
0
    if (io_stream_sendfile(outstream, instream, in_fd, &res))
1002
0
      return res;
1003
1004
    /* sendfile() not supported (with this fd), fallback to
1005
       regular sending. */
1006
0
    foutstream->no_sendfile = TRUE;
1007
0
  }
1008
1009
6.33k
  same_stream = i_stream_get_fd(instream) == foutstream->fd &&
1010
0
    foutstream->fd != -1;
1011
6.33k
  if (!same_stream)
1012
6.33k
    return io_stream_copy(&outstream->ostream, instream);
1013
0
  return io_stream_copy_same_stream(outstream, instream);
1014
6.33k
}
1015
1016
static void o_stream_file_switch_ioloop_to(struct ostream_private *stream,
1017
             struct ioloop *ioloop)
1018
6.09k
{
1019
6.09k
  struct file_ostream *fstream =
1020
6.09k
    container_of(stream, struct file_ostream, ostream);
1021
1022
6.09k
  if (fstream->io != NULL)
1023
0
    fstream->io = io_loop_move_io_to(ioloop, &fstream->io);
1024
6.09k
}
1025
1026
struct ostream *
1027
o_stream_create_file_common(struct file_ostream *fstream,
1028
  int fd, size_t max_buffer_size, bool autoclose_fd)
1029
12.1k
{
1030
12.1k
  struct ostream *ostream;
1031
1032
12.1k
  fstream->fd = fd;
1033
12.1k
  fstream->autoclose_fd = autoclose_fd;
1034
12.1k
  fstream->optimal_block_size = DEFAULT_OPTIMAL_BLOCK_SIZE;
1035
1036
12.1k
  fstream->ostream.iostream.close = o_stream_file_close;
1037
12.1k
  fstream->ostream.iostream.destroy = o_stream_file_destroy;
1038
1039
12.1k
  fstream->ostream.cork = o_stream_file_cork;
1040
12.1k
  fstream->ostream.flush = o_stream_file_flush;
1041
12.1k
  fstream->ostream.flush_pending = o_stream_file_flush_pending;
1042
12.1k
  fstream->ostream.get_buffer_used_size =
1043
12.1k
    o_stream_file_get_buffer_used_size;
1044
12.1k
  fstream->ostream.seek = o_stream_file_seek;
1045
12.1k
  fstream->ostream.sendv = o_stream_file_sendv;
1046
12.1k
  fstream->ostream.write_at = o_stream_file_write_at;
1047
12.1k
  fstream->ostream.send_istream = o_stream_file_send_istream;
1048
12.1k
  fstream->ostream.switch_ioloop_to = o_stream_file_switch_ioloop_to;
1049
1050
12.1k
  fstream->writev = o_stream_file_writev;
1051
1052
12.1k
  fstream->ostream.max_buffer_size = max_buffer_size;
1053
12.1k
  ostream = o_stream_create(&fstream->ostream, NULL, fd);
1054
1055
12.1k
  if (max_buffer_size == 0)
1056
0
    fstream->ostream.max_buffer_size = fstream->optimal_block_size;
1057
1058
12.1k
  return ostream;
1059
12.1k
}
1060
1061
static void fstream_init_file(struct file_ostream *fstream)
1062
0
{
1063
0
  struct stat st;
1064
1065
0
  fstream->no_sendfile = TRUE;
1066
0
  if (fstat(fstream->fd, &st) < 0)
1067
0
    return;
1068
1069
0
  if ((uoff_t)st.st_blksize > fstream->optimal_block_size) {
1070
    /* use the optimal block size, but with a reasonable limit */
1071
0
    fstream->optimal_block_size =
1072
0
      I_MIN(st.st_blksize, MAX_OPTIMAL_BLOCK_SIZE);
1073
0
  }
1074
1075
0
  if (S_ISREG(st.st_mode)) {
1076
0
    fstream->no_socket_cork = TRUE;
1077
0
    fstream->no_socket_nodelay = TRUE;
1078
0
    fstream->no_socket_quickack = TRUE;
1079
0
    fstream->file = TRUE;
1080
0
  }
1081
0
}
1082
1083
static struct ostream *
1084
o_stream_create_fd_common(int fd, struct iostream_fd *ref,
1085
        size_t max_buffer_size, bool autoclose_fd)
1086
12.1k
{
1087
12.1k
  struct file_ostream *fstream;
1088
12.1k
  struct ostream *ostream;
1089
12.1k
  off_t offset;
1090
1091
12.1k
  fstream = i_new(struct file_ostream, 1);
1092
12.1k
  if (ref != NULL) {
1093
0
    fstream->fd_ref = ref;
1094
0
    iostream_fd_ref(ref);
1095
0
  }
1096
12.1k
  ostream = o_stream_create_file_common
1097
12.1k
    (fstream, fd, max_buffer_size, autoclose_fd);
1098
1099
12.1k
  offset = lseek(fd, 0, SEEK_CUR);
1100
12.1k
  if (offset >= 0) {
1101
0
    ostream->offset = offset;
1102
0
    fstream->real_offset = offset;
1103
0
    fstream->buffer_offset = offset;
1104
0
    fstream_init_file(fstream);
1105
12.1k
  } else {
1106
12.1k
    struct ip_addr local_ip;
1107
1108
12.1k
    if (net_getsockname(fd, &local_ip, NULL) < 0) {
1109
      /* not a socket */
1110
0
      fstream->no_sendfile = TRUE;
1111
0
      fstream->no_socket_cork = TRUE;
1112
0
      fstream->no_socket_nodelay = TRUE;
1113
0
      fstream->no_socket_quickack = TRUE;
1114
12.1k
    } else if (local_ip.family == 0) {
1115
      /* UNIX domain socket */
1116
12.1k
      fstream->no_socket_cork = TRUE;
1117
12.1k
      fstream->no_socket_nodelay = TRUE;
1118
12.1k
      fstream->no_socket_quickack = TRUE;
1119
12.1k
    }
1120
12.1k
  }
1121
1122
12.1k
  return ostream;
1123
12.1k
}
1124
1125
struct ostream *
1126
o_stream_create_fd(int fd, size_t max_buffer_size)
1127
12.1k
{
1128
12.1k
  return o_stream_create_fd_common(fd, NULL, max_buffer_size, FALSE);
1129
12.1k
}
1130
1131
struct ostream *
1132
o_stream_create_fd_autoclose(int *fd, size_t max_buffer_size)
1133
0
{
1134
0
  struct ostream *ostream = o_stream_create_fd_common(*fd, NULL,
1135
0
      max_buffer_size, TRUE);
1136
0
  *fd = -1;
1137
0
  return ostream;
1138
0
}
1139
1140
struct ostream *o_stream_create_fd_ref_autoclose(struct iostream_fd *ref,
1141
             size_t max_buffer_size)
1142
0
{
1143
0
  return o_stream_create_fd_common(ref->fd, ref, max_buffer_size, TRUE);
1144
0
}
1145
1146
struct ostream *
1147
o_stream_create_fd_file(int fd, uoff_t offset, bool autoclose_fd)
1148
0
{
1149
0
  struct file_ostream *fstream;
1150
0
  struct ostream *ostream;
1151
1152
0
  if (offset == UOFF_T_MAX)
1153
0
    offset = lseek(fd, 0, SEEK_CUR);
1154
1155
0
  fstream = i_new(struct file_ostream, 1);
1156
0
  ostream = o_stream_create_file_common(fstream, fd, 0, autoclose_fd);
1157
0
  fstream_init_file(fstream);
1158
0
  fstream->real_offset = offset;
1159
0
  fstream->buffer_offset = offset;
1160
0
  ostream->blocking = fstream->file;
1161
0
  ostream->offset = offset;
1162
0
  return ostream;
1163
0
}
1164
1165
struct ostream *o_stream_create_fd_file_autoclose(int *fd, uoff_t offset)
1166
0
{
1167
0
  struct ostream *output;
1168
1169
0
  output = o_stream_create_fd_file(*fd, offset, TRUE);
1170
0
  *fd = -1;
1171
0
  return output;
1172
0
}
1173
1174
struct ostream *o_stream_create_file(const char *path, uoff_t offset, mode_t mode,
1175
             enum ostream_create_file_flags flags)
1176
0
{
1177
0
  int fd;
1178
0
  int open_flags = O_WRONLY|O_CREAT;
1179
0
  if (HAS_ANY_BITS(flags, OSTREAM_CREATE_FILE_FLAG_APPEND))
1180
0
    open_flags |= O_APPEND;
1181
0
  else
1182
0
    open_flags |= O_TRUNC;
1183
0
  if ((fd = open(path, open_flags, mode)) < 0)
1184
0
    return o_stream_create_error(errno);
1185
0
  return o_stream_create_fd_file_autoclose(&fd, offset);
1186
0
}
1187
1188
struct ostream *o_stream_create_fd_blocking(int fd)
1189
0
{
1190
0
  struct file_ostream *fstream;
1191
0
  struct ostream *ostream;
1192
1193
0
  fstream = i_new(struct file_ostream, 1);
1194
0
  ostream = o_stream_create_file_common(fstream, fd, 0, FALSE);
1195
  /* disable buffering entirely */
1196
0
  fstream->ostream.max_buffer_size = 0;
1197
0
  ostream->blocking = TRUE;
1198
0
  return ostream;
1199
0
}