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
the Free Software Foundation ; either version 2 of the License , or
( 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
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
/*
* 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"
# if defined(LINUX_SENDFILE_API)
# include <sys/sendfile.h>
# ifndef MSG_MORE
# define MSG_MORE 0x8000
# endif
2002-09-12 22:18:35 +04:00
ssize_t sys_sendfile ( int tofd , int fromfd , const DATA_BLOB * header , SMB_OFF_T offset , size_t count )
2002-09-12 06:12:52 +04:00
{
size_t total = 0 ;
ssize_t ret ;
2002-09-12 10:24:41 +04:00
ssize_t hdr_len = 0 ;
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 ) ;
2002-09-12 10:24:41 +04:00
if ( ret = = - 1 )
return - 1 ;
total + = ret ;
}
2002-09-12 06:12:52 +04:00
}
total = count ;
while ( total ) {
ssize_t nwritten ;
do {
2002-09-14 05:38:55 +04:00
# if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILE64)
2002-09-12 22:18:35 +04:00
nwritten = sendfile64 ( tofd , fromfd , & offset , total ) ;
2002-09-12 06:12:52 +04:00
# else
2002-09-12 22:18:35 +04:00
nwritten = sendfile ( tofd , fromfd , & offset , total ) ;
2002-09-12 06:12:52 +04:00
# endif
} while ( nwritten = = - 1 & & errno = = EINTR ) ;
if ( nwritten = = - 1 )
return - 1 ;
if ( nwritten = = 0 )
return - 1 ; /* I think we're at EOF here... */
total - = nwritten ;
}
2002-09-12 10:24:41 +04:00
return count + hdr_len ;
2002-09-12 06:12:52 +04:00
}
2002-09-14 04:34:46 +04:00
# elif defined(LINUX_BROKEN_SENDFILE_API)
2002-09-17 05:00:03 +04:00
/*
* We must use explicit 32 bit types here . This code path means Linux
* won ' t do proper 64 - bit sendfile . JRA .
*/
extern int32 sendfile ( int out_fd , int in_fd , int32 * offset , uint32 count ) ;
2002-09-14 04:34:46 +04:00
# ifndef MSG_MORE
# define MSG_MORE 0x8000
# endif
ssize_t sys_sendfile ( int tofd , int fromfd , const DATA_BLOB * header , SMB_OFF_T offset , size_t count )
{
size_t total = 0 ;
ssize_t ret ;
ssize_t hdr_len = 0 ;
2002-09-17 05:00:03 +04:00
uint32 small_total = 0 ;
int32 small_offset ;
2002-09-14 04:34:46 +04:00
/*
* Fix for broken Linux 2.4 systems with no working sendfile64 ( ) .
* If the offset + count > 2 GB then pretend we don ' t have the
2002-09-17 05:00:03 +04:00
* system call sendfile at all . The upper layer catches this
2002-09-14 04:34:46 +04:00
* and uses a normal read . JRA .
*/
if ( ( sizeof ( SMB_OFF_T ) > = 8 ) & & ( offset + count > ( SMB_OFF_T ) 0x7FFFFFFF ) ) {
errno = ENOSYS ;
return - 1 ;
}
/*
* Send the header first .
* Use MSG_MORE to cork the TCP output until sendfile is called .
*/
if ( header ) {
hdr_len = header - > length ;
2002-09-17 23:48:19 +04:00
while ( total < hdr_len ) {
2002-09-14 04:34:46 +04:00
ret = sys_send ( tofd , header - > data + total , hdr_len - total , MSG_MORE ) ;
if ( ret = = - 1 )
return - 1 ;
total + = ret ;
}
}
2002-09-17 05:00:03 +04:00
small_total = ( uint32 ) count ;
small_offset = ( int32 ) offset ;
while ( small_total ) {
int32 nwritten ;
2002-09-14 04:34:46 +04:00
do {
2002-09-17 05:00:03 +04:00
nwritten = sendfile ( tofd , fromfd , & small_offset , small_total ) ;
2002-09-14 04:34:46 +04:00
} while ( nwritten = = - 1 & & errno = = EINTR ) ;
if ( nwritten = = - 1 )
return - 1 ;
if ( nwritten = = 0 )
return - 1 ; /* I think we're at EOF here... */
2002-09-17 05:00:03 +04:00
small_total - = nwritten ;
2002-09-14 04:34:46 +04:00
}
return count + hdr_len ;
}
2002-09-12 06:12:52 +04:00
# elif defined(SOLARIS_SENDFILE_API)
2002-09-14 05:38:55 +04:00
/* Hmmm. Can't find Solaris sendfile API docs.... Where is it ? */
2002-09-12 22:18:35 +04:00
ssize_t sys_sendfile ( int tofd , int fromfd , const DATA_BLOB * header , SMB_OFF_T offset , size_t count )
2002-09-12 06:12:52 +04:00
{
2002-09-14 05:38:55 +04:00
errno = ENOSYS ;
return - 1 ;
2002-09-12 06:12:52 +04:00
}
# elif defined(HPUX_SENDFILE_API)
# include <sys/socket.h>
# include <sys/uio.h>
2002-09-12 22:18:35 +04:00
ssize_t sys_sendfile ( int tofd , int fromfd , const DATA_BLOB * header , SMB_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 ;
if ( header ) {
/* Set up the header/trailer iovec. */
hdtrl [ 0 ] . iov_base = header - > data ;
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 ;
hdtrl [ 1 ] . iov_base = 0 ;
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-14 05:38:55 +04:00
# if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILE64)
2002-09-12 22:18:35 +04:00
nwritten = sendfile64 ( tofd , fromfd , offset , total , & hdtrl [ 0 ] , 0 ) ;
2002-09-12 06:12:52 +04:00
# else
2002-09-12 22:18:35 +04:00
nwritten = sendfile ( tofd , fromfd , offset , total , & hdtrl [ 0 ] , 0 ) ;
2002-09-12 06:12:52 +04:00
# endif
} while ( nwritten = = - 1 & & errno = = EINTR ) ;
if ( nwritten = = - 1 )
return - 1 ;
2002-09-12 10:24:41 +04:00
if ( nwritten = = 0 )
return - 1 ; /* I think we're at EOF here... */
/*
* 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 {
nwritten = 0 ;
2002-09-19 00:07:56 +04:00
/* iov_base is defined as a void *... */
hdtrl [ 0 ] . iov_base = ( ( char * ) hdtrl [ 0 ] . iov_base ) + nwritten ;
2002-09-12 10:24:41 +04:00
hdtrl [ 0 ] . iov_len - = nwritten ;
}
}
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
}
2002-09-12 10:24:41 +04:00
return count + hdr_len ;
2002-09-12 06:12:52 +04:00
}
# elif defined(FREEBSD_SENDFILE_API)
# include <sys/types.h>
# include <sys/socket.h>
# include <sys/uio.h>
2002-09-12 22:18:35 +04:00
ssize_t sys_sendfile ( int tofd , int fromfd , const DATA_BLOB * header , SMB_OFF_T offset , size_t count )
2002-09-12 06:12:52 +04:00
{
size_t total = 0 ;
struct sf_hdtr hdr ;
struct iovec hdtrl ;
2002-09-12 22:18:35 +04:00
size_t hdr_len = 0 ;
2002-09-12 06:12:52 +04:00
2002-09-21 12:53:05 +04:00
hdr . headers = & hdtrl ;
hdr . hdr_cnt = 1 ;
hdr . trailers = NULL ;
hdr . trl_cnt = 0 ;
2002-09-12 06:12:52 +04:00
/* Set up the header iovec. */
2002-09-12 22:18:35 +04:00
if ( header ) {
hdtrl . iov_base = header - > data ;
hdtrl . iov_len = hdr_len = header - > length ;
} else {
hdtrl . iov_base = NULL ;
hdtrl . iov_len = 0 ;
}
2002-09-12 06:12:52 +04:00
total = count ;
2002-09-12 22:18:35 +04:00
while ( total + hdtrl . iov_len ) {
SMB_OFF_T nwritten ;
int ret ;
/*
* FreeBSD sendfile returns 0 on success , - 1 on error .
* Remember , the tofd and fromfd are reversed . . . . . : - ) .
* nwritten includes the header data sent .
*/
2002-09-12 06:12:52 +04:00
do {
2002-09-12 22:18:35 +04:00
ret = sendfile ( fromfd , tofd , offset , total , & hdr , & nwritten , 0 ) ;
} while ( ret = = - 1 & & errno = = EINTR ) ;
if ( ret = = - 1 )
2002-09-12 06:12:52 +04:00
return - 1 ;
2002-09-12 22:18:35 +04:00
if ( nwritten = = 0 )
return - 1 ; /* I think we're at EOF here... */
/*
* 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 . iov_len bytes .
* We change nwritten to be the number of file bytes written .
*/
2002-09-21 12:53:05 +04:00
if ( hdtrl . iov_base & & hdtrl . iov_len ) {
2002-09-12 22:18:35 +04:00
if ( nwritten > = hdtrl . iov_len ) {
nwritten - = hdtrl . iov_len ;
hdtrl . iov_base = NULL ;
hdtrl . iov_len = 0 ;
} else {
nwritten = 0 ;
hdtrl . iov_base + = nwritten ;
hdtrl . iov_len - = nwritten ;
}
}
2002-09-12 06:12:52 +04:00
total - = nwritten ;
2002-09-12 22:18:35 +04:00
offset + = nwritten ;
2002-09-12 06:12:52 +04:00
}
2002-09-12 22:18:35 +04:00
return count + hdr_len ;
2002-09-12 06:12:52 +04:00
}
# else /* No sendfile implementation. Return error. */
2002-09-12 22:18:35 +04:00
ssize_t sys_sendfile ( int tofd , int fromfd , const DATA_BLOB * header , SMB_OFF_T offset , size_t count )
2002-09-12 06:12:52 +04:00
{
/* No sendfile syscall. */
errno = ENOSYS ;
return - 1 ;
}
# endif