2007-10-30 03:17:16 +03:00
/*
Unix SMB / Netbios implementation .
Version 3.2 . x
recvfile implementations .
Copyright ( C ) Jeremy Allison 2007.
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 3 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 , see < http : //www.gnu.org/licenses/>.
*/
/*
* This file handles the OS dependent recvfile implementations .
* The API is such that it returns - 1 on error , else returns the
* number of bytes written .
*/
# include "includes.h"
/* Do this on our own in TRANSFER_BUF_SIZE chunks.
* It ' s safe to make direct syscalls to lseek / write here
* as we ' re below the Samba vfs layer .
*
* If tofd is - 1 we just drain the incoming socket of count
* bytes without writing to the outgoing fd .
* If a write fails we do the same ( to cope with disk full )
* errors .
*
* Returns - 1 on short reads from fromfd ( read error )
* and sets errno .
*
* Returns number of bytes written to ' tofd '
* or thrown away if ' tofd = = - 1 ' .
2007-10-31 04:18:40 +03:00
* return ! = count then sets errno .
2007-10-30 03:17:16 +03:00
* Returns count if complete success .
*/
# ifndef TRANSFER_BUF_SIZE
# define TRANSFER_BUF_SIZE (128*1024)
# endif
static ssize_t default_sys_recvfile ( int fromfd ,
int tofd ,
SMB_OFF_T offset ,
size_t count )
{
int saved_errno = 0 ;
size_t total = 0 ;
size_t bufsize = MIN ( TRANSFER_BUF_SIZE , count ) ;
size_t total_written = 0 ;
char * buffer = NULL ;
2008-05-20 23:09:48 +04:00
DEBUG ( 10 , ( " default_sys_recvfile: from = %d, to = %d, "
" offset=%.0f, count = %lu \n " ,
fromfd , tofd , ( double ) offset ,
( unsigned long ) count ) ) ;
2007-10-30 03:17:16 +03:00
if ( count = = 0 ) {
return 0 ;
}
if ( tofd ! = - 1 & & offset ! = ( SMB_OFF_T ) - 1 ) {
if ( sys_lseek ( tofd , offset , SEEK_SET ) = = - 1 ) {
if ( errno ! = ESPIPE ) {
return - 1 ;
}
}
}
buffer = SMB_MALLOC_ARRAY ( char , bufsize ) ;
if ( buffer = = NULL ) {
return - 1 ;
}
while ( total < count ) {
size_t num_written = 0 ;
ssize_t read_ret ;
size_t toread = MIN ( bufsize , count - total ) ;
/* Read from socket - ignore EINTR. */
read_ret = sys_read ( fromfd , buffer , toread ) ;
if ( read_ret < = 0 ) {
/* EOF or socket error. */
free ( buffer ) ;
return - 1 ;
}
num_written = 0 ;
while ( num_written < read_ret ) {
ssize_t write_ret ;
if ( tofd = = - 1 ) {
write_ret = read_ret ;
} else {
/* Write to file - ignore EINTR. */
write_ret = sys_write ( tofd ,
buffer + num_written ,
read_ret - num_written ) ;
if ( write_ret < = 0 ) {
/* write error - stop writing. */
tofd = - 1 ;
saved_errno = errno ;
continue ;
}
}
num_written + = ( size_t ) write_ret ;
total_written + = ( size_t ) write_ret ;
}
total + = read_ret ;
}
free ( buffer ) ;
if ( saved_errno ) {
/* Return the correct write error. */
errno = saved_errno ;
}
return ( ssize_t ) total_written ;
}
2008-05-07 02:44:39 +04:00
# if defined(HAVE_LINUX_SPLICE)
2007-10-31 02:22:24 +03:00
2007-10-31 04:18:40 +03:00
/*
* Try and use the Linux system call to do this .
* Remember we only return - 1 if the socket read
* failed . Else we return the number of bytes
* actually written . We always read count bytes
* from the network in the case of return ! = - 1.
*/
2008-05-07 02:44:39 +04:00
2007-10-30 03:17:16 +03:00
ssize_t sys_recvfile ( int fromfd ,
int tofd ,
SMB_OFF_T offset ,
size_t count )
{
2008-12-24 00:45:03 +03:00
static int pipefd [ 2 ] = { - 1 , - 1 } ;
2008-12-25 15:44:11 +03:00
static bool try_splice_call = false ;
2007-10-31 04:18:40 +03:00
size_t total_written = 0 ;
2009-01-07 13:39:34 +03:00
loff_t splice_offset = offset ;
2007-10-31 02:22:24 +03:00
2008-05-20 23:09:48 +04:00
DEBUG ( 10 , ( " sys_recvfile: from = %d, to = %d, "
" offset=%.0f, count = %lu \n " ,
fromfd , tofd , ( double ) offset ,
( unsigned long ) count ) ) ;
2007-10-31 02:22:24 +03:00
if ( count = = 0 ) {
return 0 ;
}
2008-05-07 02:44:39 +04:00
/*
* Older Linux kernels have splice for sendfile ,
* but it fails for recvfile . Ensure we only try
* this once and always fall back to the userspace
* implementation if recvfile splice fails . JRA .
*/
if ( ! try_splice_call ) {
return default_sys_recvfile ( fromfd ,
tofd ,
offset ,
count ) ;
}
2008-12-24 00:45:03 +03:00
if ( ( pipefd [ 0 ] = = - 1 ) & & ( pipe ( pipefd ) = = - 1 ) ) {
try_splice_call = false ;
return default_sys_recvfile ( fromfd , tofd , offset , count ) ;
}
while ( count > 0 ) {
int nread , to_write ;
2009-01-07 13:39:34 +03:00
nread = splice ( fromfd , NULL , pipefd [ 1 ] , NULL ,
MIN ( count , 16384 ) , SPLICE_F_MOVE ) ;
2008-12-24 00:45:03 +03:00
if ( nread = = - 1 ) {
if ( errno = = EINTR ) {
continue ;
}
if ( total_written = = 0 & &
( errno = = EBADF | | errno = = EINVAL ) ) {
try_splice_call = false ;
return default_sys_recvfile ( fromfd , tofd ,
offset , count ) ;
2007-10-31 02:22:24 +03:00
}
2008-12-24 00:45:03 +03:00
break ;
2007-10-31 02:22:24 +03:00
}
2008-12-24 00:45:03 +03:00
to_write = nread ;
while ( to_write > 0 ) {
int thistime ;
2009-01-07 13:39:34 +03:00
thistime = splice ( pipefd [ 0 ] , NULL , tofd ,
& splice_offset , to_write ,
SPLICE_F_MOVE ) ;
2008-12-24 00:45:03 +03:00
if ( thistime = = - 1 ) {
goto done ;
}
to_write - = thistime ;
}
total_written + = nread ;
count - = nread ;
2007-10-31 02:22:24 +03:00
}
2007-10-31 04:18:40 +03:00
2008-12-24 00:45:03 +03:00
done :
2007-10-31 04:18:40 +03:00
if ( total_written < count ) {
int saved_errno = errno ;
if ( drain_socket ( fromfd , count - total_written ) ! =
count - total_written ) {
/* socket is dead. */
return - 1 ;
}
errno = saved_errno ;
}
return total_written ;
2007-10-30 03:17:16 +03:00
}
# else
/*****************************************************************
No recvfile system call - use the default 128 chunk implementation .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
ssize_t sys_recvfile ( int fromfd ,
int tofd ,
SMB_OFF_T offset ,
size_t count )
{
return default_sys_recvfile ( fromfd , tofd , offset , count ) ;
}
# endif
/*****************************************************************
Throw away " count " bytes from the client socket .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
ssize_t drain_socket ( int sockfd , size_t count )
{
return default_sys_recvfile ( sockfd , - 1 , ( SMB_OFF_T ) - 1 , count ) ;
}