1 #include "network_backends.h"
2
3 #ifdef USE_FREEBSD_SENDFILE
4
5 #include "network.h"
6 #include "fdevent.h"
7 #include "log.h"
8 #include "stat_cache.h"
9
10 #include <sys/types.h>
11 #include <sys/socket.h>
12 #include <sys/stat.h>
13 #include <sys/time.h>
14 #include <sys/resource.h>
15
16 #include <netinet/in.h>
17 #include <netinet/tcp.h>
18
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <unistd.h>
22 #include <netdb.h>
23 #include <string.h>
24 #include <stdlib.h>
25
26
27 #ifndef UIO_MAXIOV
28 # if defined(__FreeBSD__) || defined(__DragonFly__)
29 /* FreeBSD 4.7, 4.9 defined it in sys/uio.h only if _KERNEL is specified */
30 # define UIO_MAXIOV 1024
31 # endif
32 #endif
33
network_write_chunkqueue_freebsdsendfile(server * srv,connection * con,int fd,chunkqueue * cq,off_t max_bytes)34 int network_write_chunkqueue_freebsdsendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) {
35 chunk *c;
36
37 for(c = cq->first; (max_bytes > 0) && (NULL != c); c = c->next) {
38 int chunk_finished = 0;
39
40 switch(c->type) {
41 case MEM_CHUNK: {
42 char * offset;
43 off_t toSend;
44 ssize_t r;
45
46 size_t num_chunks, i;
47 struct iovec chunks[UIO_MAXIOV];
48 chunk *tc;
49 size_t num_bytes = 0;
50
51 /* build writev list
52 *
53 * 1. limit: num_chunks < UIO_MAXIOV
54 * 2. limit: num_bytes < max_bytes
55 */
56 for(num_chunks = 0, tc = c; tc && tc->type == MEM_CHUNK && num_chunks < UIO_MAXIOV; num_chunks++, tc = tc->next);
57
58 for(tc = c, i = 0; i < num_chunks; tc = tc->next, i++) {
59 if (tc->mem->used == 0) {
60 chunks[i].iov_base = tc->mem->ptr;
61 chunks[i].iov_len = 0;
62 } else {
63 offset = tc->mem->ptr + tc->offset;
64 toSend = tc->mem->used - 1 - tc->offset;
65
66 chunks[i].iov_base = offset;
67
68 /* protect the return value of writev() */
69 if (toSend > max_bytes ||
70 (off_t) num_bytes + toSend > max_bytes) {
71 chunks[i].iov_len = max_bytes - num_bytes;
72
73 num_chunks = i + 1;
74 break;
75 } else {
76 chunks[i].iov_len = toSend;
77 }
78
79 num_bytes += toSend;
80 }
81 }
82
83 if ((r = writev(fd, chunks, num_chunks)) < 0) {
84 switch (errno) {
85 case EAGAIN:
86 case EINTR:
87 r = 0;
88 break;
89 case ENOTCONN:
90 case EPIPE:
91 case ECONNRESET:
92 return -2;
93 default:
94 log_error_write(srv, __FILE__, __LINE__, "ssd",
95 "writev failed:", strerror(errno), fd);
96
97 return -1;
98 }
99
100 r = 0;
101 }
102
103 /* check which chunks have been written */
104 cq->bytes_out += r;
105 max_bytes -= r;
106
107 for(i = 0, tc = c; i < num_chunks; i++, tc = tc->next) {
108 if (r >= (ssize_t)chunks[i].iov_len) {
109 /* written */
110 r -= chunks[i].iov_len;
111 tc->offset += chunks[i].iov_len;
112
113 if (chunk_finished) {
114 /* skip the chunks from further touches */
115 c = c->next;
116 } else {
117 /* chunks_written + c = c->next is done in the for()*/
118 chunk_finished = 1;
119 }
120 } else {
121 /* partially written */
122
123 tc->offset += r;
124 chunk_finished = 0;
125
126 break;
127 }
128 }
129
130 break;
131 }
132 case FILE_CHUNK: {
133 off_t offset, r;
134 off_t toSend;
135 stat_cache_entry *sce = NULL;
136
137 if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) {
138 log_error_write(srv, __FILE__, __LINE__, "sb",
139 strerror(errno), c->file.name);
140 return -1;
141 }
142
143 offset = c->file.start + c->offset;
144 toSend = c->file.length - c->offset;
145 if (toSend > max_bytes) toSend = max_bytes;
146
147 if (-1 == c->file.fd) {
148 if (-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) {
149 log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno));
150
151 return -1;
152 }
153
154 #ifdef FD_CLOEXEC
155 fcntl(c->file.fd, F_SETFD, FD_CLOEXEC);
156 #endif
157 }
158
159 r = 0;
160
161 /* FreeBSD sendfile() */
162 if (-1 == sendfile(c->file.fd, fd, offset, toSend, NULL, &r, 0)) {
163 switch(errno) {
164 case EAGAIN:
165 case EINTR:
166 /* for EAGAIN/EINTR r still contains the sent bytes */
167 break; /* try again later */
168 case EPIPE:
169 case ENOTCONN:
170 return -2;
171 default:
172 log_error_write(srv, __FILE__, __LINE__, "ssd", "sendfile: ", strerror(errno), errno);
173 return -1;
174 }
175 } else if (r == 0) {
176 /* We got an event to write but we wrote nothing
177 *
178 * - the file shrinked -> error
179 * - the remote side closed inbetween -> remote-close */
180
181 if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) {
182 /* file is gone ? */
183 return -1;
184 }
185
186 if (offset >= sce->st.st_size) {
187 /* file shrinked, close the connection */
188 return -1;
189 }
190
191 return -2;
192 }
193
194 c->offset += r;
195 cq->bytes_out += r;
196 max_bytes -= r;
197
198 if (c->offset == c->file.length) {
199 chunk_finished = 1;
200 }
201
202 break;
203 }
204 default:
205
206 log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known");
207
208 return -1;
209 }
210
211 if (!chunk_finished) {
212 /* not finished yet */
213
214 break;
215 }
216 }
217
218 return 0;
219 }
220
221 #endif
222