s3:smbd: let default_sys_recvfile() and sys_recvfile() cope with non-blocking sockets.
[Samba.git] / source3 / lib / recvfile.c
blobbffe07f75cd4c54fdcc79649f8f8dc8a5926b4ef
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"
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 * Returns -1 on short reads from fromfd (read error)
34 * and sets errno.
36 * Returns number of bytes written to 'tofd'
37 * return != count then sets errno.
38 * Returns count if complete success.
41 #ifndef TRANSFER_BUF_SIZE
42 #define TRANSFER_BUF_SIZE (128*1024)
43 #endif
45 static ssize_t default_sys_recvfile(int fromfd,
46 int tofd,
47 off_t offset,
48 size_t count)
50 int saved_errno = 0;
51 size_t total = 0;
52 size_t bufsize = MIN(TRANSFER_BUF_SIZE,count);
53 size_t total_written = 0;
54 char buffer[bufsize];
56 DEBUG(10,("default_sys_recvfile: from = %d, to = %d, "
57 "offset=%.0f, count = %lu\n",
58 fromfd, tofd, (double)offset,
59 (unsigned long)count));
61 if (count == 0) {
62 return 0;
65 if (tofd != -1 && offset != (off_t)-1) {
66 if (lseek(tofd, offset, SEEK_SET) == -1) {
67 if (errno != ESPIPE) {
68 return -1;
73 while (total < count) {
74 size_t num_written = 0;
75 ssize_t read_ret;
76 size_t toread = MIN(bufsize,count - total);
79 * Read from socket - ignore EINTR.
80 * Can't use sys_read() as that also
81 * ignores EAGAIN and EWOULDBLOCK.
83 do {
84 read_ret = read(fromfd, buffer, toread);
85 } while (read_ret == -1 && errno == EINTR);
87 #if defined(EWOULDBLOCK)
88 if (read_ret == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
89 #else
90 if (read_ret == -1 && (errno == EAGAIN)) {
91 #endif
93 * fromfd socket is in non-blocking mode.
94 * If we already read some and wrote
95 * it successfully, return that.
96 * Only return -1 if this is the first read
97 * attempt. Caller will handle both cases.
99 if (total_written != 0) {
100 return total_written;
102 return -1;
105 if (read_ret <= 0) {
106 /* EOF or socket error. */
107 return -1;
110 num_written = 0;
112 /* Don't write any more after a write error. */
113 while (tofd != -1 && (num_written < read_ret)) {
114 ssize_t write_ret;
116 /* Write to file - ignore EINTR. */
117 write_ret = sys_write(tofd,
118 buffer + num_written,
119 read_ret - num_written);
121 if (write_ret <= 0) {
122 /* write error - stop writing. */
123 tofd = -1;
124 if (total_written == 0) {
125 /* Ensure we return
126 -1 if the first
127 write failed. */
128 total_written = -1;
130 saved_errno = errno;
131 break;
134 num_written += (size_t)write_ret;
135 total_written += (size_t)write_ret;
138 total += read_ret;
141 if (saved_errno) {
142 /* Return the correct write error. */
143 errno = saved_errno;
145 return (ssize_t)total_written;
148 #if defined(HAVE_LINUX_SPLICE)
151 * Try and use the Linux system call to do this.
152 * Remember we only return -1 if the socket read
153 * failed. Else we return the number of bytes
154 * actually written. We always read count bytes
155 * from the network in the case of return != -1.
159 ssize_t sys_recvfile(int fromfd,
160 int tofd,
161 off_t offset,
162 size_t count)
164 static int pipefd[2] = { -1, -1 };
165 static bool try_splice_call = false;
166 size_t total_written = 0;
167 loff_t splice_offset = offset;
169 DEBUG(10,("sys_recvfile: from = %d, to = %d, "
170 "offset=%.0f, count = %lu\n",
171 fromfd, tofd, (double)offset,
172 (unsigned long)count));
174 if (count == 0) {
175 return 0;
179 * Older Linux kernels have splice for sendfile,
180 * but it fails for recvfile. Ensure we only try
181 * this once and always fall back to the userspace
182 * implementation if recvfile splice fails. JRA.
185 if (!try_splice_call) {
186 return default_sys_recvfile(fromfd,
187 tofd,
188 offset,
189 count);
192 if ((pipefd[0] == -1) && (pipe(pipefd) == -1)) {
193 try_splice_call = false;
194 return default_sys_recvfile(fromfd, tofd, offset, count);
197 while (count > 0) {
198 int nread, to_write;
200 nread = splice(fromfd, NULL, pipefd[1], NULL,
201 MIN(count, 16384), SPLICE_F_MOVE);
202 if (nread == -1) {
203 if (errno == EINTR) {
204 continue;
206 if (total_written == 0 &&
207 (errno == EBADF || errno == EINVAL)) {
208 try_splice_call = false;
209 return default_sys_recvfile(fromfd, tofd,
210 offset, count);
212 #if defined(EWOULDBLOCK)
213 if (errno == EAGAIN || errno == EWOULDBLOCK) {
214 #else
215 if (errno == EAGAIN) {
216 #endif
218 * fromfd socket is in non-blocking mode.
219 * If we already read some and wrote
220 * it successfully, return that.
221 * Only return -1 if this is the first read
222 * attempt. Caller will handle both cases.
224 if (total_written != 0) {
225 return total_written;
227 return -1;
229 break;
232 to_write = nread;
233 while (to_write > 0) {
234 int thistime;
235 thistime = splice(pipefd[0], NULL, tofd,
236 &splice_offset, to_write,
237 SPLICE_F_MOVE);
238 if (thistime == -1) {
239 goto done;
241 to_write -= thistime;
244 total_written += nread;
245 count -= nread;
248 done:
249 if (count) {
250 int saved_errno = errno;
251 if (drain_socket(fromfd, count) != count) {
252 /* socket is dead. */
253 return -1;
255 errno = saved_errno;
258 return total_written;
260 #else
262 /*****************************************************************
263 No recvfile system call - use the default 128 chunk implementation.
264 *****************************************************************/
266 ssize_t sys_recvfile(int fromfd,
267 int tofd,
268 off_t offset,
269 size_t count)
271 return default_sys_recvfile(fromfd, tofd, offset, count);
273 #endif
275 /*****************************************************************
276 Throw away "count" bytes from the client socket.
277 Returns count or -1 on error.
278 Must only operate on a blocking socket.
279 *****************************************************************/
281 ssize_t drain_socket(int sockfd, size_t count)
283 size_t total = 0;
284 size_t bufsize = MIN(TRANSFER_BUF_SIZE,count);
285 char buffer[bufsize];
286 int old_flags = 0;
288 if (count == 0) {
289 return 0;
292 old_flags = fcntl(sockfd, F_GETFL, 0);
293 if (set_blocking(sockfd, true) == -1) {
294 return -1;
297 while (total < count) {
298 ssize_t read_ret;
299 size_t toread = MIN(bufsize,count - total);
301 /* Read from socket - ignore EINTR. */
302 read_ret = sys_read(sockfd, buffer, toread);
303 if (read_ret <= 0) {
304 /* EOF or socket error. */
305 count = (size_t)-1;
306 goto out;
308 total += read_ret;
311 out:
313 if (fcntl(sockfd, F_SETFL, old_flags) == -1) {
314 return -1;
316 return count;