source: branches/samba-3.5.x/source3/lib/recvfile.c@ 733

Last change on this file since 733 was 733, checked in by Silvan Scherrer, 13 years ago

Samba Server 3.5: update branche to 3.5.13

File size: 6.5 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#ifdef __OS2__
28#define pipe(A) os2_pipe(A)
29#endif
30
31/* Do this on our own in TRANSFER_BUF_SIZE chunks.
32 * It's safe to make direct syscalls to lseek/write here
33 * as we're below the Samba vfs layer.
34 *
35 * Returns -1 on short reads from fromfd (read error)
36 * and sets errno.
37 *
38 * Returns number of bytes written to 'tofd'
39 * return != count then sets errno.
40 * Returns count if complete success.
41 */
42
43#ifndef TRANSFER_BUF_SIZE
44#define TRANSFER_BUF_SIZE (128*1024)
45#endif
46
47static ssize_t default_sys_recvfile(int fromfd,
48 int tofd,
49 SMB_OFF_T offset,
50 size_t count)
51{
52 int saved_errno = 0;
53 size_t total = 0;
54 size_t bufsize = MIN(TRANSFER_BUF_SIZE,count);
55 size_t total_written = 0;
56 char *buffer = NULL;
57
58 DEBUG(10,("default_sys_recvfile: from = %d, to = %d, "
59 "offset=%.0f, count = %lu\n",
60 fromfd, tofd, (double)offset,
61 (unsigned long)count));
62
63 if (count == 0) {
64 return 0;
65 }
66
67 if (tofd != -1 && offset != (SMB_OFF_T)-1) {
68 if (sys_lseek(tofd, offset, SEEK_SET) == -1) {
69 if (errno != ESPIPE) {
70 return -1;
71 }
72 }
73 }
74
75 buffer = SMB_MALLOC_ARRAY(char, bufsize);
76 if (buffer == NULL) {
77 return -1;
78 }
79
80 while (total < count) {
81 size_t num_written = 0;
82 ssize_t read_ret;
83 size_t toread = MIN(bufsize,count - total);
84
85 /* Read from socket - ignore EINTR. */
86 read_ret = sys_read(fromfd, buffer, toread);
87 if (read_ret <= 0) {
88 /* EOF or socket error. */
89 free(buffer);
90 return -1;
91 }
92
93 num_written = 0;
94
95 /* Don't write any more after a write error. */
96 while (tofd != -1 && (num_written < read_ret)) {
97 ssize_t write_ret;
98
99 /* Write to file - ignore EINTR. */
100 write_ret = sys_write(tofd,
101 buffer + num_written,
102 read_ret - num_written);
103
104 if (write_ret <= 0) {
105 /* write error - stop writing. */
106 tofd = -1;
107 if (total_written == 0) {
108 /* Ensure we return
109 -1 if the first
110 write failed. */
111 total_written = -1;
112 }
113 saved_errno = errno;
114 break;
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
143ssize_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 (count) {
217 int saved_errno = errno;
218 if (drain_socket(fromfd, count) != count) {
219 /* socket is dead. */
220 return -1;
221 }
222 errno = saved_errno;
223 }
224
225 return total_written;
226}
227#else
228
229/*****************************************************************
230 No recvfile system call - use the default 128 chunk implementation.
231*****************************************************************/
232
233ssize_t sys_recvfile(int fromfd,
234 int tofd,
235 SMB_OFF_T offset,
236 size_t count)
237{
238 return default_sys_recvfile(fromfd, tofd, offset, count);
239}
240#endif
241
242/*****************************************************************
243 Throw away "count" bytes from the client socket.
244 Returns count or -1 on error.
245*****************************************************************/
246
247ssize_t drain_socket(int sockfd, size_t count)
248{
249 size_t total = 0;
250 size_t bufsize = MIN(TRANSFER_BUF_SIZE,count);
251 char *buffer = NULL;
252
253 if (count == 0) {
254 return 0;
255 }
256
257 buffer = SMB_MALLOC_ARRAY(char, bufsize);
258 if (buffer == NULL) {
259 return -1;
260 }
261
262 while (total < count) {
263 ssize_t read_ret;
264 size_t toread = MIN(bufsize,count - total);
265
266 /* Read from socket - ignore EINTR. */
267 read_ret = sys_read(sockfd, buffer, toread);
268 if (read_ret <= 0) {
269 /* EOF or socket error. */
270 free(buffer);
271 return -1;
272 }
273 total += read_ret;
274 }
275
276 free(buffer);
277 return count;
278}
Note: See TracBrowser for help on using the repository browser.