s3: Fix bug #9085.
[Samba.git] / source3 / lib / recvfile.c
blobcc69d427d8f0460b3520db07a3670f7cb45929b0
1 /*
2 Unix SMB/Netbios implementation.
3 Version 3.2.x
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.
26 #include "includes.h"
28 /* Do this on our own in TRANSFER_BUF_SIZE chunks.
29 * It's safe to make direct syscalls to lseek/write here
30 * as we're below the Samba vfs layer.
32 * Returns -1 on short reads from fromfd (read error)
33 * and sets errno.
35 * Returns number of bytes written to 'tofd'
36 * return != count then sets errno.
37 * Returns count if complete success.
40 #ifndef TRANSFER_BUF_SIZE
41 #define TRANSFER_BUF_SIZE (128*1024)
42 #endif
44 static ssize_t default_sys_recvfile(int fromfd,
45 int tofd,
46 SMB_OFF_T offset,
47 size_t count)
49 int saved_errno = 0;
50 size_t total = 0;
51 size_t bufsize = MIN(TRANSFER_BUF_SIZE,count);
52 size_t total_written = 0;
53 char *buffer = NULL;
55 DEBUG(10,("default_sys_recvfile: from = %d, to = %d, "
56 "offset=%.0f, count = %lu\n",
57 fromfd, tofd, (double)offset,
58 (unsigned long)count));
60 if (count == 0) {
61 return 0;
64 if (tofd != -1 && offset != (SMB_OFF_T)-1) {
65 if (sys_lseek(tofd, offset, SEEK_SET) == -1) {
66 if (errno != ESPIPE) {
67 return -1;
72 buffer = SMB_MALLOC_ARRAY(char, bufsize);
73 if (buffer == NULL) {
74 return -1;
77 while (total < count) {
78 size_t num_written = 0;
79 ssize_t read_ret;
80 size_t toread = MIN(bufsize,count - total);
82 /* Read from socket - ignore EINTR. */
83 read_ret = sys_read(fromfd, buffer, toread);
84 if (read_ret <= 0) {
85 /* EOF or socket error. */
86 free(buffer);
87 return -1;
90 num_written = 0;
92 /* Don't write any more after a write error. */
93 while (tofd != -1 && (num_written < read_ret)) {
94 ssize_t write_ret;
96 /* Write to file - ignore EINTR. */
97 write_ret = sys_write(tofd,
98 buffer + num_written,
99 read_ret - num_written);
101 if (write_ret <= 0) {
102 /* write error - stop writing. */
103 tofd = -1;
104 if (total_written == 0) {
105 /* Ensure we return
106 -1 if the first
107 write failed. */
108 total_written = -1;
110 saved_errno = errno;
111 break;
114 num_written += (size_t)write_ret;
115 total_written += (size_t)write_ret;
118 total += read_ret;
121 free(buffer);
122 if (saved_errno) {
123 /* Return the correct write error. */
124 errno = saved_errno;
126 return (ssize_t)total_written;
129 #if defined(HAVE_LINUX_SPLICE)
132 * Try and use the Linux system call to do this.
133 * Remember we only return -1 if the socket read
134 * failed. Else we return the number of bytes
135 * actually written. We always read count bytes
136 * from the network in the case of return != -1.
140 ssize_t sys_recvfile(int fromfd,
141 int tofd,
142 SMB_OFF_T offset,
143 size_t count)
145 static int pipefd[2] = { -1, -1 };
146 static bool try_splice_call = false;
147 size_t total_written = 0;
148 loff_t splice_offset = offset;
150 DEBUG(10,("sys_recvfile: from = %d, to = %d, "
151 "offset=%.0f, count = %lu\n",
152 fromfd, tofd, (double)offset,
153 (unsigned long)count));
155 if (count == 0) {
156 return 0;
160 * Older Linux kernels have splice for sendfile,
161 * but it fails for recvfile. Ensure we only try
162 * this once and always fall back to the userspace
163 * implementation if recvfile splice fails. JRA.
166 if (!try_splice_call) {
167 return default_sys_recvfile(fromfd,
168 tofd,
169 offset,
170 count);
173 if ((pipefd[0] == -1) && (pipe(pipefd) == -1)) {
174 try_splice_call = false;
175 return default_sys_recvfile(fromfd, tofd, offset, count);
178 while (count > 0) {
179 int nread, to_write;
181 nread = splice(fromfd, NULL, pipefd[1], NULL,
182 MIN(count, 16384), SPLICE_F_MOVE);
183 if (nread == -1) {
184 if (errno == EINTR) {
185 continue;
187 if (total_written == 0 &&
188 (errno == EBADF || errno == EINVAL)) {
189 try_splice_call = false;
190 return default_sys_recvfile(fromfd, tofd,
191 offset, count);
193 break;
196 to_write = nread;
197 while (to_write > 0) {
198 int thistime;
199 thistime = splice(pipefd[0], NULL, tofd,
200 &splice_offset, to_write,
201 SPLICE_F_MOVE);
202 if (thistime == -1) {
203 goto done;
205 to_write -= thistime;
208 total_written += nread;
209 count -= nread;
212 done:
213 if (count) {
214 int saved_errno = errno;
215 if (drain_socket(fromfd, count) != count) {
216 /* socket is dead. */
217 return -1;
219 errno = saved_errno;
222 return total_written;
224 #else
226 /*****************************************************************
227 No recvfile system call - use the default 128 chunk implementation.
228 *****************************************************************/
230 ssize_t sys_recvfile(int fromfd,
231 int tofd,
232 SMB_OFF_T offset,
233 size_t count)
235 return default_sys_recvfile(fromfd, tofd, offset, count);
237 #endif
239 /*****************************************************************
240 Throw away "count" bytes from the client socket.
241 Returns count or -1 on error.
242 *****************************************************************/
244 ssize_t drain_socket(int sockfd, size_t count)
246 size_t total = 0;
247 size_t bufsize = MIN(TRANSFER_BUF_SIZE,count);
248 char *buffer = NULL;
250 if (count == 0) {
251 return 0;
254 buffer = SMB_MALLOC_ARRAY(char, bufsize);
255 if (buffer == NULL) {
256 return -1;
259 while (total < count) {
260 ssize_t read_ret;
261 size_t toread = MIN(bufsize,count - total);
263 /* Read from socket - ignore EINTR. */
264 read_ret = sys_read(sockfd, buffer, toread);
265 if (read_ret <= 0) {
266 /* EOF or socket error. */
267 free(buffer);
268 return -1;
270 total += read_ret;
273 free(buffer);
274 return count;