source: vendor/current/source3/lib/recvfile.c

Last change on this file was 988, checked in by Silvan Scherrer, 9 years ago

Samba Server: update vendor to version 4.4.3

File size: 7.1 KB
Line 
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
46static ssize_t default_sys_recvfile(int fromfd,
47 int tofd,
48 off_t offset,
49 size_t count)
50{
51 int saved_errno = 0;
52 size_t total = 0;
53 size_t bufsize = MIN(TRANSFER_BUF_SIZE,count);
54 size_t total_written = 0;
55 char buffer[bufsize];
56
57 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 if (count == 0) {
63 return 0;
64 }
65
66 if (tofd != -1 && offset != (off_t)-1) {
67 if (lseek(tofd, offset, SEEK_SET) == -1) {
68 if (errno != ESPIPE) {
69 return -1;
70 }
71 }
72 }
73
74 while (total < count) {
75 size_t num_written = 0;
76 ssize_t read_ret;
77 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 do {
85 read_ret = read(fromfd, buffer, toread);
86 } while (read_ret == -1 && errno == EINTR);
87
88 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 if (total_written != 0) {
97 return total_written;
98 }
99 return -1;
100 }
101
102 if (read_ret <= 0) {
103 /* EOF or socket error. */
104 return -1;
105 }
106
107 num_written = 0;
108
109 /* Don't write any more after a write error. */
110 while (tofd != -1 && (num_written < read_ret)) {
111 ssize_t write_ret;
112
113 /* Write to file - ignore EINTR. */
114 write_ret = sys_write(tofd,
115 buffer + num_written,
116 read_ret - num_written);
117
118 if (write_ret <= 0) {
119 /* write error - stop writing. */
120 tofd = -1;
121 if (total_written == 0) {
122 /* Ensure we return
123 -1 if the first
124 write failed. */
125 total_written = -1;
126 }
127 saved_errno = errno;
128 break;
129 }
130
131 num_written += (size_t)write_ret;
132 total_written += (size_t)write_ret;
133 }
134
135 total += read_ret;
136 }
137
138 if (saved_errno) {
139 /* Return the correct write error. */
140 errno = saved_errno;
141 }
142 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
156ssize_t sys_recvfile(int fromfd,
157 int tofd,
158 off_t offset,
159 size_t count)
160{
161 static int pipefd[2] = { -1, -1 };
162 static bool try_splice_call = false;
163 size_t total_written = 0;
164 loff_t splice_offset = offset;
165
166 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 if (count == 0) {
172 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 if (!try_splice_call) {
183 return default_sys_recvfile(fromfd,
184 tofd,
185 offset,
186 count);
187 }
188
189 if ((pipefd[0] == -1) && (pipe(pipefd) == -1)) {
190 try_splice_call = false;
191 return default_sys_recvfile(fromfd, tofd, offset, count);
192 }
193
194 while (count > 0) {
195 int nread, to_write;
196
197 nread = splice(fromfd, NULL, pipefd[1], NULL,
198 MIN(count, 16384), SPLICE_F_MOVE);
199 if (nread == -1) {
200 if (errno == EINTR) {
201 continue;
202 }
203 if (total_written == 0 &&
204 (errno == EBADF || errno == EINVAL)) {
205 try_splice_call = false;
206 return default_sys_recvfile(fromfd, tofd,
207 offset, count);
208 }
209 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 if (total_written != 0) {
218 return total_written;
219 }
220 return -1;
221 }
222 break;
223 }
224
225 to_write = nread;
226 while (to_write > 0) {
227 int thistime;
228 thistime = splice(pipefd[0], NULL, tofd,
229 &splice_offset, to_write,
230 SPLICE_F_MOVE);
231 if (thistime == -1) {
232 goto done;
233 }
234 to_write -= thistime;
235 }
236
237 total_written += nread;
238 count -= nread;
239 }
240
241 done:
242 if (count) {
243 int saved_errno = errno;
244 if (drain_socket(fromfd, count) != count) {
245 /* socket is dead. */
246 return -1;
247 }
248 errno = saved_errno;
249 }
250
251 return total_written;
252}
253#else
254
255/*****************************************************************
256 No recvfile system call - use the default 128 chunk implementation.
257*****************************************************************/
258
259ssize_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
274ssize_t drain_socket(int sockfd, size_t count)
275{
276 size_t total = 0;
277 size_t bufsize = MIN(TRANSFER_BUF_SIZE,count);
278 char buffer[bufsize];
279 int old_flags = 0;
280
281 if (count == 0) {
282 return 0;
283 }
284
285 old_flags = fcntl(sockfd, F_GETFL, 0);
286 if (set_blocking(sockfd, true) == -1) {
287 return -1;
288 }
289
290 while (total < count) {
291 ssize_t read_ret;
292 size_t toread = MIN(bufsize,count - total);
293
294 /* Read from socket - ignore EINTR. */
295 read_ret = sys_read(sockfd, buffer, toread);
296 if (read_ret <= 0) {
297 /* EOF or socket error. */
298 count = (size_t)-1;
299 goto out;
300 }
301 total += read_ret;
302 }
303
304 out:
305
306 if (fcntl(sockfd, F_SETFL, old_flags) == -1) {
307 return -1;
308 }
309 return count;
310}
Note: See TracBrowser for help on using the repository browser.