Coverage Report

Created: 2026-09-01 06:13

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) Dovecot authors, see top-level 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
  if (!IS_STREAM_EMPTY(fstream))
364
0
    return 0;
365
366
0
  if (fstream->buffer_size > fstream->optimal_block_size) {
367
0
    fstream->buffer = i_realloc(fstream->buffer,
368
0
              fstream->buffer_size,
369
0
              fstream->optimal_block_size);
370
0
    fstream->buffer_size = fstream->optimal_block_size;
371
0
  }
372
0
  return 1;
373
0
}
374
375
static void o_stream_tcp_flush_via_nodelay(struct file_ostream *fstream)
376
0
{
377
0
  if (net_set_tcp_nodelay(fstream->fd, TRUE) < 0) {
378
    /* Don't bother logging errors. There are quite a lot of
379
       different errors that need to be ignored, and it differs
380
       between OSes. At least:
381
       Linux: ENOTSUP, ENOTSOCK, ENOPROTOOPT
382
       FreeBSD: EINVAL, ECONNRESET */
383
0
    fstream->no_socket_nodelay = TRUE;
384
0
  } else {
385
0
    fstream->no_delay_enabled = TRUE;
386
0
  }
387
0
}
388
389
static void o_stream_file_cork(struct ostream_private *stream, bool set)
390
0
{
391
0
  struct file_ostream *fstream =
392
0
    container_of(stream, struct file_ostream, ostream);
393
0
  struct iostream_private *iostream = &fstream->ostream.iostream;
394
0
  int ret;
395
396
0
  if (stream->corked != set && !stream->ostream.closed) {
397
0
    if (set && fstream->io != NULL)
398
0
      io_remove(&fstream->io);
399
0
    else if (!set) {
400
      /* buffer flushing might close the stream */
401
0
      ret = buffer_flush(fstream);
402
0
      stream->last_errors_not_checked = TRUE;
403
0
      if (fstream->io == NULL &&
404
0
          (ret == 0 || fstream->flush_pending) &&
405
0
          !stream->ostream.closed) {
406
0
        fstream->io = io_add_to(
407
0
          io_stream_get_ioloop(iostream),
408
0
          fstream->fd, IO_WRITE,
409
0
          stream_send_io, fstream);
410
0
      }
411
0
    }
412
0
    if (stream->ostream.closed) {
413
      /* flushing may have closed the stream already */
414
0
      return;
415
0
    }
416
417
0
    if (fstream->socket_cork_set) {
418
0
      i_assert(!set);
419
0
      if (net_set_cork(fstream->fd, FALSE) < 0)
420
0
        fstream->no_socket_cork = TRUE;
421
0
      fstream->socket_cork_set = FALSE;
422
0
    }
423
0
    if (!set && !fstream->no_socket_nodelay &&
424
0
        !fstream->no_delay_enabled) {
425
      /* Uncorking - send all the pending data immediately. */
426
0
      o_stream_tcp_flush_via_nodelay(fstream);
427
0
    }
428
0
    if (!set && !fstream->no_socket_quickack) {
429
      /* Uncorking - disable delayed ACKs to reduce latency.
430
         Note that this needs to be set repeatedly. */
431
0
      if (net_set_tcp_quickack(fstream->fd, TRUE) < 0)
432
0
        fstream->no_socket_quickack = TRUE;
433
0
    }
434
0
    stream->corked = set;
435
0
  }
436
0
}
437
438
static int o_stream_file_flush(struct ostream_private *stream)
439
0
{
440
0
  struct file_ostream *fstream =
441
0
    container_of(stream, struct file_ostream, ostream);
442
443
0
  return buffer_flush(fstream);
444
0
}
445
446
static void
447
o_stream_file_flush_pending(struct ostream_private *stream, bool set)
448
0
{
449
0
  struct file_ostream *fstream =
450
0
    container_of(stream, struct file_ostream, ostream);
451
0
  struct iostream_private *iostream = &fstream->ostream.iostream;
452
453
0
  fstream->flush_pending = set;
454
0
  if (set && !stream->corked && fstream->io == NULL) {
455
0
    fstream->io = io_add_to(io_stream_get_ioloop(iostream),
456
0
          fstream->fd, IO_WRITE,
457
0
          stream_send_io, fstream);
458
0
  }
459
0
}
460
461
static size_t get_unused_space(const struct file_ostream *fstream)
462
0
{
463
0
  if (fstream->head > fstream->tail) {
464
    /* XXXT...HXXX */
465
0
    return fstream->head - fstream->tail;
466
0
  } else if (fstream->head < fstream->tail) {
467
    /* ...HXXXT... */
468
0
    return (fstream->buffer_size - fstream->tail) + fstream->head;
469
0
  } else {
470
    /* either fully unused or fully used */
471
0
    return fstream->full ? 0 : fstream->buffer_size;
472
0
  }
473
0
}
474
475
static size_t
476
o_stream_file_get_buffer_used_size(const struct ostream_private *stream)
477
0
{
478
0
  const struct file_ostream *fstream =
479
0
    container_of(stream, const struct file_ostream, ostream);
480
481
0
  return fstream->buffer_size - get_unused_space(fstream);
482
0
}
483
484
static int o_stream_file_seek(struct ostream_private *stream, uoff_t offset)
485
0
{
486
0
  struct file_ostream *fstream =
487
0
    container_of(stream, struct file_ostream, ostream);
488
489
0
  if (offset > OFF_T_MAX) {
490
0
    stream->ostream.stream_errno = EINVAL;
491
0
    return -1;
492
0
  }
493
0
  if (!fstream->file) {
494
0
    stream->ostream.stream_errno = ESPIPE;
495
0
    return -1;
496
0
  }
497
498
0
  if (buffer_flush(fstream) < 0)
499
0
    return -1;
500
501
0
  stream->ostream.offset = offset;
502
0
  fstream->buffer_offset = offset;
503
0
  return 1;
504
0
}
505
506
static void o_stream_grow_buffer(struct file_ostream *fstream, size_t bytes)
507
0
{
508
0
  size_t size, new_size, end_size;
509
510
0
  size = nearest_power(fstream->buffer_size + bytes);
511
0
  if (size > fstream->ostream.max_buffer_size) {
512
    /* limit the size */
513
0
    size = fstream->ostream.max_buffer_size;
514
0
  } else if (fstream->ostream.corked) {
515
    /* try to use optimal buffer size with corking */
516
0
    new_size = I_MIN(fstream->optimal_block_size,
517
0
         fstream->ostream.max_buffer_size);
518
0
    if (new_size > size)
519
0
      size = new_size;
520
0
  }
521
522
0
  if (size <= fstream->buffer_size)
523
0
    return;
524
525
0
  fstream->buffer = i_realloc(fstream->buffer,
526
0
            fstream->buffer_size, size);
527
528
0
  if (fstream->tail <= fstream->head && !IS_STREAM_EMPTY(fstream)) {
529
    /* move head forward to end of buffer */
530
0
    end_size = fstream->buffer_size - fstream->head;
531
0
    memmove(fstream->buffer + size - end_size,
532
0
      fstream->buffer + fstream->head, end_size);
533
0
    fstream->head = size - end_size;
534
0
  }
535
536
0
  fstream->full = FALSE;
537
0
  fstream->buffer_size = size;
538
0
}
539
540
static void stream_send_io(struct file_ostream *fstream)
541
0
{
542
0
  struct ostream *ostream = &fstream->ostream.ostream;
543
0
  struct iostream_private *iostream = &fstream->ostream.iostream;
544
0
  bool use_cork = !fstream->ostream.corked;
545
0
  int ret;
546
547
  /* Set flush_pending = FALSE first before calling the flush callback,
548
     and change it to TRUE only if callback returns 0. That way the
549
     callback can call o_stream_set_flush_pending() again and we don't
550
     forget it even if flush callback returns 1. */
551
0
  fstream->flush_pending = FALSE;
552
553
0
  o_stream_ref(ostream);
554
0
  if (use_cork)
555
0
    o_stream_cork(ostream);
556
0
  if (fstream->ostream.callback != NULL)
557
0
    ret = fstream->ostream.callback(fstream->ostream.context);
558
0
  else
559
0
    ret = o_stream_file_flush(&fstream->ostream);
560
0
  if (use_cork)
561
0
    o_stream_uncork(ostream);
562
563
0
  if (ret == 0)
564
0
    fstream->flush_pending = TRUE;
565
566
0
  if (!fstream->flush_pending && IS_STREAM_EMPTY(fstream)) {
567
0
    io_remove(&fstream->io);
568
0
  } else if (!fstream->ostream.ostream.closed) {
569
    /* Add the IO handler if it's not there already. Callback
570
       might have just returned 0 without there being any data
571
       to be sent. */
572
0
    if (fstream->io == NULL) {
573
0
      fstream->io = io_add_to(io_stream_get_ioloop(iostream),
574
0
            fstream->fd, IO_WRITE,
575
0
            stream_send_io, fstream);
576
0
    }
577
0
  }
578
579
0
  o_stream_unref(&ostream);
580
0
}
581
582
static size_t o_stream_add(struct file_ostream *fstream,
583
         const void *data, size_t size)
