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

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

Samba Server: updated vendor 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
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
45static ssize_t default_sys_recvfile(int fromfd,
46 int tofd,
47 SMB_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 != (SMB_OFF_T)-1) {
66 if (sys_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
141ssize_t sys_recvfile(int fromfd,
142 int tofd,
143 SMB_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
231ssize_t sys_recvfile(int fromfd,
232 int tofd,
233 SMB_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
245ssize_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}
Note: See TracBrowser for help on using the repository browser.