Coverage Report

Created: 2026-01-10 07:11

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
0
#define DEFAULT_OPTIMAL_BLOCK_SIZE IO_BLOCK_SIZE
24
#define MAX_OPTIMAL_BLOCK_SIZE (128*1024)
25
26
#define IS_STREAM_EMPTY(fstream) \
27
0
  ((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
0
{
36
0
  io_remove(&fstream->io);
37
38
0
  bool refs_left = fstream->fd_ref != NULL &&
39
0
    iostream_fd_unref(&fstream->fd_ref);
40
0
  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
0
  fstream->fd = -1;
50
51
0
  fstream->ostream.ostream.closed = TRUE;
52
0
}
53
54
void o_stream_file_close(struct iostream_private *stream,
55
        bool close_parent ATTR_UNUSED)
56
0
{
57
0
  struct file_ostream *fstream =
58
0
    container_of(stream, struct file_ostream, ostream.iostream);
59
60
0
  stream_closed(fstream);
61
0
}
62
63
static void o_stream_file_destroy(struct iostream_private *stream)
64
0
{
65
0
  struct file_ostream *fstream =
66
0
    container_of(stream, struct file_ostream, ostream.iostream);
67
68
0
  i_free(fstream->buffer);
69
0
}
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
0
{
87
0
  size_t used;
88
89
0
  if (IS_STREAM_EMPTY(fstream) || size == 0)
90
0
    return;
91
92
0
  if (fstream->head < fstream->tail) {
93
    /* ...HXXXT... */
94
0
    used = fstream->tail - fstream->head;
95
0
    i_assert(size <= used);
96
0
    fstream->head += size;
97
0
  } else {
98
    /* XXXT...HXXX */
99
0
    used = fstream->buffer_size - fstream->head;
100
0
    if (size > used) {
101
0
      size -= used;
102
0
      i_assert(size <= fstream->tail);
103
0
      fstream->head = size;
104
0
    } else {
105
0
      fstream->head += size;
106
0
    }
107
108
0
    fstream->full = FALSE;
109
0
  }
110
111
0
  if (fstream->head == fstream->tail)
112
0
    fstream->head = fstream->tail = 0;
113
114
0
  if (fstream->head == fstream->buffer_size)
115
0
    fstream->head = 0;
116
0
}
117
118
static void o_stream_socket_cork(struct file_ostream *fstream)
119
0
{
120
0
  if (fstream->ostream.corked && !fstream->socket_cork_set) {
121
0
    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
0
  }
128
0
}
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
0
{
160
0
  const char *syscall = NULL;
161
0
  ssize_t ret;
162
0
  size_t size, sent;
163
0
  unsigned int i;
164
165
0
  if (iov_count == 1) {
166
0
    i_assert(iov->iov_len > 0);
167
168
0
    if (!fstream->file ||
169
0
        fstream->real_offset == fstream->buffer_offset) {
170
0
      syscall = "write";
171
0
      ret = write(fstream->fd, iov->iov_base, iov->iov_len);
172
0
      if (ret > 0)
173
0
        fstream->real_offset += ret;
174
0
    } 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
0
  } 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
0
  if (ret < 0) {
222
0
    i_assert(syscall != NULL);
223
0
    *error_r = t_strdup_printf("%s() failed: %m", syscall);
224
0
  }
225
0
  return ret;
226
0
}
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
0
{
233
0
  const char *error = NULL;
234
0
  ssize_t ret, ret2;
235
0
  size_t size, total_size;
236
0
  bool partial;
237
0
  unsigned int i;
238
239
0
  for (i = 0, total_size = 0; i < iov_count; i++)
240
0
    total_size += iov[i].iov_len;
241
242
0
  o_stream_socket_cork(fstream);
243
0
  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
0
  ret = fstream->writev(fstream, iov, iov_count, &error);
257
0
  partial = ret != (ssize_t)total_size;
258
259
0
  if (ret < 0) {
260
0
    i_assert(error != NULL);
261
0
    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
0
    } else if (errno == EAGAIN || errno == EINTR) {
267
      /* try again later */
268
0
      return 0;
269
0
    }
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
0
  }
275
0
  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
0
  fstream->buffer_offset += ret;
282
0
  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
0
  i_assert(ret < 0 || !fstream->file ||
320
0
     (size_t)ret == total_size);
321
0
  return ret;
322
0
}
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
0
{
328
0
  if (IS_STREAM_EMPTY(fstream))
329
0
    return 0;
330
331
0
  if (fstream->head < fstream->tail) {
332
0
    iov[0].iov_base = fstream->buffer + fstream->head;
333
0
    iov[0].iov_len = fstream->tail - fstream->head;
334
0
    return 1;
335
0
  } else {
336
0
    iov[0].iov_base = fstream->buffer + fstream->head;
337
0
    iov[0].iov_len = fstream->buffer_size - fstream->head;
338
0
    if (fstream->tail == 0)
339
0
      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
0
  }
346
0
}
347
348
static int buffer_flush(struct file_ostream *fstream)
349
0
{
350
0
  struct const_iovec iov[2];
351
0
  int iov_len;
352
0
  ssize_t ret;
353
354
0
  iov_len = o_stream_fill_iovec(fstream, iov);
355
0
  if (iov_len > 0) {
356
0
    ret = o_stream_file_writev_full(fstream, iov, iov_len);
357
0
    if (ret < 0)
358
0
      return -1;
359
360
0
    update_buffer(fstream, ret);
361
0
  }
362
363
0
  return IS_STREAM_EMPTY(fstream) ? 1 : 0;
364
0
}
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
0
{
382
0
  struct file_ostream *fstream =
383
0
    container_of(stream, struct file_ostream, ostream);
384
0
  struct iostream_private *iostream = &fstream->ostream.iostream;
385
0
  int ret;
386
387
0
  if (stream->corked != set && !stream->ostream.closed) {
388
0
    if (set && fstream->io != NULL)
389
0
      io_remove(&fstream->io);
390
0
    else if (!set) {
391
      /* buffer flushing might close the stream */
392
0
      ret = buffer_flush(fstream);
393
0
      stream->last_errors_not_checked = TRUE;
394
0
      if (fstream->io == NULL &&
395
0
          (ret == 0 || fstream->flush_pending) &&
396
0
          !stream->ostream.closed) {
397
0
        fstream->io = io_add_to(
398
0
          io_stream_get_ioloop(iostream),
399
0
          fstream->fd, IO_WRITE,
400
0
          stream_send_io, fstream);
401
0
      }
402
0
    }
403
0
    if (stream->ostream.closed) {
404
      /* flushing may have closed the stream already */
405
0
      return;
406
0
    }
407
408
0
    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
0
    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
0
    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
0
    stream->corked = set;
426
0
  }
427
0
}
428
429
static int o_stream_file_flush(struct ostream_private *stream)
430
0
{
431
0
  struct file_ostream *fstream =
432
0
    container_of(stream, struct file_ostream, ostream);
433
434
0
  return buffer_flush(fstream);
435
0
}
436
437
static void
438
o_stream_file_flush_pending(struct ostream_private *stream, bool set)
439
0
{
440
0
  struct file_ostream *fstream =
441
0
    container_of(stream, struct file_ostream, ostream);
442
0
  struct iostream_private *iostream = &fstream->ostream.iostream;
443
444
0
  fstream->flush_pending = set;
445
0
  if (set && !stream->corked && fstream->io == NULL) {
446
0
    fstream->io = io_add_to(io_stream_get_ioloop(iostream),
447
0
          fstream->fd, IO_WRITE,
448
0
          stream_send_io, fstream);
449
0
  }
450
0
}
451
452
static size_t get_unused_space(const struct file_ostream *fstream)
453
0
{
454
0
  if (fstream->head > fstream->tail) {
455
    /* XXXT...HXXX */
456
0
    return fstream->head - fstream->tail;
457
0
  } else if (fstream->head < fstream->tail) {
458
    /* ...HXXXT... */
459
0
    return (fstream->buffer_size - fstream->tail) + fstream->head;
460
0
  } else {
461
    /* either fully unused or fully used */
462
0
    return fstream->full ? 0 : fstream->buffer_size;
463
0
  }
464
0
}
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
0
{
499
0
  size_t size, new_size, end_size;
500
501
0
  size = nearest_power(fstream->buffer_size + bytes);
502
0
  if (size > fstream->ostream.max_buffer_size) {
503
    /* limit the size */
504
0
    size = fstream->ostream.max_buffer_size;
505
0
  } else if (fstream->ostream.corked) {
506
    /* try to use optimal buffer size with corking */
507
0
    new_size = I_MIN(fstream->optimal_block_size,
508
0
         fstream->ostream.max_buffer_size);
509
0
    if (new_size > size)
510
0
      size = new_size;
511
0
  }
512
513
0
  if (size <= fstream->buffer_size)
514
0
    return;
515
516
0
  fstream->buffer = i_realloc(fstream->buffer,
517
0
            fstream->buffer_size, size);
518
519
0
  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
0
  fstream->full = FALSE;
528
0
  fstream->buffer_size = size;
529
0
}
530
531
static void stream_send_io(struct file_ostream *fstream)
532
0
{
533
0
  struct ostream *ostream = &fstream->ostream.ostream;
534
0
  struct iostream_private *iostream = &fstream->ostream.iostream;
535
0
  bool use_cork = !fstream->ostream.corked;
536
0
  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
0
  fstream->flush_pending = FALSE;
543
544
0
  o_stream_ref(ostream);
545
0
  if (use_cork)
546
0
    o_stream_cork(ostream);
547
0
  if (fstream->ostream.callback != NULL)
548
0
    ret = fstream->ostream.callback(fstream->ostream.context);
549
0
  else
550
0
    ret = o_stream_file_flush(&fstream->ostream);
551
0
  if (use_cork)
552
0
    o_stream_uncork(ostream);
553
554
0
  if (ret == 0)
555
0
    fstream->flush_pending = TRUE;
556
557
0
  if (!fstream->flush_pending && IS_STREAM_EMPTY(fstream)) {
558
0
    io_remove(&fstream->io);
559
0
  } 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
0
    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
0
  }
569
570
0
  o_stream_unref(&ostream);
571
0
}
572
573
static size_t o_stream_add(struct file_ostream *fstream,
574
         const void *data, size_t size)