584
0
{
585
0
  struct iostream_private *iostream = &fstream->ostream.iostream;
586
0
  size_t unused, sent;
587
0
  int i;
588
589
0
  unused = get_unused_space(fstream);
590
0
  if (unused < size)
591
0
    o_stream_grow_buffer(fstream, size-unused);
592
0
  if (fstream->buffer_size == 0) {
593
    /* max_buffer_size=0 with nothing buffered so far:
594
       o_stream_grow_buffer() allocated no buffer at all.
595
       Nothing can be added. */
596
0
    return 0;
597
0
  }
598
599
0
  sent = 0;
600
0
  for (i = 0; i < 2 && sent < size && !fstream->full; i++) {
601
0
    unused = fstream->tail >= fstream->head ?
602
0
      fstream->buffer_size - fstream->tail :
603
0
      fstream->head - fstream->tail;
604
605
0
    if (unused > size-sent)
606
0
      unused = size-sent;
607
0
    memcpy(fstream->buffer + fstream->tail,
608
0
           CONST_PTR_OFFSET(data, sent), unused);
609
0
    sent += unused;
610
611
0
    fstream->tail += unused;
612
0
    if (fstream->tail == fstream->buffer_size)
613
0
      fstream->tail = 0;
614
615
0
    if (fstream->head == fstream->tail &&
616
0
        fstream->buffer_size > 0)
617
0
      fstream->full = TRUE;
618
0
  }
619
620
0
  if (sent != 0 && fstream->io == NULL &&
621
0
      !fstream->ostream.corked && !fstream->file) {
622
0
    fstream->io = io_add_to(io_stream_get_ioloop(iostream),
623
0
          fstream->fd, IO_WRITE, stream_send_io,
624
0
              fstream);
625
0
  }
626
627
0
  return sent;
628
0
}
629
630
ssize_t o_stream_file_sendv(struct ostream_private *stream,
631
           const struct const_iovec *iov,
632
           unsigned int iov_count)
