source: trunk/server/source3/lib/recvfile.c@ 888

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

Samba Server: updated trunk to 3.6.9

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#include "system/filesys.h"
28#ifdef __OS2__
29#define pipe(A) os2_pipe(A)
30#endif
31
32/* Do this on our own in TRANSFER_BUF_SIZE chunks.
33 * It's safe to make direct syscalls to lseek/write here
34 * as we're below the Samba vfs layer.
35 *
36 * Returns -1 on short reads from fromfd (read error)
37 * and sets errno.
38 *
39 * Returns number of bytes written to 'tofd'
40 * return != count then sets errno.
41 * Returns count if complete success.
42 */
43
44#ifndef TRANSFER_BUF_SIZE
45#define TRANSFER_BUF_SIZE (128*1024)
46#endif
47
48static ssize_t default_sys_recvfile(int fromfd,
49 int tofd,
50 SMB_OFF_T offset,
51 size_t count)
52{
53 int saved_errno = 0;
54 size_t total = 0;
55 size_t bufsize = MIN(TRANSFER_BUF_SIZE,count);
56 size_t total_written = 0;
57 char *buffer = NULL;
58
59 DEBUG(10,("default_sys_recvfile: from = %d, to = %d, "
60 "offset=%.0f, count = %lu\n",
61 fromfd, tofd, (double)offset,
62 (unsigned long)count));
63
64 if (count == 0) {
65 return 0;
66 }
67
68 if (tofd != -1 && offset != (SMB_OFF_T)-1) {
69 if (sys_lseek(tofd, offset, SEEK_SET) == -1) {
70 if (errno != ESPIPE) {
71 return -1;
72 }
73 }
74 }
75
76 buffer = SMB_MALLOC_ARRAY(char, bufsize);
77 if (buffer == NULL) {
78 return -1;
79 }
80
81 while (total < count) {
82 size_t num_written = 0;
83 ssize_t read_ret;
84 size_t toread = MIN(bufsize,count - total);
85
86 /* Read from socket - ignore EINTR. */
87 read_ret = sys_read(fromfd, buffer, toread);
88 if (read_ret <= 0) {
89 /* EOF or socket error. */
90 free(buffer);
91 return -1;
92 }
93
94 num_written = 0;
95
96 /* Don't write any more after a write error. */
97 while (tofd != -1 && (num_written < read_ret)) {
98 ssize_t write_ret;
99
100 /* Write to file - ignore EINTR. */
101 write_ret = sys_write(tofd,
102 buffer + num_written,
103 read_ret - num_written);
104
105 if (write_ret <= 0) {
106 /* write error - stop writing. */
107 tofd = -1;
108 if (total_written == 0) {
109 /* Ensure we return
110 -1 if the first
111 write failed. */
112 total_written = -1;
113 }
114 saved_errno = errno;
115 break;
116 }
117
118 num_written += (size_t)write_ret;
119 total_written += (size_t)write_ret;
120 }
121
122 total += read_ret;
123 }
124
125 free(buffer);
126 if (saved_errno) {
127 /* Return the correct write error. */
128 errno = saved_errno;
129 }
130 return (ssize_t)total_written;
131}
132
133#if defined(HAVE_LINUX_SPLICE)
134
135/*
136 * Try and use the Linux system call to do this.
137 * Remember we only return -1 if the socket read
138 * failed. Else we return the number of bytes
139 * actually written. We always read count bytes
140 * from the network in the case of return != -1.
141 */
142
143
144ssize_t sys_recvfile(int fromfd,
145 int tofd,
146 SMB_OFF_T offset,
147 size_t count)
148{
149 static int pipefd[2] = { -1, -1 };
150 static bool try_splice_call = false;
151 size_t total_written = 0;
152 loff_t splice_offset = offset;
153
154 DEBUG(10,("sys_recvfile: from = %d, to = %d, "
155 "offset=%.0f, count = %lu\n",
156 fromfd, tofd, (double)offset,
157 (unsigned long)count));
158
159 if (count == 0) {
160 return 0;
161 }
162
163 /*
164 * Older Linux kernels have splice for sendfile,
165 * but it fails for recvfile. Ensure we only try
166 * this once and always fall back to the userspace
167 * implementation if recvfile splice fails. JRA.
168 */
169
170 if (!try_splice_call) {
171 return default_sys_recvfile(fromfd,
172 tofd,
173 offset,
174 count);
175 }
176
177 if ((pipefd[0] == -1) && (pipe(pipefd) == -1)) {
178 try_splice_call = false;
179 return default_sys_recvfile(fromfd, tofd, offset, count);
180 }
181
182 while (count > 0) {
183 int nread, to_write;
184
185 nread = splice(fromfd, NULL, pipefd[1], NULL,
186 MIN(count, 16384), SPLICE_F_MOVE);
187 if (nread == -1) {
188 if (errno == EINTR) {
189 continue;
190 }
191 if (total_written == 0 &&
192 (errno == EBADF || errno == EINVAL)) {
193 try_splice_call = false;
194 return default_sys_recvfile(fromfd, tofd,
195 offset, count);
196 }
197 break;
198 }
199
200 to_write = nread;
201 while (to_write > 0) {
202 int thistime;
203 thistime = splice(pipefd[0], NULL, tofd,
204 &splice_offset, to_write,
205 SPLICE_F_MOVE);
206 if (thistime == -1) {
207 goto done;
208 }
209 to_write -= thistime;
210 }
211
212 total_written += nread;
213 count -= nread;
214 }
215
216 done:
217 if (count) {
218 int saved_errno = errno;
219 if (drain_socket(fromfd, count) != count) {
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
234ssize_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 Returns count or -1 on error.
246*****************************************************************/
247
248ssize_t drain_socket(int sockfd, size_t count)
249{
250 size_t total = 0;
251 size_t bufsize = MIN(TRANSFER_BUF_SIZE,count);
252 char *buffer = NULL;
253
254 if (count == 0) {
255 return 0;
256 }
257
258 buffer = SMB_MALLOC_ARRAY(char, bufsize);
259 if (buffer == NULL) {
260 return -1;
261 }
262
263 while (total < count) {
264 ssize_t read_ret;
265 size_t toread = MIN(bufsize,count - total);
266
267 /* Read from socket - ignore EINTR. */
268 read_ret = sys_read(sockfd, buffer, toread);
269 if (read_ret <= 0) {
270 /* EOF or socket error. */
271 free(buffer);
272 return -1;
273 }
274 total += read_ret;
275 }
276
277 free(buffer);
278 return count;
279}
Note: See TracBrowser for help on using the repository browser.