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"
2011-02-26 01:20:06 +03:00
# include "system/filesys.h"
2015-10-12 16:57:34 +03:00
# include "lib/util/sys_rw.h"
2007-10-30 03:17:16 +03:00
/* 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 .
*
* Returns - 1 on short reads from fromfd ( read error )
* and sets errno .
*
* Returns number of bytes written to ' tofd '
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 ,
2012-04-05 08:53:08 +04:00
off_t offset ,
2007-10-30 03:17:16 +03:00
size_t count )
{
int saved_errno = 0 ;
size_t total = 0 ;
size_t bufsize = MIN ( TRANSFER_BUF_SIZE , count ) ;
size_t total_written = 0 ;
2013-12-05 14:20:49 +04:00
char buffer [ bufsize ] ;
2007-10-30 03:17:16 +03:00
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 ;
}
2012-04-05 08:53:08 +04:00
if ( tofd ! = - 1 & & offset ! = ( off_t ) - 1 ) {
2012-03-28 05:37:04 +04:00
if ( lseek ( tofd , offset , SEEK_SET ) = = - 1 ) {
2007-10-30 03:17:16 +03:00
if ( errno ! = ESPIPE ) {
return - 1 ;
}
}
}
while ( total < count ) {
size_t num_written = 0 ;
ssize_t read_ret ;
size_t toread = MIN ( bufsize , count - total ) ;
2014-04-15 23:43:06 +04:00
/*
* Read from socket - ignore EINTR .
* Can ' t use sys_read ( ) as that also
* ignores EAGAIN and EWOULDBLOCK .
*/
do {
read_ret = read ( fromfd , buffer , toread ) ;
} while ( read_ret = = - 1 & & errno = = EINTR ) ;
if ( read_ret = = - 1 & & ( errno = = EAGAIN | | errno = = EWOULDBLOCK ) ) {
/*
* fromfd socket is in non - blocking mode .
* If we already read some and wrote
* it successfully , return that .
* Only return - 1 if this is the first read
* attempt . Caller will handle both cases .
*/
if ( total_written ! = 0 ) {
return total_written ;
}
return - 1 ;
}
2007-10-30 03:17:16 +03:00
if ( read_ret < = 0 ) {
/* EOF or socket error. */
return - 1 ;
}
num_written = 0 ;
2011-12-31 08:45:10 +04:00
/* Don't write any more after a write error. */
while ( tofd ! = - 1 & & ( num_written < read_ret ) ) {
2007-10-30 03:17:16 +03:00
ssize_t write_ret ;
2011-12-31 08:45:10 +04:00
/* 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 ;
if ( total_written = = 0 ) {
/* Ensure we return
- 1 if the first
write failed . */
total_written = - 1 ;
}
saved_errno = errno ;
break ;
2007-10-30 03:17:16 +03:00
}
num_written + = ( size_t ) write_ret ;
total_written + = ( size_t ) write_ret ;
}
total + = read_ret ;
}
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 ,
2012-04-05 08:53:08 +04:00
off_t offset ,
2007-10-30 03:17:16 +03:00
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
}
2014-04-15 23:43:06 +04:00
if ( errno = = EAGAIN | | errno = = EWOULDBLOCK ) {
/*
* fromfd socket is in non - blocking mode .
* If we already read some and wrote
* it successfully , return that .
* Only return - 1 if this is the first read
* attempt . Caller will handle both cases .
*/
if ( total_written ! = 0 ) {
return total_written ;
}
return - 1 ;
}
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 :
2011-12-25 09:12:09 +04:00
if ( count ) {
2007-10-31 04:18:40 +03:00
int saved_errno = errno ;
2011-12-25 09:12:09 +04:00
if ( drain_socket ( fromfd , count ) ! = count ) {
2007-10-31 04:18:40 +03:00
/* 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 ,
2012-04-05 08:53:08 +04:00
off_t offset ,
2007-10-30 03:17:16 +03:00
size_t count )
{
return default_sys_recvfile ( fromfd , tofd , offset , count ) ;
}
# endif
/*****************************************************************
Throw away " count " bytes from the client socket .
2011-12-31 08:23:00 +04:00
Returns count or - 1 on error .
2013-04-08 21:16:48 +04:00
Must only operate on a blocking socket .
2007-10-30 03:17:16 +03:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
ssize_t drain_socket ( int sockfd , size_t count )
{
2011-12-31 08:23:00 +04:00
size_t total = 0 ;
size_t bufsize = MIN ( TRANSFER_BUF_SIZE , count ) ;
2013-12-05 14:20:49 +04:00
char buffer [ bufsize ] ;
2013-04-08 21:16:48 +04:00
int old_flags = 0 ;
2011-12-31 08:23:00 +04:00
if ( count = = 0 ) {
return 0 ;
}
2013-04-08 21:16:48 +04:00
old_flags = fcntl ( sockfd , F_GETFL , 0 ) ;
if ( set_blocking ( sockfd , true ) = = - 1 ) {
return - 1 ;
}
2011-12-31 08:23:00 +04:00
while ( total < count ) {
ssize_t read_ret ;
size_t toread = MIN ( bufsize , count - total ) ;
/* Read from socket - ignore EINTR. */
read_ret = sys_read ( sockfd , buffer , toread ) ;
if ( read_ret < = 0 ) {
/* EOF or socket error. */
2013-04-08 21:16:48 +04:00
count = ( size_t ) - 1 ;
goto out ;
2011-12-31 08:23:00 +04:00
}
total + = read_ret ;
}
2013-04-08 21:16:48 +04:00
out :
if ( fcntl ( sockfd , F_SETFL , old_flags ) = = - 1 ) {
return - 1 ;
}
2011-12-31 08:23:00 +04:00
return count ;
2007-10-30 03:17:16 +03:00
}