633
0
{
634
0
  struct file_ostream *fstream =
635
0
    container_of(stream, struct file_ostream, ostream);
636
0
  size_t size, total_size, added, optimal_size;
637
0
  unsigned int i;
638
0
  ssize_t ret = 0;
639
640
0
  for (i = 0, size = 0; i < iov_count; i++)
641
0
    size += iov[i].iov_len;
642
0
  total_size = size;
643
644
0
  if (size > get_unused_space(fstream) && !IS_STREAM_EMPTY(fstream)) {
645
0
    if (o_stream_file_flush(stream) < 0)
646
0
      return -1;
647
0
  }
648
649
0
  optimal_size = I_MIN(fstream->optimal_block_size,
650
0
           fstream->ostream.max_buffer_size);
651
0
  if (IS_STREAM_EMPTY(fstream) &&
652
0
      (!stream->corked || size >= optimal_size)) {
653
    /* send immediately */
654
0
    ret = o_stream_file_writev_full(fstream, iov, iov_count);
655
0
    if (ret < 0)
656
0
      return -1;
657
658
0
    size = ret;
659
0
    while (size > 0 && iov_count > 0 && size >= iov[0].iov_len) {
660
0
      size -= iov[0].iov_len;
661
0
      iov++;
662
0
      iov_count--;
663
0
    }
664
665
0
    if (iov_count == 0)
666
0
      i_assert(size == 0);
667
0
    else {
668
0
      added = o_stream_add(fstream,
669
0
          CONST_PTR_OFFSET(iov[0].iov_base, size),
670
0
          iov[0].iov_len - size);
671
0
      ret += added;
672
673
0
      if (added != iov[0].iov_len - size) {
674
        /* buffer full */
675
0
        stream->ostream.offset += ret;
676
0
        return ret;
677
0
      }
678
679
0
      iov++;
680
0
      iov_count--;
681
0
    }
682
0
  }
683
684
  /* buffer it, at least partly */
685
0
  for (i = 0; i < iov_count; i++) {
686
0
    added = o_stream_add(fstream, iov[i].iov_base, iov[i].iov_len);
687
0
    ret += added;
688
0
    if (added != iov[i].iov_len)
689
0
      break;
690
0
  }
691
0
  stream->ostream.offset += ret;
692
0
  i_assert((size_t)ret <= total_size);
693
0
  i_assert((size_t)ret == total_size || !fstream->file);
694
0
  return ret;
695
0
}
696
697
static size_t
698
o_stream_file_update_buffer(struct file_ostream *fstream,
699
          const void *data, size_t size, size_t pos)
