v3-4-ctdb: Bump ctdb vendor patch level to 17
[obnox/samba-ctdb.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
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.
31  *
32  * If tofd is -1 we just drain the incoming socket of count
33  * bytes without writing to the outgoing fd.
34  * If a write fails we do the same (to cope with disk full)
35  * errors.
36  *
37  * Returns -1 on short reads from fromfd (read error)
38  * and sets errno.
39  *
40  * Returns number of bytes written to 'tofd'
41  * or thrown away if 'tofd == -1'.
42  * return != count then sets errno.
43  * Returns count if complete success.
44  */
45
46 #ifndef TRANSFER_BUF_SIZE
47 #define TRANSFER_BUF_SIZE (128*1024)
48 #endif
49
50 static ssize_t default_sys_recvfile(int fromfd,
51                         int tofd,
52                         SMB_OFF_T offset,
53                         size_t count)
54 {
55         int saved_errno = 0;
56         size_t total = 0;
57         size_t bufsize = MIN(TRANSFER_BUF_SIZE,count);
58         size_t total_written = 0;
59         char *buffer = NULL;
60
61         DEBUG(10,("default_sys_recvfile: from = %d, to = %d, "
62                 "offset=%.0f, count = %lu\n",
63                 fromfd, tofd, (double)offset,
64                 (unsigned long)count));
65
66         if (count == 0) {
67                 return 0;
68         }
69
70         if (tofd != -1 && offset != (SMB_OFF_T)-1) {
71                 if (sys_lseek(tofd, offset, SEEK_SET) == -1) {
72                         if (errno != ESPIPE) {
73                                 return -1;
74                         }
75                 }
76         }
77
78         buffer = SMB_MALLOC_ARRAY(char, bufsize);
79         if (buffer == NULL) {
80                 return -1;
81         }
82
83         while (total < count) {
84                 size_t num_written = 0;
85                 ssize_t read_ret;
86                 size_t toread = MIN(bufsize,count - total);
87
88                 /* Read from socket - ignore EINTR. */
89                 read_ret = sys_read(fromfd, buffer, toread);
90                 if (read_ret <= 0) {
91                         /* EOF or socket error. */
92                         free(buffer);
93                         return -1;
94                 }
95
96                 num_written = 0;
97
98                 while (num_written < read_ret) {
99                         ssize_t write_ret;
100
101                         if (tofd == -1) {
102                                 write_ret = read_ret;
103                         } else {
104                                 /* Write to file - ignore EINTR. */
105                                 write_ret = sys_write(tofd,
106                                                 buffer + num_written,
107                                                 read_ret - num_written);
108
109                                 if (write_ret <= 0) {
110                                         /* write error - stop writing. */
111                                         tofd = -1;
112                                         saved_errno = errno;
113                                         continue;
114                                 }
115                         }
116
117                         num_written += (size_t)write_ret;
118                         total_written += (size_t)write_ret;
119                 }
120
121                 total += read_ret;
122         }
123
124         free(buffer);
125         if (saved_errno) {
126                 /* Return the correct write error. */
127                 errno = saved_errno;
128         }
129         return (ssize_t)total_written;
130 }
131
132 #if defined(HAVE_LINUX_SPLICE)
133
134 /*
135  * Try and use the Linux system call to do this.
136  * Remember we only return -1 if the socket read
137  * failed. Else we return the number of bytes
138  * actually written. We always read count bytes
139  * from the network in the case of return != -1.
140  */
141
142
143 ssize_t sys_recvfile(int fromfd,
144                         int tofd,
145                         SMB_OFF_T offset,
146                         size_t count)
147 {
148         static int pipefd[2] = { -1, -1 };
149         static bool try_splice_call = false;
150         size_t total_written = 0;
151         loff_t splice_offset = offset;
152
153         DEBUG(10,("sys_recvfile: from = %d, to = %d, "
154                 "offset=%.0f, count = %lu\n",
155                 fromfd, tofd, (double)offset,
156                 (unsigned long)count));
157
158         if (count == 0) {
159                 return 0;
160         }
161
162         /*
163          * Older Linux kernels have splice for sendfile,
164          * but it fails for recvfile. Ensure we only try
165          * this once and always fall back to the userspace
166          * implementation if recvfile splice fails. JRA.
167          */
168
169         if (!try_splice_call) {
170                 return default_sys_recvfile(fromfd,
171                                 tofd,
172                                 offset,
173                                 count);
174         }
175
176         if ((pipefd[0] == -1) && (pipe(pipefd) == -1)) {
177                 try_splice_call = false;
178                 return default_sys_recvfile(fromfd, tofd, offset, count);
179         }
180
181         while (count > 0) {
182                 int nread, to_write;
183
184                 nread = splice(fromfd, NULL, pipefd[1], NULL,
185                                MIN(count, 16384), SPLICE_F_MOVE);
186                 if (nread == -1) {
187                         if (errno == EINTR) {
188                                 continue;
189                         }
190                         if (total_written == 0 &&
191                             (errno == EBADF || errno == EINVAL)) {
192                                 try_splice_call = false;
193                                 return default_sys_recvfile(fromfd, tofd,
194                                                             offset, count);
195                         }
196                         break;
197                 }
198
199                 to_write = nread;
200                 while (to_write > 0) {
201                         int thistime;
202                         thistime = splice(pipefd[0], NULL, tofd,
203                                           &splice_offset, to_write,
204                                           SPLICE_F_MOVE);
205                         if (thistime == -1) {
206                                 goto done;
207                         }
208                         to_write -= thistime;
209                 }
210
211                 total_written += nread;
212                 count -= nread;
213         }
214
215  done:
216         if (total_written < count) {
217                 int saved_errno = errno;
218                 if (drain_socket(fromfd, count-total_written) !=
219                                 count-total_written) {
220                         /* socket is dead. */
221                         return -1;
222                 }
223                 errno = saved_errno;
224         }
225
226         return total_written;
227 }
228 #else
229
230 /*****************************************************************
231  No recvfile system call - use the default 128 chunk implementation.
232 *****************************************************************/
233
234 ssize_t sys_recvfile(int fromfd,
235                         int tofd,
236                         SMB_OFF_T offset,
237                         size_t count)
238 {
239         return default_sys_recvfile(fromfd, tofd, offset, count);
240 }
241 #endif
242
243 /*****************************************************************
244  Throw away "count" bytes from the client socket.
245 *****************************************************************/
246
247 ssize_t drain_socket(int sockfd, size_t count)
248 {
249         return default_sys_recvfile(sockfd, -1, (SMB_OFF_T)-1, count);
250 }