From 0af071d7628106e3d919018ed8604c9da2af4d50 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 29 Oct 2007 17:17:16 -0700 Subject: Forgot to add recvfile.c Jeremy. (This used to be commit d5886218e53910b7015e6f13bc634e1bf338565a) --- source3/lib/recvfile.c | 158 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 source3/lib/recvfile.c (limited to 'source3/lib/recvfile.c') diff --git a/source3/lib/recvfile.c b/source3/lib/recvfile.c new file mode 100644 index 0000000000..cd9fb12716 --- /dev/null +++ b/source3/lib/recvfile.c @@ -0,0 +1,158 @@ +/* + 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 . +*/ + +/* + * 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'. + * eturn != count then sets errno. + * 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; + + 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; +} + +#if defined(HAVE_SPLICE_SYSCALL) +ssize_t sys_recvfile(int fromfd, + int tofd, + SMB_OFF_T offset, + size_t count) +{ + errno = ENOSYS + return -1; +} +#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); +} -- cgit From c3250149e12338fac5093991b385ad2807c92d1f Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 30 Oct 2007 16:22:24 -0700 Subject: Add new parameter, "min receivefile size" (by default set to zero). If non-zero, writeX calls greater than this value will be left in the socket buffer for later handling with recvfile (or userspace equivalent). Definition of recvfile for your system is left as an exercise for the reader (I'm working on getting splice working :-). Jeremy. (This used to be commit 11c03b75ddbcb6e36b231bb40a1773d1c550621c) --- source3/lib/recvfile.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) (limited to 'source3/lib/recvfile.c') diff --git a/source3/lib/recvfile.c b/source3/lib/recvfile.c index cd9fb12716..9d77f94a29 100644 --- a/source3/lib/recvfile.c +++ b/source3/lib/recvfile.c @@ -125,13 +125,49 @@ static ssize_t default_sys_recvfile(int fromfd, } #if defined(HAVE_SPLICE_SYSCALL) + +#ifdef JRA_SPLICE_TEST +#include +#include + +#define __NR_splice 313 +_syscall6( long, splice, + int, fromfd, + loff_t *, fromoffset, + int, tofd, + loff_t *, tooffset, + size_t, count, + unsigned int, flags); +#endif + ssize_t sys_recvfile(int fromfd, int tofd, SMB_OFF_T offset, size_t count) { - errno = ENOSYS - return -1; + size_t total = 0; + + if (count == 0) { + return 0; + } + + while (total < count) { + ssize_t ret = splice(fromfd, + NULL, + tofd, + &offset, + count, + 0); + if (ret == -1) { + if (errno != EINTR) { + return -1; + } + continue; + } + total += ret; + count -= ret; + } + return total; } #else -- cgit From ff82c0a037b7c7ce69d87ab70284acc71df5e1a7 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 30 Oct 2007 18:18:40 -0700 Subject: Handle the disk full error case correctly. Jeremy. (This used to be commit b7088bb9c2a00d4717b9a7efa4bddc0c005f4efb) --- source3/lib/recvfile.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) (limited to 'source3/lib/recvfile.c') diff --git a/source3/lib/recvfile.c b/source3/lib/recvfile.c index 9d77f94a29..a009ce9dd6 100644 --- a/source3/lib/recvfile.c +++ b/source3/lib/recvfile.c @@ -39,7 +39,7 @@ * * Returns number of bytes written to 'tofd' * or thrown away if 'tofd == -1'. - * eturn != count then sets errno. + * return != count then sets errno. * Returns count if complete success. */ @@ -140,18 +140,26 @@ _syscall6( long, splice, unsigned int, flags); #endif +/* + * 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. + */ + ssize_t sys_recvfile(int fromfd, int tofd, SMB_OFF_T offset, size_t count) { - size_t total = 0; + size_t total_written = 0; if (count == 0) { return 0; } - while (total < count) { + while (total_written < count) { ssize_t ret = splice(fromfd, NULL, tofd, @@ -160,14 +168,25 @@ ssize_t sys_recvfile(int fromfd, 0); if (ret == -1) { if (errno != EINTR) { - return -1; + break; } continue; } - total += ret; + total_written += ret; count -= ret; } - return total; + + 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; } #else -- cgit From 133fad90b5f9cef26f9c78cc5b302358c9c4da6a Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 31 Oct 2007 14:01:35 -0700 Subject: Make explicit draining the socket on RECVFILE. Add capability for large UNIX write if not signing and recvfile set. Cope with large UNIX write length on incoming processing. Stevef - we can now test 1-16Mb writes from CIFFS. Jeremy. (This used to be commit 8cf78776b0a44bd026cef3d74eb11cfb415f8303) --- source3/lib/recvfile.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) (limited to 'source3/lib/recvfile.c') diff --git a/source3/lib/recvfile.c b/source3/lib/recvfile.c index a009ce9dd6..f9788fdefb 100644 --- a/source3/lib/recvfile.c +++ b/source3/lib/recvfile.c @@ -126,20 +126,6 @@ static ssize_t default_sys_recvfile(int fromfd, #if defined(HAVE_SPLICE_SYSCALL) -#ifdef JRA_SPLICE_TEST -#include -#include - -#define __NR_splice 313 -_syscall6( long, splice, - int, fromfd, - loff_t *, fromoffset, - int, tofd, - loff_t *, tooffset, - size_t, count, - unsigned int, flags); -#endif - /* * Try and use the Linux system call to do this. * Remember we only return -1 if the socket read @@ -168,6 +154,13 @@ ssize_t sys_recvfile(int fromfd, 0); if (ret == -1) { if (errno != EINTR) { + if (total_written == 0 && + errno == EBADF || errno == EINVAL) { + return default_sys_recvfile(fromfd, + tofd, + offset, + count); + } break; } continue; -- cgit From 82572cfd4370fbcfbe35f1d5f11fb05fc732cba2 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 6 May 2008 15:44:39 -0700 Subject: Enable tests for splice on Linux. Add a static (vl, I hate this) so we can detect broken Linux recvfile splice and correctly fall back. Jeremy. (This used to be commit ec2d301a7aac173aba41dd2074037f27d05095ce) --- source3/lib/recvfile.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'source3/lib/recvfile.c') diff --git a/source3/lib/recvfile.c b/source3/lib/recvfile.c index f9788fdefb..6e20933350 100644 --- a/source3/lib/recvfile.c +++ b/source3/lib/recvfile.c @@ -124,7 +124,7 @@ static ssize_t default_sys_recvfile(int fromfd, return (ssize_t)total_written; } -#if defined(HAVE_SPLICE_SYSCALL) +#if defined(HAVE_LINUX_SPLICE) /* * Try and use the Linux system call to do this. @@ -134,17 +134,33 @@ static ssize_t default_sys_recvfile(int fromfd, * from the network in the case of return != -1. */ + ssize_t sys_recvfile(int fromfd, int tofd, SMB_OFF_T offset, size_t count) { + static bool try_splice_call = true; size_t total_written = 0; if (count == 0) { return 0; } + /* + * 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); + } + while (total_written < count) { ssize_t ret = splice(fromfd, NULL, @@ -155,7 +171,8 @@ ssize_t sys_recvfile(int fromfd, if (ret == -1) { if (errno != EINTR) { if (total_written == 0 && - errno == EBADF || errno == EINVAL) { + (errno == EBADF || errno == EINVAL)) { + try_splice_call = false; return default_sys_recvfile(fromfd, tofd, offset, -- cgit From 8ca459e067c3d4f3495e0a6dafea7296e3dfb2ab Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 20 May 2008 12:09:48 -0700 Subject: Fix bug #5477 - recvfile code was broken. Jeremy. (This used to be commit 830337f054a6c0646d85df33d9958e99283e727a) --- source3/lib/recvfile.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'source3/lib/recvfile.c') diff --git a/source3/lib/recvfile.c b/source3/lib/recvfile.c index 6e20933350..513742ce8f 100644 --- a/source3/lib/recvfile.c +++ b/source3/lib/recvfile.c @@ -58,6 +58,11 @@ static ssize_t default_sys_recvfile(int fromfd, size_t total_written = 0; char *buffer = NULL; + DEBUG(10,("default_sys_recvfile: from = %d, to = %d, " + "offset=%.0f, count = %lu\n", + fromfd, tofd, (double)offset, + (unsigned long)count)); + if (count == 0) { return 0; } @@ -143,6 +148,11 @@ ssize_t sys_recvfile(int fromfd, static bool try_splice_call = true; size_t total_written = 0; + DEBUG(10,("sys_recvfile: from = %d, to = %d, " + "offset=%.0f, count = %lu\n", + fromfd, tofd, (double)offset, + (unsigned long)count)); + if (count == 0) { return 0; } -- cgit