Line data Source code
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 : #include "lib/util/sys_rw.h"
29 :
30 : /* Do this on our own in TRANSFER_BUF_SIZE chunks.
31 : * It's safe to make direct syscalls to lseek/write here
32 : * as we're below the Samba vfs layer.
33 : *
34 : * Returns -1 on short reads from fromfd (read error)
35 : * and sets errno.
36 : *
37 : * Returns number of bytes written to 'tofd'
38 : * return != count then sets errno.
39 : * Returns count if complete success.
40 : */
41 :
42 : #ifndef TRANSFER_BUF_SIZE
43 : #define TRANSFER_BUF_SIZE (128*1024)
44 : #endif
45 :
46 0 : static ssize_t default_sys_recvfile(int fromfd,
47 : int tofd,
48 : off_t offset,
49 : size_t count)
50 0 : {
51 0 : int saved_errno = 0;
52 0 : size_t total = 0;
53 0 : size_t bufsize = MIN(TRANSFER_BUF_SIZE,count);
54 0 : size_t total_written = 0;
55 0 : char buffer[bufsize];
56 :
57 0 : DEBUG(10,("default_sys_recvfile: from = %d, to = %d, "
58 : "offset=%.0f, count = %lu\n",
59 : fromfd, tofd, (double)offset,
60 : (unsigned long)count));
61 :
62 0 : if (count == 0) {
63 0 : return 0;
64 : }
65 :
66 0 : if (tofd != -1 && offset != (off_t)-1) {
67 0 : if (lseek(tofd, offset, SEEK_SET) == -1) {
68 0 : if (errno != ESPIPE) {
69 0 : return -1;
70 : }
71 : }
72 : }
73 :
74 0 : while (total < count) {
75 0 : size_t num_written = 0;
76 0 : ssize_t read_ret;
77 0 : size_t toread = MIN(bufsize,count - total);
78 :
79 : /*
80 : * Read from socket - ignore EINTR.
81 : * Can't use sys_read() as that also
82 : * ignores EAGAIN and EWOULDBLOCK.
83 : */
84 0 : do {
85 0 : read_ret = read(fromfd, buffer, toread);
86 0 : } while (read_ret == -1 && errno == EINTR);
87 :
88 0 : if (read_ret == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
89 : /*
90 : * fromfd socket is in non-blocking mode.
91 : * If we already read some and wrote
92 : * it successfully, return that.
93 : * Only return -1 if this is the first read
94 : * attempt. Caller will handle both cases.
95 : */
96 0 : if (total_written != 0) {
97 0 : return total_written;
98 : }
99 0 : return -1;
100 : }
101 :
102 0 : if (read_ret <= 0) {
103 : /* EOF or socket error. */
104 0 : return -1;
105 : }
106 :
107 0 : num_written = 0;
108 :
109 : /* Don't write any more after a write error. */
110 0 : while (tofd != -1 && (num_written < read_ret)) {
111 0 : ssize_t write_ret;
112 :
113 : /* Write to file - ignore EINTR. */
114 0 : write_ret = sys_write(tofd,
115 : buffer + num_written,
116 : read_ret - num_written);
117 :
118 0 : if (write_ret <= 0) {
119 : /* write error - stop writing. */
120 0 : tofd = -1;
121 0 : if (total_written == 0) {
122 : /* Ensure we return
123 : -1 if the first
124 : write failed. */
125 0 : total_written = -1;
126 : }
127 0 : saved_errno = errno;
128 0 : break;
129 : }
130 :
131 0 : num_written += (size_t)write_ret;
132 0 : total_written += (size_t)write_ret;
133 : }
134 :
135 0 : total += read_ret;
136 : }
137 :
138 0 : if (saved_errno) {
139 : /* Return the correct write error. */
140 0 : errno = saved_errno;
141 : }
142 0 : return (ssize_t)total_written;
143 : }
144 :
145 : #if defined(HAVE_LINUX_SPLICE)
146 :
147 : /*
148 : * Try and use the Linux system call to do this.
149 : * Remember we only return -1 if the socket read
150 : * failed. Else we return the number of bytes
151 : * actually written. We always read count bytes
152 : * from the network in the case of return != -1.
153 : */
154 :
155 :
156 0 : ssize_t sys_recvfile(int fromfd,
157 : int tofd,
158 : off_t offset,
159 : size_t count)
160 : {
161 0 : static int pipefd[2] = { -1, -1 };
162 0 : static bool try_splice_call = false;
163 0 : size_t total_written = 0;
164 0 : loff_t splice_offset = offset;
165 :
166 0 : DEBUG(10,("sys_recvfile: from = %d, to = %d, "
167 : "offset=%.0f, count = %lu\n",
168 : fromfd, tofd, (double)offset,
169 : (unsigned long)count));
170 :
171 0 : if (count == 0) {
172 0 : return 0;
173 : }
174 :
175 : /*
176 : * Older Linux kernels have splice for sendfile,
177 : * but it fails for recvfile. Ensure we only try
178 : * this once and always fall back to the userspace
179 : * implementation if recvfile splice fails. JRA.
180 : */
181 :
182 0 : if (!try_splice_call) {
183 0 : return default_sys_recvfile(fromfd,
184 : tofd,
185 : offset,
186 : count);
187 : }
188 :
189 0 : if ((pipefd[0] == -1) && (pipe(pipefd) == -1)) {
190 0 : try_splice_call = false;
191 0 : return default_sys_recvfile(fromfd, tofd, offset, count);
192 : }
193 :
194 0 : while (count > 0) {
195 0 : int nread, to_write;
196 :
197 0 : nread = splice(fromfd, NULL, pipefd[1], NULL,
198 : MIN(count, 16384), SPLICE_F_MOVE);
199 0 : if (nread == -1) {
200 0 : if (errno == EINTR) {
201 0 : continue;
202 : }
203 0 : if (total_written == 0 &&
204 0 : (errno == EBADF || errno == EINVAL)) {
205 0 : try_splice_call = false;
206 0 : return default_sys_recvfile(fromfd, tofd,
207 : offset, count);
208 : }
209 0 : if (errno == EAGAIN || errno == EWOULDBLOCK) {
210 : /*
211 : * fromfd socket is in non-blocking mode.
212 : * If we already read some and wrote
213 : * it successfully, return that.
214 : * Only return -1 if this is the first read
215 : * attempt. Caller will handle both cases.
216 : */
217 0 : if (total_written != 0) {
218 0 : return total_written;
219 : }
220 0 : return -1;
221 : }
222 0 : break;
223 : }
224 :
225 0 : to_write = nread;
226 0 : while (to_write > 0) {
227 0 : int thistime;
228 0 : thistime = splice(pipefd[0], NULL, tofd,
229 : &splice_offset, to_write,
230 : SPLICE_F_MOVE);
231 0 : if (thistime == -1) {
232 0 : goto done;
233 : }
234 0 : to_write -= thistime;
235 : }
236 :
237 0 : total_written += nread;
238 0 : count -= nread;
239 : }
240 :
241 0 : done:
242 0 : if (count) {
243 0 : int saved_errno = errno;
244 0 : if (drain_socket(fromfd, count) != count) {
245 : /* socket is dead. */
246 0 : return -1;
247 : }
248 0 : errno = saved_errno;
249 : }
250 :
251 0 : return total_written;
252 : }
253 : #else
254 :
255 : /*****************************************************************
256 : No recvfile system call - use the default 128 chunk implementation.
257 : *****************************************************************/
258 :
259 : ssize_t sys_recvfile(int fromfd,
260 : int tofd,
261 : off_t offset,
262 : size_t count)
263 : {
264 : return default_sys_recvfile(fromfd, tofd, offset, count);
265 : }
266 : #endif
267 :
268 : /*****************************************************************
269 : Throw away "count" bytes from the client socket.
270 : Returns count or -1 on error.
271 : Must only operate on a blocking socket.
272 : *****************************************************************/
273 :
274 0 : ssize_t drain_socket(int sockfd, size_t count)
275 0 : {
276 0 : size_t total = 0;
277 0 : size_t bufsize = MIN(TRANSFER_BUF_SIZE,count);
278 0 : char buffer[bufsize];
279 0 : int old_flags = 0;
280 :
281 0 : if (count == 0) {
282 0 : return 0;
283 : }
284 :
285 0 : old_flags = fcntl(sockfd, F_GETFL, 0);
286 0 : if (set_blocking(sockfd, true) == -1) {
287 0 : return -1;
288 : }
289 :
290 0 : while (total < count) {
291 0 : ssize_t read_ret;
292 0 : size_t toread = MIN(bufsize,count - total);
293 :
294 : /* Read from socket - ignore EINTR. */
295 0 : read_ret = sys_read(sockfd, buffer, toread);
296 0 : if (read_ret <= 0) {
297 : /* EOF or socket error. */
298 0 : count = (size_t)-1;
299 0 : goto out;
300 : }
301 0 : total += read_ret;
302 : }
303 :
304 0 : out:
305 :
306 0 : if (fcntl(sockfd, F_SETFL, old_flags) == -1) {
307 0 : return -1;
308 : }
309 0 : return count;
310 : }
|