2 Unix SMB/Netbios implementation.
4 recvfile implementations.
5 Copyright (C) Jeremy Allison 2007.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, see <http://www.gnu.org/licenses/>.
21 * This file handles the OS dependent recvfile implementations.
22 * The API is such that it returns -1 on error, else returns the
23 * number of bytes written.
27 #include "system/filesys.h"
29 /* Do this on our own in TRANSFER_BUF_SIZE chunks.
30 * It's safe to make direct syscalls to lseek/write here
31 * as we're below the Samba vfs layer.
33 * If tofd is -1 we just drain the incoming socket of count
34 * bytes without writing to the outgoing fd.
35 * If a write fails we do the same (to cope with disk full)
38 * Returns -1 on short reads from fromfd (read error)
41 * Returns number of bytes written to 'tofd'
42 * or thrown away if 'tofd == -1'.
43 * return != count then sets errno.
44 * Returns count if complete success.
47 #ifndef TRANSFER_BUF_SIZE
48 #define TRANSFER_BUF_SIZE (128*1024)
51 static ssize_t default_sys_recvfile(int fromfd,
58 size_t bufsize = MIN(TRANSFER_BUF_SIZE,count);
59 size_t total_written = 0;
62 DEBUG(10,("default_sys_recvfile: from = %d, to = %d, "
63 "offset=%.0f, count = %lu\n",
64 fromfd, tofd, (double)offset,
65 (unsigned long)count));
71 if (tofd != -1 && offset != (SMB_OFF_T)-1) {
72 if (sys_lseek(tofd, offset, SEEK_SET) == -1) {
73 if (errno != ESPIPE) {
79 buffer = SMB_MALLOC_ARRAY(char, bufsize);
84 while (total < count) {
85 size_t num_written = 0;
87 size_t toread = MIN(bufsize,count - total);
89 /* Read from socket - ignore EINTR. */
90 read_ret = sys_read(fromfd, buffer, toread);
92 /* EOF or socket error. */
99 while (num_written < read_ret) {
103 write_ret = read_ret;
105 /* Write to file - ignore EINTR. */
106 write_ret = sys_write(tofd,
107 buffer + num_written,
108 read_ret - num_written);
110 if (write_ret <= 0) {
111 /* write error - stop writing. */
118 num_written += (size_t)write_ret;
119 total_written += (size_t)write_ret;
127 /* Return the correct write error. */
130 return (ssize_t)total_written;
133 #if defined(HAVE_LINUX_SPLICE)
136 * Try and use the Linux system call to do this.
137 * Remember we only return -1 if the socket read
138 * failed. Else we return the number of bytes
139 * actually written. We always read count bytes
140 * from the network in the case of return != -1.
144 ssize_t sys_recvfile(int fromfd,
149 static int pipefd[2] = { -1, -1 };
150 static bool try_splice_call = false;
151 size_t total_written = 0;
152 loff_t splice_offset = offset;
154 DEBUG(10,("sys_recvfile: from = %d, to = %d, "
155 "offset=%.0f, count = %lu\n",
156 fromfd, tofd, (double)offset,
157 (unsigned long)count));
164 * Older Linux kernels have splice for sendfile,
165 * but it fails for recvfile. Ensure we only try
166 * this once and always fall back to the userspace
167 * implementation if recvfile splice fails. JRA.
170 if (!try_splice_call) {
171 return default_sys_recvfile(fromfd,
177 if ((pipefd[0] == -1) && (pipe(pipefd) == -1)) {
178 try_splice_call = false;
179 return default_sys_recvfile(fromfd, tofd, offset, count);
185 nread = splice(fromfd, NULL, pipefd[1], NULL,
186 MIN(count, 16384), SPLICE_F_MOVE);
188 if (errno == EINTR) {
191 if (total_written == 0 &&
192 (errno == EBADF || errno == EINVAL)) {
193 try_splice_call = false;
194 return default_sys_recvfile(fromfd, tofd,
201 while (to_write > 0) {
203 thistime = splice(pipefd[0], NULL, tofd,
204 &splice_offset, to_write,
206 if (thistime == -1) {
209 to_write -= thistime;
212 total_written += nread;
218 int saved_errno = errno;
219 if (drain_socket(fromfd, count) != count) {
220 /* socket is dead. */
226 return total_written;
230 /*****************************************************************
231 No recvfile system call - use the default 128 chunk implementation.
232 *****************************************************************/
234 ssize_t sys_recvfile(int fromfd,
239 return default_sys_recvfile(fromfd, tofd, offset, count);
243 /*****************************************************************
244 Throw away "count" bytes from the client socket.
245 *****************************************************************/
247 ssize_t drain_socket(int sockfd, size_t count)
249 return default_sys_recvfile(sockfd, -1, (SMB_OFF_T)-1, count);