700
0
{
701
0
  size_t avail, copy_size;
702
703
0
  if (fstream->head < fstream->tail) {
704
    /* ...HXXXT... */
705
0
    i_assert(pos < fstream->tail);
706
0
    avail = fstream->tail - pos;
707
0
  } else {
708
    /* XXXT...HXXX */
709
0
    avail = fstream->buffer_size - pos;
710
0
  }
711
0
  copy_size = I_MIN(size, avail);
712
0
  memcpy(fstream->buffer + pos, data, copy_size);
713
0
  data = CONST_PTR_OFFSET(data, copy_size);
714
0
  size -= copy_size;
715
716
0
  if (size > 0 && fstream->head >= fstream->tail) {
717
    /* wraps to beginning of the buffer */
718
0
    copy_size = I_MIN(size, fstream->tail);
719
0
    memcpy(fstream->buffer, data, copy_size);
720
0
    size -= copy_size;
721
0
  }
722
0
  return size;
723
0
}
724
725
static int
726
o_stream_file_write_at(struct ostream_private *stream,
727
           const void *data, size_t size, uoff_t offset)
728
0
{
729
0
  struct file_ostream *fstream =
730
0
    container_of(stream, struct file_ostream, ostream);
731
0
  size_t used, pos, skip, left;
732
733
  /* update buffer if the write overlaps it */
734
0
  used = file_buffer_get_used_size(fstream);
735
0
  if (used > 0 &&
736
0
      fstream->buffer_offset < offset + size &&
737
0
      fstream->buffer_offset + used > offset) {
738
0
    if (fstream->buffer_offset <= offset) {
739
      /* updating from the beginning */
740
0
      skip = 0;
741
0
    } else {
742
0
      skip = fstream->buffer_offset - offset;
743
0
    }
744
0
    pos = (fstream->head + offset + skip - fstream->buffer_offset) %
745
0
      fstream->buffer_size;
746
0
    left = o_stream_file_update_buffer(fstream,
747
0
        CONST_PTR_OFFSET(data, skip), size - skip, pos);
748
0
    if (left > 0) {
749
      /* didn't write all of it */
750
0
      if (skip > 0) {
751
        /* we also have to write a prefix. don't
752
           bother with two syscalls, just write all
753
           of it in one pwrite(). */
754
0
      } else {
755
        /* write only the suffix */
756
0
        size_t update_count = size - left;
757
758
0
        data = CONST_PTR_OFFSET(data, update_count);
759
0
        size -= update_count;
760
0
        offset += update_count;
761
0
      }
762
0
    } else if (skip == 0) {
763
      /* everything done */
764
0
      return 0;
765
0
    } else {
766
      /* still have to write prefix */
767
0
      size = skip;
768
0
    }
769
0
  }
770
771
  /* we couldn't write everything to the buffer. flush the buffer
772
     and pwrite() the rest. */
773
0
  if (o_stream_file_flush(stream) < 0)
774
0
    return -1;
775
776
0
  if (pwrite_full(fstream->fd, data, size, offset) < 0) {
777
0
    stream->ostream.stream_errno = errno;
778
0
    stream_closed(fstream);
779
0
    return -1;
780
0
  }
781
0
  return 0;
782
0
}
783
784
static bool
785
io_stream_sendfile(struct ostream_private *outstream,
786
       struct istream *instream, int in_fd,
787
       enum ostream_send_istream_result *res_r)
