2 Unix SMB/Netbios implementation.
4 sendfile implementations.
5 Copyright (C) Jeremy Allison 2002.
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.
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/>.
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.
27 #include "system/filesys.h"
29 #if defined(LINUX_SENDFILE_API)
31 #include <sys/sendfile.h>
34 #define MSG_MORE 0x8000
37 ssize_t
sys_sendfile(int tofd
, int fromfd
, const DATA_BLOB
*header
, off_t offset
, size_t count
)
43 bool socket_flags_changed
= false;
46 * Send the header first.
47 * Use MSG_MORE to cork the TCP output until sendfile is called.
51 hdr_len
= header
->length
;
52 while (total
< hdr_len
) {
53 ret
= sys_send(tofd
, header
->data
+ total
,hdr_len
- total
, MSG_MORE
);
55 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
) {
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.
63 old_flags
= fcntl(tofd
, F_GETFL
, 0);
64 ret
= set_blocking(tofd
, true);
68 socket_flags_changed
= true;
81 nwritten
= sendfile(tofd
, fromfd
, &offset
, total
);
82 } while (nwritten
== -1 && errno
== EINTR
);
84 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
) {
85 if (socket_flags_changed
) {
87 * We're already in blocking
88 * mode. This is an error.
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
101 old_flags
= fcntl(tofd
, F_GETFL
, 0);
102 ret
= set_blocking(tofd
, true);
106 socket_flags_changed
= true;
110 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.
119 errno
= EINTR
; /* Normally we can never return this. */
126 * EOF, return a short read
128 ret
= hdr_len
+ (count
- total
);
134 ret
= count
+ hdr_len
;
138 if (socket_flags_changed
) {
139 int saved_errno
= errno
;
142 /* Restore the old state of the socket. */
143 err
= fcntl(tofd
, F_SETFL
, old_flags
);
155 #elif defined(SOLARIS_SENDFILE_API)
158 * Solaris sendfile code written by Pierre Belanger <belanger@pobox.com>.
161 #include <sys/sendfile.h>
163 ssize_t
sys_sendfile(int tofd
, int fromfd
, const DATA_BLOB
*header
, off_t offset
, size_t count
)
166 size_t total
, xferred
;
167 struct sendfilevec vec
[2];
171 bool socket_flags_changed
= false;
176 vec
[0].sfv_fd
= SFV_FD_SELF
;
178 vec
[0].sfv_off
= (off_t
)header
->data
;
179 vec
[0].sfv_len
= hdr_len
= header
->length
;
181 vec
[1].sfv_fd
= fromfd
;
183 vec
[1].sfv_off
= offset
;
184 vec
[1].sfv_len
= count
;
189 vec
[0].sfv_fd
= fromfd
;
191 vec
[0].sfv_off
= offset
;
192 vec
[0].sfv_len
= count
;
195 total
= count
+ hdr_len
;
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.
207 nwritten
= sendfilev(tofd
, vec
, sfvcnt
, &xferred
);
208 if (nwritten
== -1 && errno
== EINTR
) {
210 continue; /* Nothing written yet. */
215 if (nwritten
== -1) {
216 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
) {
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
224 old_flags
= fcntl(tofd
, F_GETFL
, 0);
225 ret
= set_blocking(tofd
, true);
229 socket_flags_changed
= true;
237 goto out
; /* I think we're at EOF here... */
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
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
;
251 /* Move vec[1].* to vec[0].* and set sfvcnt to 1 */
255 vec
[0].sfv_off
+= nwritten
;
256 vec
[0].sfv_len
-= nwritten
;
260 ret
= count
+ hdr_len
;
264 if (socket_flags_changed
) {
271 /* Restore the old state of the socket. */
272 err
= fcntl(tofd
, F_SETFL
, old_flags
);
284 #elif defined(HPUX_SENDFILE_API)
286 #include <sys/socket.h>
289 ssize_t
sys_sendfile(int tofd
, int fromfd
, const DATA_BLOB
*header
, off_t offset
, size_t count
)
292 struct iovec hdtrl
[2];
296 bool socket_flags_changed
= false;
299 /* Set up the header/trailer iovec. */
300 hdtrl
[0].iov_base
= (void *)header
->data
;
301 hdtrl
[0].iov_len
= hdr_len
= header
->length
;
303 hdtrl
[0].iov_base
= NULL
;
304 hdtrl
[0].iov_len
= hdr_len
= 0;
306 hdtrl
[1].iov_base
= NULL
;
307 hdtrl
[1].iov_len
= 0;
310 while (total
+ hdtrl
[0].iov_len
) {
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.
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
) {
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
332 old_flags
= fcntl(tofd
, F_GETFL
, 0);
333 ret
= set_blocking(tofd
, true);
337 socket_flags_changed
= true;
344 ret
= -1; /* I think we're at EOF here... */
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.
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;
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
;
370 ret
= count
+ hdr_len
;
374 if (socket_flags_changed
) {
381 /* Restore the old state of the socket. */
382 err
= fcntl(tofd
, F_SETFL
, old_flags
);
394 #elif defined(FREEBSD_SENDFILE_API) || defined(DARWIN_SENDFILE_API)
396 #include <sys/types.h>
397 #include <sys/socket.h>
400 ssize_t
sys_sendfile(int tofd
, int fromfd
,
401 const DATA_BLOB
*header
, off_t offset
, size_t count
)
403 struct sf_hdtr sf_header
= {0};
404 struct iovec io_header
= {0};
409 bool socket_flags_changed
= false;
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;
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);
428 ret
= sendfile(fromfd
, tofd
, offset
, count
, &sf_header
, &nwritten
, 0);
430 if (ret
== -1 && errno
!= EINTR
) {
431 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
) {
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
439 old_flags
= fcntl(tofd
, F_GETFL
, 0);
440 ret
= set_blocking(tofd
, true);
444 socket_flags_changed
= true;
447 /* Send failed, we are toast. */
453 /* EOF of offset is after EOF. */
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
;
464 /* Partial header was sent. */
465 io_header
.iov_len
-= nwritten
;
467 ((uint8_t *)io_header
.iov_base
) + nwritten
;
480 if (socket_flags_changed
) {
487 /* Restore the old state of the socket. */
488 err
= fcntl(tofd
, F_SETFL
, old_flags
);
500 #elif defined(AIX_SENDFILE_API)
502 /* BEGIN AIX SEND_FILE */
504 /* Contributed by William Jojo <jojowil@hvcc.edu> */
505 #include <sys/socket.h>
507 ssize_t
sys_sendfile(int tofd
, int fromfd
, const DATA_BLOB
*header
, off_t offset
, size_t count
)
509 struct sf_parms hdtrl
;
512 bool socket_flags_changed
= false;
514 /* Set up the header/trailer struct params. */
516 hdtrl
.header_data
= header
->data
;
517 hdtrl
.header_length
= header
->length
;
519 hdtrl
.header_data
= NULL
;
520 hdtrl
.header_length
= 0;
522 hdtrl
.trailer_data
= NULL
;
523 hdtrl
.trailer_length
= 0;
525 hdtrl
.file_descriptor
= fromfd
;
526 hdtrl
.file_offset
= offset
;
527 hdtrl
.file_bytes
= count
;
529 while ( hdtrl
.file_bytes
+ hdtrl
.header_length
) {
533 There are three possible return values from send_file:
537 -1 an error has occurred, errno contains the error code.
539 0 the command has completed successfully.
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.
546 ret
= send_file(&tofd
, &hdtrl
, 0);
547 } while ((ret
== 1) || (ret
== -1 && errno
== EINTR
));
549 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
) {
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
557 old_flags
= fcntl(tofd
, F_GETFL
, 0);
558 ret
= set_blocking(tofd
, true);
562 socket_flags_changed
= true;
569 ret
= count
+ header
->length
;
573 if (socket_flags_changed
) {
580 /* Restore the old state of the socket. */
581 err
= fcntl(tofd
, F_SETFL
, old_flags
);
592 /* END AIX SEND_FILE */
594 #else /* No sendfile implementation. Return error. */
596 ssize_t
sys_sendfile(int tofd
, int fromfd
, const DATA_BLOB
*header
, off_t offset
, size_t count
)
598 /* No sendfile syscall. */