Line data Source code
1 : /*
2 : Unix SMB/Netbios implementation.
3 : Version 2.2.x / 3.0.x
4 : sendfile implementations.
5 : Copyright (C) Jeremy Allison 2002.
6 :
7 : This program is free software; you can redistribute it and/or modify
8 : it under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3 of the License, or
10 : (at your option) any later version.
11 : This program is distributed in the hope that it will be useful,
12 : but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : GNU General Public License for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program; if not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : /*
21 : * This file handles the OS dependent sendfile implementations.
22 : * The API is such that it returns -1 on error, else returns the
23 : * number of bytes written.
24 : */
25 :
26 : #include "includes.h"
27 : #include "system/filesys.h"
28 :
29 : #if defined(LINUX_SENDFILE_API)
30 :
31 : #include <sys/sendfile.h>
32 :
33 : #ifndef MSG_MORE
34 : #define MSG_MORE 0x8000
35 : #endif
36 :
37 0 : ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
38 : {
39 0 : size_t total=0;
40 0 : ssize_t ret = -1;
41 0 : size_t hdr_len = 0;
42 0 : int old_flags = 0;
43 0 : bool socket_flags_changed = false;
44 :
45 : /*
46 : * Send the header first.
47 : * Use MSG_MORE to cork the TCP output until sendfile is called.
48 : */
49 :
50 0 : if (header) {
51 0 : hdr_len = header->length;
52 0 : while (total < hdr_len) {
53 0 : ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE);
54 0 : if (ret == -1) {
55 0 : if (errno == EAGAIN || errno == EWOULDBLOCK) {
56 : /*
57 : * send() must complete before we can
58 : * send any other outgoing data on the
59 : * socket. Ensure socket is in blocking
60 : * mode. For SMB2 by default the socket
61 : * is in non-blocking mode.
62 : */
63 0 : old_flags = fcntl(tofd, F_GETFL, 0);
64 0 : ret = set_blocking(tofd, true);
65 0 : if (ret == -1) {
66 0 : goto out;
67 : }
68 0 : socket_flags_changed = true;
69 0 : continue;
70 : }
71 0 : goto out;
72 : }
73 0 : total += ret;
74 : }
75 : }
76 :
77 0 : total = count;
78 0 : while (total) {
79 0 : ssize_t nwritten;
80 0 : do {
81 0 : nwritten = sendfile(tofd, fromfd, &offset, total);
82 0 : } while (nwritten == -1 && errno == EINTR);
83 0 : if (nwritten == -1) {
84 0 : if (errno == EAGAIN || errno == EWOULDBLOCK) {
85 0 : if (socket_flags_changed) {
86 : /*
87 : * We're already in blocking
88 : * mode. This is an error.
89 : */
90 0 : ret = -1;
91 0 : goto out;
92 : }
93 :
94 : /*
95 : * Sendfile must complete before we can
96 : * send any other outgoing data on the socket.
97 : * Ensure socket is in blocking mode.
98 : * For SMB2 by default the socket is in
99 : * non-blocking mode.
100 : */
101 0 : old_flags = fcntl(tofd, F_GETFL, 0);
102 0 : ret = set_blocking(tofd, true);
103 0 : if (ret == -1) {
104 0 : goto out;
105 : }
106 0 : socket_flags_changed = true;
107 0 : continue;
108 : }
109 :
110 0 : if (errno == ENOSYS || errno == EINVAL) {
111 : /* Ok - we're in a world of pain here. We just sent
112 : * the header, but the sendfile failed. We have to
113 : * emulate the sendfile at an upper layer before we
114 : * disable it's use. So we do something really ugly.
115 : * We set the errno to a strange value so we can detect
116 : * this at the upper level and take care of it without
117 : * layer violation. JRA.
118 : */
119 0 : errno = EINTR; /* Normally we can never return this. */
120 : }
121 0 : ret = -1;
122 0 : goto out;
123 : }
124 0 : if (nwritten == 0) {
125 : /*
126 : * EOF, return a short read
127 : */
128 0 : ret = hdr_len + (count - total);
129 0 : goto out;
130 : }
131 0 : total -= nwritten;
132 : }
133 :
134 0 : ret = count + hdr_len;
135 :
136 0 : out:
137 :
138 0 : if (socket_flags_changed) {
139 0 : int saved_errno = errno;
140 0 : int err;
141 :
142 : /* Restore the old state of the socket. */
143 0 : err = fcntl(tofd, F_SETFL, old_flags);
144 0 : if (err == -1) {
145 0 : return -1;
146 : }
147 0 : if (ret == -1) {
148 0 : errno = saved_errno;
149 : }
150 : }
151 :
152 0 : return ret;
153 : }
154 :
155 : #elif defined(SOLARIS_SENDFILE_API)
156 :
157 : /*
158 : * Solaris sendfile code written by Pierre Belanger <belanger@pobox.com>.
159 : */
160 :
161 : #include <sys/sendfile.h>
162 :
163 : ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
164 : {
165 : int sfvcnt;
166 : size_t total, xferred;
167 : struct sendfilevec vec[2];
168 : ssize_t hdr_len = 0;
169 : int old_flags = 0;
170 : ssize_t ret = -1;
171 : bool socket_flags_changed = false;
172 :
173 : if (header) {
174 : sfvcnt = 2;
175 :
176 : vec[0].sfv_fd = SFV_FD_SELF;
177 : vec[0].sfv_flag = 0;
178 : vec[0].sfv_off = (off_t)header->data;
179 : vec[0].sfv_len = hdr_len = header->length;
180 :
181 : vec[1].sfv_fd = fromfd;
182 : vec[1].sfv_flag = 0;
183 : vec[1].sfv_off = offset;
184 : vec[1].sfv_len = count;
185 :
186 : } else {
187 : sfvcnt = 1;
188 :
189 : vec[0].sfv_fd = fromfd;
190 : vec[0].sfv_flag = 0;
191 : vec[0].sfv_off = offset;
192 : vec[0].sfv_len = count;
193 : }
194 :
195 : total = count + hdr_len;
196 :
197 : while (total) {
198 : ssize_t nwritten;
199 :
200 : /*
201 : * Although not listed in the API error returns, this is almost certainly
202 : * a slow system call and will be interrupted by a signal with EINTR. JRA.
203 : */
204 :
205 : xferred = 0;
206 :
207 : nwritten = sendfilev(tofd, vec, sfvcnt, &xferred);
208 : if (nwritten == -1 && errno == EINTR) {
209 : if (xferred == 0)
210 : continue; /* Nothing written yet. */
211 : else
212 : nwritten = xferred;
213 : }
214 :
215 : if (nwritten == -1) {
216 : if (errno == EAGAIN || errno == EWOULDBLOCK) {
217 : /*
218 : * Sendfile must complete before we can
219 : * send any other outgoing data on the socket.
220 : * Ensure socket is in blocking mode.
221 : * For SMB2 by default the socket is in
222 : * non-blocking mode.
223 : */
224 : old_flags = fcntl(tofd, F_GETFL, 0);
225 : ret = set_blocking(tofd, true);
226 : if (ret == -1) {
227 : goto out;
228 : }
229 : socket_flags_changed = true;
230 : continue;
231 : }
232 : ret = -1;
233 : goto out;
234 : }
235 : if (nwritten == 0) {
236 : ret = -1;
237 : goto out; /* I think we're at EOF here... */
238 : }
239 :
240 : /*
241 : * If this was a short (signal interrupted) write we may need
242 : * to subtract it from the header data, or null out the header
243 : * data altogether if we wrote more than vec[0].sfv_len bytes.
244 : * We move vec[1].* to vec[0].* and set sfvcnt to 1
245 : */
246 :
247 : if (sfvcnt == 2 && nwritten >= vec[0].sfv_len) {
248 : vec[1].sfv_off += nwritten - vec[0].sfv_len;
249 : vec[1].sfv_len -= nwritten - vec[0].sfv_len;
250 :
251 : /* Move vec[1].* to vec[0].* and set sfvcnt to 1 */
252 : vec[0] = vec[1];
253 : sfvcnt = 1;
254 : } else {
255 : vec[0].sfv_off += nwritten;
256 : vec[0].sfv_len -= nwritten;
257 : }
258 : total -= nwritten;
259 : }
260 : ret = count + hdr_len;
261 :
262 : out:
263 :
264 : if (socket_flags_changed) {
265 : int saved_errno;
266 : int err;
267 :
268 : if (ret == -1) {
269 : saved_errno = errno;
270 : }
271 : /* Restore the old state of the socket. */
272 : err = fcntl(tofd, F_SETFL, old_flags);
273 : if (err == -1) {
274 : return -1;
275 : }
276 : if (ret == -1) {
277 : errno = saved_errno;
278 : }
279 : }
280 :
281 : return ret;
282 : }
283 :
284 : #elif defined(HPUX_SENDFILE_API)
285 :
286 : #include <sys/socket.h>
287 : #include <sys/uio.h>
288 :
289 : ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
290 : {
291 : size_t total=0;
292 : struct iovec hdtrl[2];
293 : size_t hdr_len = 0;
294 : int old_flags = 0;
295 : ssize_t ret = -1;
296 : bool socket_flags_changed = false;
297 :
298 : if (header) {
299 : /* Set up the header/trailer iovec. */
300 : hdtrl[0].iov_base = (void *)header->data;
301 : hdtrl[0].iov_len = hdr_len = header->length;
302 : } else {
303 : hdtrl[0].iov_base = NULL;
304 : hdtrl[0].iov_len = hdr_len = 0;
305 : }
306 : hdtrl[1].iov_base = NULL;
307 : hdtrl[1].iov_len = 0;
308 :
309 : total = count;
310 : while (total + hdtrl[0].iov_len) {
311 : ssize_t nwritten;
312 :
313 : /*
314 : * HPUX guarantees that if any data was written before
315 : * a signal interrupt then sendfile returns the number of
316 : * bytes written (which may be less than requested) not -1.
317 : * nwritten includes the header data sent.
318 : */
319 :
320 : do {
321 : nwritten = sendfile(tofd, fromfd, offset, total, &hdtrl[0], 0);
322 : } while (nwritten == -1 && errno == EINTR);
323 : if (nwritten == -1) {
324 : if (errno == EAGAIN || errno == EWOULDBLOCK) {
325 : /*
326 : * Sendfile must complete before we can
327 : * send any other outgoing data on the socket.
328 : * Ensure socket is in blocking mode.
329 : * For SMB2 by default the socket is in
330 : * non-blocking mode.
331 : */
332 : old_flags = fcntl(tofd, F_GETFL, 0);
333 : ret = set_blocking(tofd, true);
334 : if (ret == -1) {
335 : goto out;
336 : }
337 : socket_flags_changed = true;
338 : continue;
339 : }
340 : ret = -1;
341 : goto out;
342 : }
343 : if (nwritten == 0) {
344 : ret = -1; /* I think we're at EOF here... */
345 : goto out;
346 : }
347 :
348 : /*
349 : * If this was a short (signal interrupted) write we may need
350 : * to subtract it from the header data, or null out the header
351 : * data altogether if we wrote more than hdtrl[0].iov_len bytes.
352 : * We change nwritten to be the number of file bytes written.
353 : */
354 :
355 : if (hdtrl[0].iov_base && hdtrl[0].iov_len) {
356 : if (nwritten >= hdtrl[0].iov_len) {
357 : nwritten -= hdtrl[0].iov_len;
358 : hdtrl[0].iov_base = NULL;
359 : hdtrl[0].iov_len = 0;
360 : } else {
361 : /* iov_base is defined as a void *... */
362 : hdtrl[0].iov_base = (void *)(((char *)hdtrl[0].iov_base) + nwritten);
363 : hdtrl[0].iov_len -= nwritten;
364 : nwritten = 0;
365 : }
366 : }
367 : total -= nwritten;
368 : offset += nwritten;
369 : }
370 : ret = count + hdr_len;
371 :
372 : out:
373 :
374 : if (socket_flags_changed) {
375 : int saved_errno;
376 : int err;
377 :
378 : if (ret == -1) {
379 : saved_errno = errno;
380 : }
381 : /* Restore the old state of the socket. */
382 : err = fcntl(tofd, F_SETFL, old_flags);
383 : if (err == -1) {
384 : return -1;
385 : }
386 : if (ret == -1) {
387 : errno = saved_errno;
388 : }
389 : }
390 :
391 : return ret;
392 : }
393 :
394 : #elif defined(FREEBSD_SENDFILE_API) || defined(DARWIN_SENDFILE_API)
395 :
396 : #include <sys/types.h>
397 : #include <sys/socket.h>
398 : #include <sys/uio.h>
399 :
400 : ssize_t sys_sendfile(int tofd, int fromfd,
401 : const DATA_BLOB *header, off_t offset, size_t count)
402 : {
403 : struct sf_hdtr sf_header = {0};
404 : struct iovec io_header = {0};
405 : int old_flags = 0;
406 :
407 : off_t nwritten;
408 : ssize_t ret = -1;
409 : bool socket_flags_changed = false;
410 :
411 : if (header) {
412 : sf_header.headers = &io_header;
413 : sf_header.hdr_cnt = 1;
414 : io_header.iov_base = header->data;
415 : io_header.iov_len = header->length;
416 : sf_header.trailers = NULL;
417 : sf_header.trl_cnt = 0;
418 : }
419 :
420 : while (count != 0) {
421 :
422 : nwritten = count;
423 : #if defined(DARWIN_SENDFILE_API)
424 : /* Darwin recycles nwritten as a value-result parameter, apart from that this
425 : sendfile implementation is quite the same as the FreeBSD one */
426 : ret = sendfile(fromfd, tofd, offset, &nwritten, &sf_header, 0);
427 : #else
428 : ret = sendfile(fromfd, tofd, offset, count, &sf_header, &nwritten, 0);
429 : #endif
430 : if (ret == -1 && errno != EINTR) {
431 : if (errno == EAGAIN || errno == EWOULDBLOCK) {
432 : /*
433 : * Sendfile must complete before we can
434 : * send any other outgoing data on the socket.
435 : * Ensure socket is in blocking mode.
436 : * For SMB2 by default the socket is in
437 : * non-blocking mode.
438 : */
439 : old_flags = fcntl(tofd, F_GETFL, 0);
440 : ret = set_blocking(tofd, true);
441 : if (ret == -1) {
442 : goto out;
443 : }
444 : socket_flags_changed = true;
445 : continue;
446 : }
447 : /* Send failed, we are toast. */
448 : ret = -1;
449 : goto out;
450 : }
451 :
452 : if (nwritten == 0) {
453 : /* EOF of offset is after EOF. */
454 : break;
455 : }
456 :
457 : if (sf_header.hdr_cnt) {
458 : if (io_header.iov_len <= nwritten) {
459 : /* Entire header was sent. */
460 : sf_header.headers = NULL;
461 : sf_header.hdr_cnt = 0;
462 : nwritten -= io_header.iov_len;
463 : } else {
464 : /* Partial header was sent. */
465 : io_header.iov_len -= nwritten;
466 : io_header.iov_base =
467 : ((uint8_t *)io_header.iov_base) + nwritten;
468 : nwritten = 0;
469 : }
470 : }
471 :
472 : offset += nwritten;
473 : count -= nwritten;
474 : }
475 :
476 : ret = nwritten;
477 :
478 : out:
479 :
480 : if (socket_flags_changed) {
481 : int saved_errno;
482 : int err;
483 :
484 : if (ret == -1) {
485 : saved_errno = errno;
486 : }
487 : /* Restore the old state of the socket. */
488 : err = fcntl(tofd, F_SETFL, old_flags);
489 : if (err == -1) {
490 : return -1;
491 : }
492 : if (ret == -1) {
493 : errno = saved_errno;
494 : }
495 : }
496 :
497 : return ret;
498 : }
499 :
500 : #elif defined(AIX_SENDFILE_API)
501 :
502 : /* BEGIN AIX SEND_FILE */
503 :
504 : /* Contributed by William Jojo <jojowil@hvcc.edu> */
505 : #include <sys/socket.h>
506 :
507 : ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
508 : {
509 : struct sf_parms hdtrl;
510 : int old_flags = 0;
511 : ssize_t ret = -1;
512 : bool socket_flags_changed = false;
513 :
514 : /* Set up the header/trailer struct params. */
515 : if (header) {
516 : hdtrl.header_data = header->data;
517 : hdtrl.header_length = header->length;
518 : } else {
519 : hdtrl.header_data = NULL;
520 : hdtrl.header_length = 0;
521 : }
522 : hdtrl.trailer_data = NULL;
523 : hdtrl.trailer_length = 0;
524 :
525 : hdtrl.file_descriptor = fromfd;
526 : hdtrl.file_offset = offset;
527 : hdtrl.file_bytes = count;
528 :
529 : while ( hdtrl.file_bytes + hdtrl.header_length ) {
530 : /*
531 : Return Value
532 :
533 : There are three possible return values from send_file:
534 :
535 : Value Description
536 :
537 : -1 an error has occurred, errno contains the error code.
538 :
539 : 0 the command has completed successfully.
540 :
541 : 1 the command was completed partially, some data has been
542 : transmitted but the command has to return for some reason,
543 : for example, the command was interrupted by signals.
544 : */
545 : do {
546 : ret = send_file(&tofd, &hdtrl, 0);
547 : } while ((ret == 1) || (ret == -1 && errno == EINTR));
548 : if ( ret == -1 ) {
549 : if (errno == EAGAIN || errno == EWOULDBLOCK) {
550 : /*
551 : * Sendfile must complete before we can
552 : * send any other outgoing data on the socket.
553 : * Ensure socket is in blocking mode.
554 : * For SMB2 by default the socket is in
555 : * non-blocking mode.
556 : */
557 : old_flags = fcntl(tofd, F_GETFL, 0);
558 : ret = set_blocking(tofd, true);
559 : if (ret == -1) {
560 : goto out;
561 : }
562 : socket_flags_changed = true;
563 : continue;
564 : }
565 : goto out;
566 : }
567 : }
568 :
569 : ret = count + header->length;
570 :
571 : out:
572 :
573 : if (socket_flags_changed) {
574 : int saved_errno;
575 : int err;
576 :
577 : if (ret == -1) {
578 : saved_errno = errno;
579 : }
580 : /* Restore the old state of the socket. */
581 : err = fcntl(tofd, F_SETFL, old_flags);
582 : if (err == -1) {
583 : return -1;
584 : }
585 : if (ret == -1) {
586 : errno = saved_errno;
587 : }
588 : }
589 :
590 : return ret;
591 : }
592 : /* END AIX SEND_FILE */
593 :
594 : #else /* No sendfile implementation. Return error. */
595 :
596 : ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
597 : {
598 : /* No sendfile syscall. */
599 : errno = ENOSYS;
600 : return -1;
601 : }
602 : #endif
|