788
0
{
789
0
  struct file_ostream *foutstream =
790
0
    container_of(outstream, struct file_ostream, ostream);
791
0
  uoff_t in_size, offset, send_size, v_offset, abs_start_offset;
792
0
  ssize_t ret;
793
0
  bool sendfile_not_supported = FALSE;
794
795
0
  if ((ret = i_stream_get_size(instream, TRUE, &in_size)) < 0) {
796
0
    *res_r = OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT;
797
0
    return TRUE;
798
0
  }
799
0
  if (ret == 0) {
800
    /* size unknown. we can't use sendfile(). */
801
0
    return FALSE;
802
0
  }
803
804
0
  o_stream_socket_cork(foutstream);
805
806
  /* flush out any data in buffer */
807
0
  if ((ret = buffer_flush(foutstream)) < 0) {
808
0
    *res_r = OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT;
809
0
    return TRUE;
810
0
  } else if (ret == 0) {
811
0
    *res_r = OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT;
812
0
    return TRUE;
813
0
  }
814
815
0
  if (o_stream_lseek(foutstream) < 0) {
816
0
    *res_r = OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT;
817
0
    return TRUE;
818
0
  }
819
820
0
  v_offset = instream->v_offset;
821
0
  abs_start_offset = i_stream_get_absolute_offset(instream) - v_offset;
822
0
  while (v_offset < in_size) {
823
0
    offset = abs_start_offset + v_offset;
824
0
    send_size = in_size - v_offset;
825
826
0
    ret = safe_sendfile(foutstream->fd, in_fd, &offset,
827
0
            MAX_SSIZE_T(send_size));
828
0
    if (ret <= 0) {
829
0
      if (ret == 0) {
830
        /* Unexpectedly early EOF at input */
831
0
        i_stream_seek(instream, v_offset);
832
0
        instream->eof = TRUE;
833
0
        *res_r = OSTREAM_SEND_ISTREAM_RESULT_FINISHED;
834
0
        return TRUE;
835
0
      }
836
0
      if (foutstream->file) {
837
0
        if (errno == EINTR) {
838
          /* automatically retry */
839
0
          continue;
840
0
        }
841
0
      } else {
842
0
        if (errno == EINTR || errno == EAGAIN) {
843
0
          ret = 0;
844
0
          break;
845
0
        }
846
0
      }
847
0
      if (errno == EINVAL)
848
0
        sendfile_not_supported = TRUE;
849
0
      else {
850
0
        io_stream_set_error(&outstream->iostream,
851
0
                "sendfile() failed: %m");
852
0
        outstream->ostream.stream_errno = errno;
853
        /* close only if error wasn't because
854
           sendfile() isn't supported */
855
0
        stream_closed(foutstream);
856
0
      }
857
0
      break;
858
0
    }
859
860
0
    v_offset += ret;
861
0
    foutstream->real_offset += ret;
862
0
    foutstream->buffer_offset += ret;
863
0
    outstream->ostream.offset += ret;
864
0
  }
865
866
0
  i_stream_seek(instream, v_offset);
867
0
  if (v_offset == in_size) {
868
0
    instream->eof = TRUE;
869
0
    *res_r = OSTREAM_SEND_ISTREAM_RESULT_FINISHED;
870
0
    return TRUE;
871
0
  }
872
0
  i_assert(ret <= 0);
873
0
  if (sendfile_not_supported)
874
0
    return FALSE;
875
0
  if (ret < 0)
876
0
    *res_r = OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT;
877
0
  else
878
0
    *res_r = OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT;
879
0
  return TRUE;
880
0
}
881
882
static enum ostream_send_istream_result
883
io_stream_copy_backwards(struct ostream_private *outstream,
884
       struct istream *instream, uoff_t in_size)
