Coverage Report

Created: 2023-06-29 06:59

/src/dovecot/src/lib/sendfile-util.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2
3
/* kludge a bit to remove _FILE_OFFSET_BITS definition from config.h.
4
   It's required to be able to include sys/sendfile.h with Linux. */
5
#include "config.h"
6
#undef HAVE_CONFIG_H
7
8
#ifdef HAVE_LINUX_SENDFILE
9
#  undef _FILE_OFFSET_BITS
10
#endif
11
12
#include "lib.h"
13
#include "sendfile-util.h"
14
15
#ifdef HAVE_LINUX_SENDFILE
16
17
#include <sys/sendfile.h>
18
19
ssize_t safe_sendfile(int out_fd, int in_fd, uoff_t *offset, size_t count)
20
0
{
21
  /* REMEMBER: uoff_t and off_t may not be of same size. */
22
0
  off_t safe_offset;
23
0
  ssize_t ret;
24
25
0
  i_assert(count > 0);
26
27
  /* make sure given offset fits into off_t */
28
0
  if (sizeof(off_t) * CHAR_BIT == 32) {
29
    /* 32bit off_t */
30
0
    if (*offset >= 2147483647L) {
31
0
      errno = EINVAL;
32
0
      return -1;
33
0
    }
34
0
    if (count > 2147483647L - *offset)
35
0
      count = 2147483647L - *offset;
36
0
  } else {
37
    /* they're most likely the same size. if not, fix this
38
       code later */
39
0
    i_assert(sizeof(off_t) == sizeof(uoff_t));
40
41
0
    if (*offset >= OFF_T_MAX) {
42
0
      errno = EINVAL;
43
0
      return -1;
44
0
    }
45
0
    if (count > OFF_T_MAX - *offset)
46
0
      count = OFF_T_MAX - *offset;
47
0
  }
48
49
0
  safe_offset = (off_t)*offset;
50
0
  ret = sendfile(out_fd, in_fd, &safe_offset, count);
51
  /* ret=0 : trying to read past EOF */
52
0
  *offset = (uoff_t)safe_offset;
53
0
  return ret;
54
0
}
55
56
#elif defined(HAVE_FREEBSD_SENDFILE)
57
58
#include <sys/socket.h>
59
#include <sys/uio.h>
60
61
ssize_t safe_sendfile(int out_fd, int in_fd, uoff_t *offset, size_t count)
62
{
63
  struct sf_hdtr hdtr;
64
  off_t sbytes;
65
  int ret;
66
67
  /* if count=0 is passed to sendfile(), it sends everything
68
     from in_fd until EOF. We don't want that. */
69
  i_assert(count > 0);
70
  i_assert(count <= SSIZE_T_MAX);
71
72
  i_zero(&hdtr);
73
  ret = sendfile(in_fd, out_fd, *offset, count, &hdtr, &sbytes, 0);
74
75
  *offset += sbytes;
76
77
  if (ret == 0 || (ret < 0 && errno == EAGAIN && sbytes > 0))
78
    return (ssize_t)sbytes;
79
  else {
80
    if (errno == ENOTSOCK) {
81
      /* out_fd wasn't a socket. behave as if sendfile()
82
         wasn't supported at all. */
83
      errno = EINVAL;
84
    }
85
    return -1;
86
  }
87
}
88
89
#elif defined (HAVE_SOLARIS_SENDFILE)
90
91
#include <sys/sendfile.h>
92
#include "net.h"
93
94
ssize_t safe_sendfile(int out_fd, int in_fd, uoff_t *offset, size_t count)
95
{
96
  ssize_t ret;
97
  off_t s_offset;
98
99
  i_assert(count > 0);
100
  i_assert(count <= SSIZE_T_MAX);
101
102
  /* NOTE: if outfd is not a socket, some Solaris versions will
103
     kernel panic */
104
105
  s_offset = (off_t)*offset;
106
  ret = sendfile(out_fd, in_fd, &s_offset, count);
107
108
  if (ret < 0) {
109
    /* if remote is gone, EPIPE is returned */
110
    if (errno == EINVAL) {
111
      /* most likely trying to read past EOF */
112
      ret = 0;
113
    } else if (errno == EAFNOSUPPORT || errno == EOPNOTSUPP) {
114
      /* not supported, return Linux-like EINVAL so caller
115
         sees only consistent errnos. */
116
      errno = EINVAL;
117
    } else if (s_offset != (off_t)*offset) {
118
      /* some data was sent, return it */
119
      i_assert(s_offset > (off_t)*offset);
120
      ret = s_offset - (off_t)*offset;
121
    }
122
  }
123
  *offset = (uoff_t)s_offset;
124
  i_assert(ret < 0 || (size_t)ret <= count);
125
  return ret;
126
}
127
128
#else
129
ssize_t safe_sendfile(int out_fd ATTR_UNUSED, int in_fd ATTR_UNUSED,
130
          uoff_t *offset ATTR_UNUSED,
131
          size_t count ATTR_UNUSED)
132
{
133
  errno = EINVAL;
134
  return -1;
135
}
136
137
#endif