s3: Remove db_ctdb_fetch
[kai/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 = NULL;
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         buffer = SMB_MALLOC_ARRAY(char, bufsize);
74         if (buffer == NULL) {
75                 return -1;
76         }
77
78         while (total < count) {
79                 size_t num_written = 0;
80                 ssize_t read_ret;
81                 size_t toread = MIN(bufsize,count - total);
82
83                 /* Read from socket - ignore EINTR. */
84                 read_ret = sys_read(fromfd, buffer, toread);
85                 if (read_ret <= 0) {
86                         /* EOF or socket error. */
87                         free(buffer);
88                         return -1;
89                 }
90
91                 num_written = 0;
92
93                 /* Don't write any more after a write error. */
94                 while (tofd != -1 && (num_written < read_ret)) {
95                         ssize_t write_ret;
96
97                         /* Write to file - ignore EINTR. */
98                         write_ret = sys_write(tofd,
99                                         buffer + num_written,
100                                         read_ret - num_written);
101
102                         if (write_ret <= 0) {
103                                 /* write error - stop writing. */
104                                 tofd = -1;
105                                 if (total_written == 0) {
106                                         /* Ensure we return
107                                            -1 if the first
108                                            write failed. */
109                                         total_written = -1;
110                                 }
111                                 saved_errno = errno;
112                                 break;
113                         }
114
115                         num_written += (size_t)write_ret;
116                         total_written += (size_t)write_ret;
117                 }
118
119                 total += read_ret;
120         }
121
122         free(buffer);
123         if (saved_errno) {
124                 /* Return the correct write error. */
125                 errno = saved_errno;
126         }
127         return (ssize_t)total_written;
128 }
129
130 #if defined(HAVE_LINUX_SPLICE)
131
132 /*
133  * Try and use the Linux system call to do this.
134  * Remember we only return -1 if the socket read
135  * failed. Else we return the number of bytes
136  * actually written. We always read count bytes
137  * from the network in the case of return != -1.
138  */
139
140
141 ssize_t sys_recvfile(int fromfd,
142                         int tofd,
143                         off_t offset,
144                         size_t count)
145 {
146         static int pipefd[2] = { -1, -1 };
147         static bool try_splice_call = false;
148         size_t total_written = 0;
149         loff_t splice_offset = offset;
150
151         DEBUG(10,("sys_recvfile: from = %d, to = %d, "
152                 "offset=%.0f, count = %lu\n",
153                 fromfd, tofd, (double)offset,
154                 (unsigned long)count));
155
156         if (count == 0) {
157                 return 0;
158         }
159
160         /*
161          * Older Linux kernels have splice for sendfile,
162          * but it fails for recvfile. Ensure we only try
163          * this once and always fall back to the userspace
164          * implementation if recvfile splice fails. JRA.
165          */
166
167         if (!try_splice_call) {
168                 return default_sys_recvfile(fromfd,
169                                 tofd,
170                                 offset,
171                                 count);
172         }
173
174         if ((pipefd[0] == -1) && (pipe(pipefd) == -1)) {
175                 try_splice_call = false;
176                 return default_sys_recvfile(fromfd, tofd, offset, count);
177         }
178
179         while (count > 0) {
180                 int nread, to_write;
181
182                 nread = splice(fromfd, NULL, pipefd[1], NULL,
183                                MIN(count, 16384), SPLICE_F_MOVE);
184                 if (nread == -1) {
185                         if (errno == EINTR) {
186                                 continue;
187                         }
188                         if (total_written == 0 &&
189                             (errno == EBADF || errno == EINVAL)) {
190                                 try_splice_call = false;
191                                 return default_sys_recvfile(fromfd, tofd,
192                                                             offset, count);
193                         }
194                         break;
195                 }
196
197                 to_write = nread;
198                 while (to_write > 0) {
199                         int thistime;
200                         thistime = splice(pipefd[0], NULL, tofd,
201                                           &splice_offset, to_write,
202                                           SPLICE_F_MOVE);
203                         if (thistime == -1) {
204                                 goto done;
205                         }
206                         to_write -= thistime;
207                 }
208
209                 total_written += nread;
210                 count -= nread;
211         }
212
213  done:
214         if (count) {
215                 int saved_errno = errno;
216                 if (drain_socket(fromfd, count) != count) {
217                         /* socket is dead. */
218                         return -1;
219                 }
220                 errno = saved_errno;
221         }
222
223         return total_written;
224 }
225 #else
226
227 /*****************************************************************
228  No recvfile system call - use the default 128 chunk implementation.
229 *****************************************************************/
230
231 ssize_t sys_recvfile(int fromfd,
232                         int tofd,
233                         off_t offset,
234                         size_t count)
235 {
236         return default_sys_recvfile(fromfd, tofd, offset, count);
237 }
238 #endif
239
240 /*****************************************************************
241  Throw away "count" bytes from the client socket.
242  Returns count or -1 on error.
243 *****************************************************************/
244
245 ssize_t drain_socket(int sockfd, size_t count)
246 {
247         size_t total = 0;
248         size_t bufsize = MIN(TRANSFER_BUF_SIZE,count);
249         char *buffer = NULL;
250
251         if (count == 0) {
252                 return 0;
253         }
254
255         buffer = SMB_MALLOC_ARRAY(char, bufsize);
256         if (buffer == NULL) {
257                 return -1;
258         }
259
260         while (total < count) {
261                 ssize_t read_ret;
262                 size_t toread = MIN(bufsize,count - total);
263
264                 /* Read from socket - ignore EINTR. */
265                 read_ret = sys_read(sockfd, buffer, toread);
266                 if (read_ret <= 0) {
267                         /* EOF or socket error. */
268                         free(buffer);
269                         return -1;
270                 }
271                 total += read_ret;
272         }
273
274         free(buffer);
275         return count;
276 }