885
0
{
886
0
  struct file_ostream *foutstream =
887
0
    container_of(outstream, struct file_ostream, ostream);
888
0
  uoff_t in_start_offset, in_offset, in_limit, out_offset;
889
0
  const unsigned char *data;
890
0
  size_t buffer_size, size, read_size;
891
0
  ssize_t ret;
892
893
0
  i_assert(IS_STREAM_EMPTY(foutstream));
894
895
  /* figure out optimal buffer size */
896
0
  buffer_size = instream->real_stream->buffer_size;
897
0
  if (buffer_size == 0 || buffer_size > foutstream->buffer_size) {
898
0
    if (foutstream->optimal_block_size > foutstream->buffer_size) {
899
0
      o_stream_grow_buffer(foutstream,
900
0
               foutstream->optimal_block_size -
901
0
               foutstream->buffer_size);
902
0
    }
903
904
0
    buffer_size = foutstream->buffer_size;
905
0
  }
906
907
0
  in_start_offset = instream->v_offset;
908
0
  in_offset = in_limit = in_size;
909
0
  out_offset = outstream->ostream.offset + (in_offset - in_start_offset);
910
911
0
  while (in_offset > in_start_offset) {
912
0
    if (in_offset - in_start_offset <= buffer_size)
913
0
      read_size = in_offset - in_start_offset;
914
0
    else
915
0
      read_size = buffer_size;
916
0
    in_offset -= read_size;
917
0
    out_offset -= read_size;
918
919
0
    for (;;) {
920
0
      i_assert(in_offset < in_limit);
921
922
0
      i_stream_seek(instream, in_offset);
923
0
      read_size = in_limit - in_offset;
924
925
      /* FIXME: something's wrong here */
926
0
      if (i_stream_read_bytes(instream, &data, &size,
927
0
            read_size) == 0)
928
0
        i_unreached();
929
0
      if (size >= read_size) {
930
0
        size = read_size;
931
0
        if (instream->mmaped) {
932
          /* we'll have to write it through
933
             buffer or the file gets corrupted */
934
0
          i_assert(size <=
935
0
             foutstream->buffer_size);
936
0
          memcpy(foutstream->buffer, data, size);
937
0
          data = foutstream->buffer;
938
0
        }
939
0
        break;
940
0
      }
941
942
      /* buffer too large probably, try with smaller */
943
0
      read_size -= size;
944
0
      in_offset += read_size;
945
0
      out_offset += read_size;
946
0
      buffer_size -= read_size;
947
0
    }
948
0
    in_limit -= size;
949
950
0
    ret = pwrite_full(foutstream->fd, data, size, out_offset);
951
0
    if (ret < 0) {
952
      /* error */
953
0
      outstream->ostream.stream_errno = errno;
954
0
      return OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT;
955
0
    }
956
0
    i_stream_skip(instream, size);
957
0
  }
958
  /* make it visible that we're at instream's EOF */
959
0
  i_stream_seek(instream, in_size);
960
0
  instream->eof = TRUE;
961
962
0
  outstream->ostream.offset += in_size - in_start_offset;
963
0
  return OSTREAM_SEND_ISTREAM_RESULT_FINISHED;
964
0
}
965
966
static enum ostream_send_istream_result
967
io_stream_copy_same_stream(struct ostream_private *outstream,
968
         struct istream *instream)
969
0
{
970
0
  uoff_t in_size;
971
0
  off_t in_abs_offset, ret = 0;
972
973
  /* copying data within same fd. we'll have to be careful with
974
     seeks and overlapping writes. */
975
0
  if ((ret = i_stream_get_size(instream, TRUE, &in_size)) < 0)
976
0
    return OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT;
977
0
  if (ret == 0) {
978
    /* if we couldn't find out the size, it means that instream
979
       isn't a regular file_istream. we can be reasonably sure that
980
       we can copy it safely the regular way. (there's really no
981
       other possibility, other than failing completely.) */
982
0
    return io_stream_copy(&outstream->ostream, instream);
983
0
  }
984
0
  i_assert(instream->v_offset <= in_size);
985
986
0
  in_abs_offset = i_stream_get_absolute_offset(instream);
987
0
  ret = (off_t)outstream->ostream.offset - in_abs_offset;
988
0
  if (ret == 0) {
989
    /* copying data over itself. we don't really
990
       need to do that, just fake it. */
991
0
    return OSTREAM_SEND_ISTREAM_RESULT_FINISHED;
992
0
  }
993
0
  if (ret > 0 && in_size > (uoff_t)ret) {
994
    /* overlapping */
995
0
    i_assert(instream->seekable);
996
0
    return io_stream_copy_backwards(outstream, instream, in_size);
997
0
  } else {
998
    /* non-overlapping */
999
0
    return io_stream_copy(&outstream->ostream, instream);
1000
0
  }
1001
0
}
1002
1003
static enum ostream_send_istream_result
1004
o_stream_file_send_istream(struct ostream_private *outstream,
1005
         struct istream *instream)
1006
0
{
1007
0
  struct file_ostream *foutstream =
1008
0
    container_of(outstream, struct file_ostream, ostream);
1009
0
  bool same_stream;
1010
0
  int in_fd;
1011
0
  enum ostream_send_istream_result res;
1012
1013
0
  in_fd = !instream->readable_fd ? -1 : i_stream_get_fd(instream);
1014
0
  if (!foutstream->no_sendfile && in_fd != -1 &&
1015
0
      in_fd != foutstream->fd && instream->seekable) {
1016
0
    if (io_stream_sendfile(outstream, instream, in_fd, &res))
1017
0
      return res;
1018
1019
    /* sendfile() not supported (with this fd), fallback to
1020
       regular sending. */
1021
0
    foutstream->no_sendfile = TRUE;
1022
0
  }
1023
1024
0
  same_stream = i_stream_get_fd(instream) == foutstream->fd &&
1025
0
    foutstream->fd != -1;
1026
0
  if (!same_stream)
1027
0
    return io_stream_copy(&outstream->ostream, instream);
1028
0
  return io_stream_copy_same_stream(outstream, instream);
1029
0
}
1030
1031
static void o_stream_file_switch_ioloop_to(struct ostream_private *stream,
1032
             struct ioloop *ioloop)
