2002-09-12 06:12:52 +04:00
/*
Unix SMB / Netbios implementation .
Version 2.2 . x / 3.0 . x
sendfile implementations .
Copyright ( C ) Jeremy Allison 2002.
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
2007-07-09 23:25:36 +04:00
the Free Software Foundation ; either version 3 of the License , or
2002-09-12 06:12:52 +04:00
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
2007-07-10 09:23:25 +04:00
along with this program ; if not , see < http : //www.gnu.org/licenses/>.
2002-09-12 06:12:52 +04:00
*/
/*
* This file handles the OS dependent sendfile implementations .
* The API is such that it returns - 1 on error , else returns the
* number of bytes written .
*/
# include "includes.h"
2018-07-18 23:32:49 +03:00
# include "system/filesys.h"
2002-09-12 06:12:52 +04:00
# if defined(LINUX_SENDFILE_API)
# include <sys/sendfile.h>
# ifndef MSG_MORE
# define MSG_MORE 0x8000
# endif
2012-04-05 08:53:08 +04:00
ssize_t sys_sendfile ( int tofd , int fromfd , const DATA_BLOB * header , off_t offset , size_t count )
2002-09-12 06:12:52 +04:00
{
size_t total = 0 ;
2018-07-18 23:32:49 +03:00
ssize_t ret = - 1 ;
2003-01-24 19:34:54 +03:00
size_t hdr_len = 0 ;
2018-07-18 23:32:49 +03:00
int old_flags = 0 ;
bool socket_flags_changed = false ;
2002-09-12 06:12:52 +04:00
/*
* Send the header first .
* Use MSG_MORE to cork the TCP output until sendfile is called .
*/
2002-09-12 10:24:41 +04:00
if ( header ) {
hdr_len = header - > length ;
2002-09-17 23:48:19 +04:00
while ( total < hdr_len ) {
2002-09-12 22:18:35 +04:00
ret = sys_send ( tofd , header - > data + total , hdr_len - total , MSG_MORE ) ;
2018-07-18 23:32:49 +03:00
if ( ret = = - 1 ) {
if ( errno = = EAGAIN | | errno = = EWOULDBLOCK ) {
/*
* send ( ) must complete before we can
* send any other outgoing data on the
* socket . Ensure socket is in blocking
* mode . For SMB2 by default the socket
* is in non - blocking mode .
*/
old_flags = fcntl ( tofd , F_GETFL , 0 ) ;
ret = set_blocking ( tofd , true ) ;
if ( ret = = - 1 ) {
goto out ;
}
socket_flags_changed = true ;
continue ;
}
goto out ;
}
2002-09-12 10:24:41 +04:00
total + = ret ;
}
2002-09-12 06:12:52 +04:00
}
total = count ;
while ( total ) {
ssize_t nwritten ;
do {
2002-09-12 22:18:35 +04:00
nwritten = sendfile ( tofd , fromfd , & offset , total ) ;
2018-07-18 23:32:49 +03:00
} while ( nwritten = = - 1 & & errno = = EINTR ) ;
2004-11-25 01:05:59 +03:00
if ( nwritten = = - 1 ) {
2018-07-18 23:32:49 +03:00
if ( errno = = EAGAIN | | errno = = EWOULDBLOCK ) {
if ( socket_flags_changed ) {
/*
* We ' re already in blocking
* mode . This is an error .
*/
ret = - 1 ;
goto out ;
}
/*
* Sendfile must complete before we can
* send any other outgoing data on the socket .
* Ensure socket is in blocking mode .
* For SMB2 by default the socket is in
* non - blocking mode .
*/
old_flags = fcntl ( tofd , F_GETFL , 0 ) ;
ret = set_blocking ( tofd , true ) ;
if ( ret = = - 1 ) {
goto out ;
}
socket_flags_changed = true ;
continue ;
}
2009-01-13 22:19:11 +03:00
if ( errno = = ENOSYS | | errno = = EINVAL ) {
2004-11-25 01:05:59 +03:00
/* Ok - we're in a world of pain here. We just sent
* the header , but the sendfile failed . We have to
* emulate the sendfile at an upper layer before we
* disable it ' s use . So we do something really ugly .
* We set the errno to a strange value so we can detect
* this at the upper level and take care of it without
* layer violation . JRA .
*/
errno = EINTR ; /* Normally we can never return this. */
}
2018-07-18 23:32:49 +03:00
ret = - 1 ;
goto out ;
2004-11-25 01:05:59 +03:00
}
2009-01-14 00:02:44 +03:00
if ( nwritten = = 0 ) {
/*
* EOF , return a short read
*/
2018-07-18 23:32:49 +03:00
ret = hdr_len + ( count - total ) ;
goto out ;
2009-01-14 00:02:44 +03:00
}
2002-09-12 06:12:52 +04:00
total - = nwritten ;
}
2018-07-18 23:32:49 +03:00
ret = count + hdr_len ;
out :
if ( socket_flags_changed ) {
2021-07-23 09:46:51 +03:00
int saved_errno = errno ;
2018-07-18 23:32:49 +03:00
int err ;
/* Restore the old state of the socket. */
err = fcntl ( tofd , F_SETFL , old_flags ) ;
if ( err = = - 1 ) {
return - 1 ;
}
if ( ret = = - 1 ) {
errno = saved_errno ;
}
}
return ret ;
2002-09-12 06:12:52 +04:00
}
# elif defined(SOLARIS_SENDFILE_API)
2002-10-01 01:17:18 +04:00
/*
2002-10-01 09:54:40 +04:00
* Solaris sendfile code written by Pierre Belanger < belanger @ pobox . com > .
2002-10-01 01:17:18 +04:00
*/
# include <sys/sendfile.h>
2012-04-05 08:53:08 +04:00
ssize_t sys_sendfile ( int tofd , int fromfd , const DATA_BLOB * header , off_t offset , size_t count )
2002-09-12 06:12:52 +04:00
{
2002-10-01 01:17:18 +04:00
int sfvcnt ;
size_t total , xferred ;
struct sendfilevec vec [ 2 ] ;
ssize_t hdr_len = 0 ;
2018-07-19 01:29:37 +03:00
int old_flags = 0 ;
ssize_t ret = - 1 ;
bool socket_flags_changed = false ;
2002-10-01 01:17:18 +04:00
if ( header ) {
sfvcnt = 2 ;
vec [ 0 ] . sfv_fd = SFV_FD_SELF ;
vec [ 0 ] . sfv_flag = 0 ;
2003-11-02 16:59:37 +03:00
vec [ 0 ] . sfv_off = ( off_t ) header - > data ;
2002-10-01 09:54:40 +04:00
vec [ 0 ] . sfv_len = hdr_len = header - > length ;
2002-10-01 01:17:18 +04:00
vec [ 1 ] . sfv_fd = fromfd ;
vec [ 1 ] . sfv_flag = 0 ;
vec [ 1 ] . sfv_off = offset ;
vec [ 1 ] . sfv_len = count ;
} else {
sfvcnt = 1 ;
vec [ 0 ] . sfv_fd = fromfd ;
vec [ 0 ] . sfv_flag = 0 ;
vec [ 0 ] . sfv_off = offset ;
vec [ 0 ] . sfv_len = count ;
}
total = count + hdr_len ;
while ( total ) {
ssize_t nwritten ;
/*
* Although not listed in the API error returns , this is almost certainly
* a slow system call and will be interrupted by a signal with EINTR . JRA .
*/
xferred = 0 ;
nwritten = sendfilev ( tofd , vec , sfvcnt , & xferred ) ;
2018-07-19 01:29:37 +03:00
if ( nwritten = = - 1 & & errno = = EINTR ) {
2002-10-01 01:17:18 +04:00
if ( xferred = = 0 )
continue ; /* Nothing written yet. */
else
nwritten = xferred ;
}
2018-07-19 01:29:37 +03:00
if ( nwritten = = - 1 ) {
if ( errno = = EAGAIN | | errno = = EWOULDBLOCK ) {
/*
* Sendfile must complete before we can
* send any other outgoing data on the socket .
* Ensure socket is in blocking mode .
* For SMB2 by default the socket is in
* non - blocking mode .
*/
old_flags = fcntl ( tofd , F_GETFL , 0 ) ;
ret = set_blocking ( tofd , true ) ;
if ( ret = = - 1 ) {
goto out ;
}
socket_flags_changed = true ;
continue ;
}
ret = - 1 ;
goto out ;
}
if ( nwritten = = 0 ) {
ret = - 1 ;
goto out ; /* I think we're at EOF here... */
}
2002-10-01 01:17:18 +04:00
/*
* If this was a short ( signal interrupted ) write we may need
* to subtract it from the header data , or null out the header
* data altogether if we wrote more than vec [ 0 ] . sfv_len bytes .
* We move vec [ 1 ] . * to vec [ 0 ] . * and set sfvcnt to 1
*/
if ( sfvcnt = = 2 & & nwritten > = vec [ 0 ] . sfv_len ) {
vec [ 1 ] . sfv_off + = nwritten - vec [ 0 ] . sfv_len ;
vec [ 1 ] . sfv_len - = nwritten - vec [ 0 ] . sfv_len ;
/* Move vec[1].* to vec[0].* and set sfvcnt to 1 */
vec [ 0 ] = vec [ 1 ] ;
sfvcnt = 1 ;
} else {
vec [ 0 ] . sfv_off + = nwritten ;
vec [ 0 ] . sfv_len - = nwritten ;
}
total - = nwritten ;
}
2018-07-19 01:29:37 +03:00
ret = count + hdr_len ;
out :
if ( socket_flags_changed ) {
int saved_errno ;
int err ;
if ( ret = = - 1 ) {
saved_errno = errno ;
}
/* Restore the old state of the socket. */
err = fcntl ( tofd , F_SETFL , old_flags ) ;
if ( err = = - 1 ) {
return - 1 ;
}
if ( ret = = - 1 ) {
errno = saved_errno ;
}
}
return ret ;
2002-09-12 06:12:52 +04:00
}
# elif defined(HPUX_SENDFILE_API)
# include <sys/socket.h>
# include <sys/uio.h>
2012-04-05 08:53:08 +04:00
ssize_t sys_sendfile ( int tofd , int fromfd , const DATA_BLOB * header , off_t offset , size_t count )
2002-09-12 06:12:52 +04:00
{
size_t total = 0 ;
struct iovec hdtrl [ 2 ] ;
2002-09-12 10:24:41 +04:00
size_t hdr_len = 0 ;
2018-07-19 01:36:47 +03:00
int old_flags = 0 ;
ssize_t ret = - 1 ;
bool socket_flags_changed = false ;
2002-09-12 10:24:41 +04:00
if ( header ) {
/* Set up the header/trailer iovec. */
2009-05-12 22:45:37 +04:00
hdtrl [ 0 ] . iov_base = ( void * ) header - > data ;
2002-09-12 10:24:41 +04:00
hdtrl [ 0 ] . iov_len = hdr_len = header - > length ;
} else {
hdtrl [ 0 ] . iov_base = NULL ;
hdtrl [ 0 ] . iov_len = hdr_len = 0 ;
}
2002-09-12 06:12:52 +04:00
hdtrl [ 1 ] . iov_base = NULL ;
2004-12-21 01:33:37 +03:00
hdtrl [ 1 ] . iov_len = 0 ;
2002-09-12 06:12:52 +04:00
total = count ;
2002-09-12 10:24:41 +04:00
while ( total + hdtrl [ 0 ] . iov_len ) {
2002-09-12 06:12:52 +04:00
ssize_t nwritten ;
2002-09-12 10:24:41 +04:00
/*
* HPUX guarantees that if any data was written before
* a signal interrupt then sendfile returns the number of
* bytes written ( which may be less than requested ) not - 1.
* nwritten includes the header data sent .
*/
2002-09-12 06:12:52 +04:00
do {
2002-09-12 22:18:35 +04:00
nwritten = sendfile ( tofd , fromfd , offset , total , & hdtrl [ 0 ] , 0 ) ;
2018-07-19 01:36:47 +03:00
} while ( nwritten = = - 1 & & errno = = EINTR ) ;
if ( nwritten = = - 1 ) {
if ( errno = = EAGAIN | | errno = = EWOULDBLOCK ) {
/*
* Sendfile must complete before we can
* send any other outgoing data on the socket .
* Ensure socket is in blocking mode .
* For SMB2 by default the socket is in
* non - blocking mode .
*/
old_flags = fcntl ( tofd , F_GETFL , 0 ) ;
ret = set_blocking ( tofd , true ) ;
if ( ret = = - 1 ) {
goto out ;
}
socket_flags_changed = true ;
continue ;
}
ret = - 1 ;
goto out ;
}
if ( nwritten = = 0 ) {
ret = - 1 ; /* I think we're at EOF here... */
goto out ;
}
2002-09-12 10:24:41 +04:00
/*
* If this was a short ( signal interrupted ) write we may need
* to subtract it from the header data , or null out the header
* data altogether if we wrote more than hdtrl [ 0 ] . iov_len bytes .
* We change nwritten to be the number of file bytes written .
*/
if ( hdtrl [ 0 ] . iov_base & & hdtrl [ 0 ] . iov_len ) {
if ( nwritten > = hdtrl [ 0 ] . iov_len ) {
nwritten - = hdtrl [ 0 ] . iov_len ;
hdtrl [ 0 ] . iov_base = NULL ;
hdtrl [ 0 ] . iov_len = 0 ;
} else {
2002-09-19 00:07:56 +04:00
/* iov_base is defined as a void *... */
2009-05-12 22:45:37 +04:00
hdtrl [ 0 ] . iov_base = ( void * ) ( ( ( char * ) hdtrl [ 0 ] . iov_base ) + nwritten ) ;
2002-09-12 10:24:41 +04:00
hdtrl [ 0 ] . iov_len - = nwritten ;
2002-09-30 09:24:12 +04:00
nwritten = 0 ;
2002-09-12 10:24:41 +04:00
}
}
2002-09-12 06:12:52 +04:00
total - = nwritten ;
2002-09-12 10:24:41 +04:00
offset + = nwritten ;
2002-09-12 06:12:52 +04:00
}
2018-07-19 01:36:47 +03:00
ret = count + hdr_len ;
out :
if ( socket_flags_changed ) {
int saved_errno ;
int err ;
if ( ret = = - 1 ) {
saved_errno = errno ;
}
/* Restore the old state of the socket. */
err = fcntl ( tofd , F_SETFL , old_flags ) ;
if ( err = = - 1 ) {
return - 1 ;
}
if ( ret = = - 1 ) {
errno = saved_errno ;
}
}
return ret ;
2002-09-12 06:12:52 +04:00
}
2012-06-29 22:16:18 +04:00
# elif defined(FREEBSD_SENDFILE_API) || defined(DARWIN_SENDFILE_API)
2002-09-12 06:12:52 +04:00
# include <sys/types.h>
# include <sys/socket.h>
# include <sys/uio.h>
2012-06-29 22:16:18 +04:00
ssize_t sys_sendfile ( int tofd , int fromfd ,
const DATA_BLOB * header , off_t offset , size_t count )
2002-09-12 06:12:52 +04:00
{
2012-06-29 22:16:18 +04:00
struct sf_hdtr sf_header = { 0 } ;
struct iovec io_header = { 0 } ;
2018-07-19 01:44:34 +03:00
int old_flags = 0 ;
2002-09-12 06:12:52 +04:00
2012-06-29 22:16:18 +04:00
off_t nwritten ;
2018-07-19 01:44:34 +03:00
ssize_t ret = - 1 ;
bool socket_flags_changed = false ;
2002-09-12 06:12:52 +04:00
2002-09-12 22:18:35 +04:00
if ( header ) {
2012-06-29 22:16:18 +04:00
sf_header . headers = & io_header ;
sf_header . hdr_cnt = 1 ;
io_header . iov_base = header - > data ;
io_header . iov_len = header - > length ;
sf_header . trailers = NULL ;
sf_header . trl_cnt = 0 ;
2002-09-12 22:18:35 +04:00
}
2002-09-12 06:12:52 +04:00
2012-06-29 22:16:18 +04:00
while ( count ! = 0 ) {
2002-09-12 22:18:35 +04:00
2012-06-29 22:16:18 +04:00
nwritten = count ;
# if defined(DARWIN_SENDFILE_API)
/* Darwin recycles nwritten as a value-result parameter, apart from that this
sendfile implementation is quite the same as the FreeBSD one */
ret = sendfile ( fromfd , tofd , offset , & nwritten , & sf_header , 0 ) ;
# else
ret = sendfile ( fromfd , tofd , offset , count , & sf_header , & nwritten , 0 ) ;
# endif
2018-07-19 01:44:34 +03:00
if ( ret = = - 1 & & errno ! = EINTR ) {
if ( errno = = EAGAIN | | errno = = EWOULDBLOCK ) {
/*
* Sendfile must complete before we can
* send any other outgoing data on the socket .
* Ensure socket is in blocking mode .
* For SMB2 by default the socket is in
* non - blocking mode .
*/
old_flags = fcntl ( tofd , F_GETFL , 0 ) ;
ret = set_blocking ( tofd , true ) ;
if ( ret = = - 1 ) {
goto out ;
}
socket_flags_changed = true ;
continue ;
}
2012-06-29 22:16:18 +04:00
/* Send failed, we are toast. */
2018-07-19 01:44:34 +03:00
ret = - 1 ;
goto out ;
2012-06-29 22:16:18 +04:00
}
2002-09-12 22:18:35 +04:00
2012-06-29 22:16:18 +04:00
if ( nwritten = = 0 ) {
/* EOF of offset is after EOF. */
break ;
}
2002-09-12 22:18:35 +04:00
2012-06-29 22:16:18 +04:00
if ( sf_header . hdr_cnt ) {
if ( io_header . iov_len < = nwritten ) {
/* Entire header was sent. */
sf_header . headers = NULL ;
sf_header . hdr_cnt = 0 ;
nwritten - = io_header . iov_len ;
2002-09-12 22:18:35 +04:00
} else {
2012-06-29 22:16:18 +04:00
/* Partial header was sent. */
io_header . iov_len - = nwritten ;
io_header . iov_base =
( ( uint8_t * ) io_header . iov_base ) + nwritten ;
2002-09-30 09:24:12 +04:00
nwritten = 0 ;
2002-09-12 22:18:35 +04:00
}
}
2012-06-29 22:16:18 +04:00
2002-09-12 22:18:35 +04:00
offset + = nwritten ;
2012-06-29 22:16:18 +04:00
count - = nwritten ;
2002-09-12 06:12:52 +04:00
}
2012-06-29 22:16:18 +04:00
2018-07-19 01:44:34 +03:00
ret = nwritten ;
out :
if ( socket_flags_changed ) {
int saved_errno ;
int err ;
if ( ret = = - 1 ) {
saved_errno = errno ;
}
/* Restore the old state of the socket. */
err = fcntl ( tofd , F_SETFL , old_flags ) ;
if ( err = = - 1 ) {
return - 1 ;
}
if ( ret = = - 1 ) {
errno = saved_errno ;
}
}
return ret ;
2002-09-12 06:12:52 +04:00
}
2004-12-21 01:33:37 +03:00
# elif defined(AIX_SENDFILE_API)
/* BEGIN AIX SEND_FILE */
/* Contributed by William Jojo <jojowil@hvcc.edu> */
# include <sys/socket.h>
2012-04-05 08:53:08 +04:00
ssize_t sys_sendfile ( int tofd , int fromfd , const DATA_BLOB * header , off_t offset , size_t count )
2004-12-21 01:33:37 +03:00
{
struct sf_parms hdtrl ;
2018-07-19 01:49:29 +03:00
int old_flags = 0 ;
ssize_t ret = - 1 ;
bool socket_flags_changed = false ;
2004-12-21 01:33:37 +03:00
/* Set up the header/trailer struct params. */
if ( header ) {
hdtrl . header_data = header - > data ;
hdtrl . header_length = header - > length ;
} else {
hdtrl . header_data = NULL ;
hdtrl . header_length = 0 ;
}
hdtrl . trailer_data = NULL ;
hdtrl . trailer_length = 0 ;
hdtrl . file_descriptor = fromfd ;
hdtrl . file_offset = offset ;
hdtrl . file_bytes = count ;
while ( hdtrl . file_bytes + hdtrl . header_length ) {
/*
Return Value
There are three possible return values from send_file :
Value Description
- 1 an error has occurred , errno contains the error code .
0 the command has completed successfully .
1 the command was completed partially , some data has been
transmitted but the command has to return for some reason ,
for example , the command was interrupted by signals .
*/
do {
ret = send_file ( & tofd , & hdtrl , 0 ) ;
2018-07-19 01:49:29 +03:00
} while ( ( ret = = 1 ) | | ( ret = = - 1 & & errno = = EINTR ) ) ;
if ( ret = = - 1 ) {
if ( errno = = EAGAIN | | errno = = EWOULDBLOCK ) {
/*
* Sendfile must complete before we can
* send any other outgoing data on the socket .
* Ensure socket is in blocking mode .
* For SMB2 by default the socket is in
* non - blocking mode .
*/
old_flags = fcntl ( tofd , F_GETFL , 0 ) ;
ret = set_blocking ( tofd , true ) ;
if ( ret = = - 1 ) {
goto out ;
}
socket_flags_changed = true ;
continue ;
}
goto out ;
}
}
ret = count + header - > length ;
out :
if ( socket_flags_changed ) {
int saved_errno ;
int err ;
if ( ret = = - 1 ) {
saved_errno = errno ;
}
/* Restore the old state of the socket. */
err = fcntl ( tofd , F_SETFL , old_flags ) ;
if ( err = = - 1 ) {
2004-12-21 01:33:37 +03:00
return - 1 ;
2018-07-19 01:49:29 +03:00
}
if ( ret = = - 1 ) {
errno = saved_errno ;
}
2004-12-21 01:33:37 +03:00
}
2018-07-19 01:49:29 +03:00
return ret ;
2004-12-21 01:33:37 +03:00
}
/* END AIX SEND_FILE */
2002-09-12 06:12:52 +04:00
# else /* No sendfile implementation. Return error. */
2012-04-05 08:53:08 +04:00
ssize_t sys_sendfile ( int tofd , int fromfd , const DATA_BLOB * header , off_t offset , size_t count )
2002-09-12 06:12:52 +04:00
{
/* No sendfile syscall. */
errno = ENOSYS ;
return - 1 ;
}
# endif