s3:smbd: let default_sys_recvfile() and sys_recvfile() cope with non-blocking sockets.
[samba.git] / source3 / lib / recvfile.c
1 /*
2  Unix SMB/Netbios implementation.
3  Version 3.2.x
4  recvfile implementations.
5  Copyright (C) Jeremy Allison 2007.
6
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.
15
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/>.
18 */
19
20 /*
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.
24  */
25
26 #include "includes.h"
27 #include "system/filesys.h"
28
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.
32  *
33  * Returns -1 on short reads from fromfd (read error)
34  * and sets errno.
35  *
36  * Returns number of bytes written to 'tofd'
37  * return != count then sets errno.
38  * Returns count if complete success.
39  */
40
41 #ifndef TRANSFER_BUF_SIZE
42 #define TRANSFER_BUF_SIZE (128*1024)
43 #endif
44
45 static ssize_t default_sys_recvfile(int fromfd,
46                         int tofd,
47                         off_t offset,
48                         size_t count)
49 {
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];
55
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));
60
61         if (count == 0) {
62                 return 0;
63         }
64
65         if (tofd != -1 && offset != (off_t)-1) {
66                 if (lseek(tofd, offset, SEEK_SET) == -1) {
67                         if (errno != ESPIPE) {
68                                 return -1;
69                         }
70                 }
71         }
72
73         while (total < count) {
74                 size_t num_written = 0;
75                 ssize_t read_ret;
76                 size_t toread = MIN(bufsize,count - total);
77
78                 /*
79                  * Read from socket - ignore EINTR.
80                  * Can't use sys_read() as that also
81                  * ignores EAGAIN and EWOULDBLOCK.
82                  */
83                 do {
84                         read_ret = read(fromfd, buffer, toread);
85                 } while (read_ret == -1 && errno == EINTR);
86
87 #if defined(EWOULDBLOCK)
88                 if (read_ret == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
89 #else
90                 if (read_ret == -1 && (errno == EAGAIN)) {
91 #endif
92                         /*
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.
98                          */
99                         if (total_written != 0) {
100                                 return total_written;
101                         }
102                         return -1;
103                 }
104
105                 if (read_ret <= 0) {
106                         /* EOF or socket error. */
107                         return -1;
108                 }
109
110                 num_written = 0;
111
112                 /* Don't write any more after a write error. */
113                 while (tofd != -1 && (num_written < read_ret)) {
114                         ssize_t write_ret;
115
116                         /* Write to file - ignore EINTR. */
117                         write_ret = sys_write(tofd,
118                                         buffer + num_written,
119                                         read_ret - num_written);
120
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;
129                                 }
130                                 saved_errno = errno;
131                                 break;
132                         }
133
134                         num_written += (size_t)write_ret;
135                         total_written += (size_t)write_ret;
136                 }
137
138                 total += read_ret;
139         }
140
141         if (saved_errno) {
142                 /* Return the correct write error. */
143                 errno = saved_errno;
144         }
145         return (ssize_t)total_written;
146 }
147
148 #if defined(HAVE_LINUX_SPLICE)
149
150 /*
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.
156  */
157
158
159 ssize_t sys_recvfile(int fromfd,
160                         int tofd,
161                         off_t offset,
162                         size_t count)
163 {
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;
168
169         DEBUG(10,("sys_recvfile: from = %d, to = %d, "
170                 "offset=%.0f, count = %lu\n",
171                 fromfd, tofd, (double)offset,
172                 (unsigned long)count));
173
174         if (count == 0) {
175                 return 0;
176         }
177
178         /*
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.
183          */
184
185         if (!try_splice_call) {
186                 return default_sys_recvfile(fromfd,
187                                 tofd,
188                                 offset,
189                                 count);
190         }
191
192         if ((pipefd[0] == -1) && (pipe(pipefd) == -1)) {
193                 try_splice_call = false;
194                 return default_sys_recvfile(fromfd, tofd, offset, count);
195         }
196
197         while (count > 0) {
198                 int nread, to_write;
199
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;
205                         }
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);
211                         }
212 #if defined(EWOULDBLOCK)
213                         if (errno == EAGAIN || errno == EWOULDBLOCK) {
214 #else
215                         if (errno == EAGAIN) {
216 #endif
217                                 /*
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.
223                                  */
224                                 if (total_written != 0) {
225                                         return total_written;
226                                 }
227                                 return -1;
228                         }
229                         break;
230                 }
231
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;
240                         }
241                         to_write -= thistime;
242                 }
243
244                 total_written += nread;
245                 count -= nread;
246         }
247
248  done:
249         if (count) {
250                 int saved_errno = errno;
251                 if (drain_socket(fromfd, count) != count) {
252                         /* socket is dead. */
253                         return -1;
254                 }
255                 errno = saved_errno;
256         }
257
258         return total_written;
259 }
260 #else
261
262 /*****************************************************************
263  No recvfile system call - use the default 128 chunk implementation.
264 *****************************************************************/
265
266 ssize_t sys_recvfile(int fromfd,
267                         int tofd,
268                         off_t offset,
269                         size_t count)
270 {
271         return default_sys_recvfile(fromfd, tofd, offset, count);
272 }
273 #endif
274
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 *****************************************************************/
280
281 ssize_t drain_socket(int sockfd, size_t count)
282 {
283         size_t total = 0;
284         size_t bufsize = MIN(TRANSFER_BUF_SIZE,count);
285         char buffer[bufsize];
286         int old_flags = 0;
287
288         if (count == 0) {
289                 return 0;
290         }
291
292         old_flags = fcntl(sockfd, F_GETFL, 0);
293         if (set_blocking(sockfd, true) == -1) {
294                 return -1;
295         }
296
297         while (total < count) {
298                 ssize_t read_ret;
299                 size_t toread = MIN(bufsize,count - total);
300
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;
307                 }
308                 total += read_ret;
309         }
310
311   out:
312
313         if (fcntl(sockfd, F_SETFL, old_flags) == -1) {
314                 return -1;
315         }
316         return count;
317 }