1033
0
{
1034
0
  struct file_ostream *fstream =
1035
0
    container_of(stream, struct file_ostream, ostream);
1036
1037
0
  if (fstream->io != NULL)
1038
0
    fstream->io = io_loop_move_io_to(ioloop, &fstream->io);
1039
0
}
1040
1041
struct ostream *
1042
o_stream_create_file_common(struct file_ostream *fstream,
1043
  int fd, size_t max_buffer_size, bool autoclose_fd)
1044
0
{
1045
0
  struct ostream *ostream;
1046
1047
0
  fstream->fd = fd;
1048
0
  fstream->autoclose_fd = autoclose_fd;
1049
0
  fstream->optimal_block_size = DEFAULT_OPTIMAL_BLOCK_SIZE;
1050
1051
0
  fstream->ostream.iostream.close = o_stream_file_close;
1052
0
  fstream->ostream.iostream.destroy = o_stream_file_destroy;
1053
1054
0
  fstream->ostream.cork = o_stream_file_cork;
1055
0
  fstream->ostream.flush = o_stream_file_flush;
1056
0
  fstream->ostream.flush_pending = o_stream_file_flush_pending;
1057
0
  fstream->ostream.get_buffer_used_size =
1058
0
    o_stream_file_get_buffer_used_size;
1059
0
  fstream->ostream.seek = o_stream_file_seek;
1060
0
  fstream->ostream.sendv = o_stream_file_sendv;
1061
0
  fstream->ostream.write_at = o_stream_file_write_at;
1062
0
  fstream->ostream.send_istream = o_stream_file_send_istream;
1063
0
  fstream->ostream.switch_ioloop_to = o_stream_file_switch_ioloop_to;
1064
1065
0
  fstream->writev = o_stream_file_writev;
1066
1067
0
  fstream->ostream.max_buffer_size = max_buffer_size;
1068
0
  ostream = o_stream_create(&fstream->ostream, NULL, fd);
1069
1070
0
  if (max_buffer_size == 0)
1071
0
    fstream->ostream.max_buffer_size = fstream->optimal_block_size;
1072
1073
0
  return ostream;
1074
0
}
1075
1076
static void fstream_init_file(struct file_ostream *fstream)
1077
0
{
1078
0
  struct stat st;
1079
1080
0
  fstream->no_sendfile = TRUE;
1081
0
  if (fstat(fstream->fd, &st) < 0)
1082
0
    return;
1083
1084
0
  if ((uoff_t)st.st_blksize > fstream->optimal_block_size) {
1085
    /* use the optimal block size, but with a reasonable limit */
1086
0
    fstream->optimal_block_size =
1087
0
      I_MIN(st.st_blksize, MAX_OPTIMAL_BLOCK_SIZE);
1088
0
  }
1089
1090
0
  if (S_ISREG(st.st_mode)) {
1091
0
    fstream->no_socket_cork = TRUE;
1092
0
    fstream->no_socket_nodelay = TRUE;
1093
0
    fstream->no_socket_quickack = TRUE;
1094
0
    fstream->file = TRUE;
1095
0
  }
1096
0
}
1097
1098
static struct ostream *
1099
o_stream_create_fd_common(int fd, struct iostream_fd *ref,
1100
        size_t max_buffer_size, bool autoclose_fd)