575
0
{
576
0
  struct iostream_private *iostream = &fstream->ostream.iostream;
577
0
  size_t unused, sent;
578
0
  int i;
579
580
0
  unused = get_unused_space(fstream);
581
0
  if (unused < size)
582
0
    o_stream_grow_buffer(fstream, size-unused);
583
584
0
  sent = 0;
585
0
  for (i = 0; i < 2 && sent < size && !fstream->full; i++) {
586
0
    unused = fstream->tail >= fstream->head ?
587
0
      fstream->buffer_size - fstream->tail :
588
0
      fstream->head - fstream->tail;
589
590
0
    if (unused > size-sent)
591
0
      unused = size-sent;
592
0
    memcpy(fstream->buffer + fstream->tail,
593
0
           CONST_PTR_OFFSET(data, sent), unused);
594
0
    sent += unused;
595
596
0
    fstream->tail += unused;
597
0
    if (fstream->tail == fstream->buffer_size)
598
0
      fstream->tail = 0;
599
600
0
    if (fstream->head == fstream->tail &&
601
0
        fstream->buffer_size > 0)
602
0
      fstream->full = TRUE;
603
0
  }
604
605
0
  if (sent != 0 && fstream->io == NULL &&
606
0
      !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
0
  return sent;
613
0
}
614
615
ssize_t o_stream_file_sendv(struct ostream_private *stream,
616
           const struct const_iovec *iov,
617
           unsigned int iov_count)
618
0
{
619
0
  struct file_ostream *fstream =
620
0
    container_of(stream, struct file_ostream, ostream);
621
0
  size_t size, total_size, added, optimal_size;
622
0
  unsigned int i;
623
0
  ssize_t ret = 0;
624
625
0
  for (i = 0, size = 0; i < iov_count; i++)
626
0
    size += iov[i].iov_len;
627
0
  total_size = size;
628
629
0
  if (size > get_unused_space(fstream) && !IS_STREAM_EMPTY(fstream)) {
630
0
    if (o_stream_file_flush(stream) < 0)
631
0
      return -1;
632
0
  }
633
634
0
  optimal_size = I_MIN(fstream->optimal_block_size,
635
0
           fstream->ostream.max_buffer_size);
636
0
  if (IS_STREAM_EMPTY(fstream) &&
637
0
      (!stream->corked || size >= optimal_size)) {
638
    /* send immediately */
639
0
    ret = o_stream_file_writev_full(fstream, iov, iov_count);
640
0
    if (ret < 0)
641
0
      return -1;
642
643
0
    size = ret;
644
0
    while (size > 0 && iov_count > 0 && size >= iov[0].iov_len) {
645
0
      size -= iov[0].iov_len;
646
0
      iov++;
647
0
      iov_count--;
648
0
    }
649
650
0
    if (iov_count == 0)
651
0
      i_assert(size == 0);
652
0
    else {
653
0
      added = o_stream_add(fstream,
654
0
          CONST_PTR_OFFSET(iov[0].iov_base, size),
655
0
          iov[0].iov_len - size);
656
0
      ret += added;
657
658
0
      if (added != iov[0].iov_len - size) {
659
        /* buffer full */
660
0
        stream->ostream.offset += ret;
661
0
        return ret;
662
0
      }
663
664
0
      iov++;
665
0
      iov_count--;
666
0
    }
667
0
  }
668
669
  /* buffer it, at least partly */
670
0
  for (i = 0; i < iov_count; i++) {
671
0
    added = o_stream_add(fstream, iov[i].iov_base, iov[i].iov_len);
672
0
    ret += added;
673
0
    if (added != iov[i].iov_len)
674
0
      break;
675
0
  }
676
0
  stream->ostream.offset += ret;
677
0
  i_assert((size_t)ret <= total_size);
678
0
  i_assert((size_t)ret == total_size || !fstream->file);
679
0
  return ret;
680
0
}
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
0
{
992
0
  struct file_ostream *foutstream =
993
0
    container_of(outstream, struct file_ostream, ostream);
994
0
  bool same_stream;
995
0
  int in_fd;
996
0
  enum ostream_send_istream_result res;
997
998
0
  in_fd = !instream->readable_fd ? -1 : i_stream_get_fd(instream);
999
0
  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
0
  same_stream = i_stream_get_fd(instream) == foutstream->fd &&
1010
0
    foutstream->fd != -1;
1011
0
  if (!same_stream)
1012
0
    return io_stream_copy(&outstream->ostream, instream);
1013
0
  return io_stream_copy_same_stream(outstream, instream);
1014
0
}
1015
1016
static void o_stream_file_switch_ioloop_to(struct ostream_private *stream,
1017
             struct ioloop *ioloop)
1018
0
{
1019
0
  struct file_ostream *fstream =
1020
0
    container_of(stream, struct file_ostream, ostream);
1021
1022
0
  if (fstream->io != NULL)
1023
0
    fstream->io = io_loop_move_io_to(ioloop, &fstream->io);
1024
0
}
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
0
{
1030
0
  struct ostream *ostream;
1031
1032
0
  fstream->fd = fd;
1033
0
  fstream->autoclose_fd = autoclose_fd;
1034
0
  fstream->optimal_block_size = DEFAULT_OPTIMAL_BLOCK_SIZE;
1035
1036
0
  fstream->ostream.iostream.close = o_stream_file_close;
1037
0
  fstream->ostream.iostream.destroy = o_stream_file_destroy;
1038
1039
0
  fstream->ostream.cork = o_stream_file_cork;
1040
0
  fstream->ostream.flush = o_stream_file_flush;
1041
0
  fstream->ostream.flush_pending = o_stream_file_flush_pending;
1042
0
  fstream->ostream.get_buffer_used_size =
1043
0
    o_stream_file_get_buffer_used_size;
1044
0
  fstream->ostream.seek = o_stream_file_seek;
1045
0
  fstream->ostream.sendv = o_stream_file_sendv;
1046
0
  fstream->ostream.write_at = o_stream_file_write_at;
1047
0
  fstream->ostream.send_istream = o_stream_file_send_istream;
1048
0
  fstream->ostream.switch_ioloop_to = o_stream_file_switch_ioloop_to;
1049
1050
0
  fstream->writev = o_stream_file_writev;
1051
1052
0
  fstream->ostream.max_buffer_size = max_buffer_size;
1053
0
  ostream = o_stream_create(&fstream->ostream, NULL, fd);
1054
1055
0
  if (max_buffer_size == 0)
1056
0
    fstream->ostream.max_buffer_size = fstream->optimal_block_size;
1057
1058
0
  return ostream;
1059
0
}
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
0
{
1087
0
  struct file_ostream *fstream;
1088
0
  struct ostream *ostream;
1089
0
  off_t offset;
1090
1091
0
  fstream = i_new(struct file_ostream, 1);
1092
0
  if (ref != NULL) {
1093
0
    fstream->fd_ref = ref;
1094
0
    iostream_fd_ref(ref);
1095
0
  }
1096
0
  ostream = o_stream_create_file_common
1097
0
    (fstream, fd, max_buffer_size, autoclose_fd);
1098
1099
0
  offset = lseek(fd, 0, SEEK_CUR);
1100
0
  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
0
  } else {
1106
0
    struct ip_addr local_ip;
1107
1108
0
    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
0
    } else if (local_ip.family == 0) {
1115
      /* UNIX domain socket */
1116
0
      fstream->no_socket_cork = TRUE;
1117
0
      fstream->no_socket_nodelay = TRUE;
1118
0
      fstream->no_socket_quickack = TRUE;
1119
0
    }
1120
0
  }
1121
1122
0
  return ostream;
1123
0
}
1124
1125
struct ostream *
1126
o_stream_create_fd(int fd, size_t max_buffer_size)
1127
0
{
1128
0
  return o_stream_create_fd_common(fd, NULL, max_buffer_size, FALSE);
1129
0
}
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
}