1101
0
{
1102
0
  struct file_ostream *fstream;
1103
0
  struct ostream *ostream;
1104
0
  off_t offset;
1105
1106
0
  fstream = i_new(struct file_ostream, 1);
1107
0
  if (ref != NULL) {
1108
0
    fstream->fd_ref = ref;
1109
0
    iostream_fd_ref(ref);
1110
0
  }
1111
0
  ostream = o_stream_create_file_common
1112
0
    (fstream, fd, max_buffer_size, autoclose_fd);
1113
1114
0
  offset = lseek(fd, 0, SEEK_CUR);
1115
0
  if (offset >= 0) {
1116
0
    ostream->offset = offset;
1117
0
    fstream->real_offset = offset;
1118
0
    fstream->buffer_offset = offset;
1119
0
    fstream_init_file(fstream);
1120
0
  } else {
1121
0
    struct ip_addr local_ip;
1122
1123
0
    if (net_getsockname(fd, &local_ip, NULL) < 0) {
1124
      /* not a socket */
1125
0
      fstream->no_sendfile = TRUE;
1126
0
      fstream->no_socket_cork = TRUE;
1127
0
      fstream->no_socket_nodelay = TRUE;
1128
0
      fstream->no_socket_quickack = TRUE;
1129
0
    } else if (local_ip.family == 0) {
1130
      /* UNIX domain socket */
1131
0
      fstream->no_socket_cork = TRUE;
1132
0
      fstream->no_socket_nodelay = TRUE;
1133
0
      fstream->no_socket_quickack = TRUE;
1134
0
    }
1135
0
  }
1136
1137
0
  return ostream;
1138
0
}
1139
1140
struct ostream *
1141
o_stream_create_fd(int fd, size_t max_buffer_size)
1142
0
{
1143
0
  return o_stream_create_fd_common(fd, NULL, max_buffer_size, FALSE);
1144
0
}
1145
1146
struct ostream *
1147
o_stream_create_fd_autoclose(int *fd, size_t max_buffer_size)
1148
0
{
1149
0
  struct ostream *ostream = o_stream_create_fd_common(*fd, NULL,
1150
0
      max_buffer_size, TRUE);
1151
0
  *fd = -1;
1152
0
  return ostream;
1153
0
}
1154
1155
struct ostream *o_stream_create_fd_ref_autoclose(struct iostream_fd *ref,
1156
             size_t max_buffer_size)
1157
0
{
1158
0
  return o_stream_create_fd_common(ref->fd, ref, max_buffer_size, TRUE);
1159
0
}
1160
1161
struct ostream *
1162
o_stream_create_fd_file(int fd, uoff_t offset, bool autoclose_fd)
1163
0
{
1164
0
  struct file_ostream *fstream;
1165
0
  struct ostream *ostream;
1166
1167
0
  if (offset == UOFF_T_MAX)
1168
0
    offset = lseek(fd, 0, SEEK_CUR);
1169
1170
0
  fstream = i_new(struct file_ostream, 1);
1171
0
  ostream = o_stream_create_file_common(fstream, fd, 0, autoclose_fd);
1172
0
  fstream_init_file(fstream);
1173
0
  fstream->real_offset = offset;
1174
0
  fstream->buffer_offset = offset;
1175
0
  ostream->blocking = fstream->file;
1176
0
  ostream->offset = offset;
1177
0
  return ostream;
1178
0
}
1179
1180
struct ostream *o_stream_create_fd_file_autoclose(int *fd, uoff_t offset)
1181
0
{
1182
0
  struct ostream *output;
1183
1184
0
  output = o_stream_create_fd_file(*fd, offset, TRUE);
1185
0
  *fd = -1;
1186
0
  return output;
1187
0
}
1188
1189
struct ostream *o_stream_create_file(const char *path, uoff_t offset, mode_t mode,
1190
             enum ostream_create_file_flags flags)
1191
0
{
1192
0
  int fd;
1193
0
  int open_flags = O_WRONLY | O_CREAT | O_NOFOLLOW;
1194
0
  if (HAS_ANY_BITS(flags, OSTREAM_CREATE_FILE_FLAG_APPEND))
1195
0
    open_flags |= O_APPEND;
1196
0
  else
1197
0
    open_flags |= O_TRUNC;
1198
0
  if ((fd = open(path, open_flags, mode)) < 0)
1199
0
    return o_stream_create_error(errno);
1200
0
  return o_stream_create_fd_file_autoclose(&fd, offset);
1201
0
}
1202
1203
struct ostream *o_stream_create_fd_blocking(int fd)
1204
0
{
1205
0
  struct file_ostream *fstream;
1206
0
  struct ostream *ostream;
1207
1208
0
  fstream = i_new(struct file_ostream, 1);
1209
0
  ostream = o_stream_create_file_common(fstream, fd, 0, FALSE);
1210
  /* disable buffering entirely */
1211
0
  fstream->ostream.max_buffer_size = 0;
1212
0
  ostream->blocking = TRUE;
1213
0
  return ostream;
1214
0
}