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

Last change on this file since 717 was 664, checked in by Silvan Scherrer, 14 years ago

Samba Server 3.5: fixed a missing return, changed os2_socketpair to os2_pipe

File size: 6.0 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 * If tofd is -1 we just drain the incoming socket of count
36 * bytes without writing to the outgoing fd.
37 * If a write fails we do the same (to cope with disk full)
38 * errors.
39 *
40 * Returns -1 on short reads from fromfd (read error)
41 * and sets errno.
42 *
43 * Returns number of bytes written to 'tofd'
44 * or thrown away if 'tofd == -1'.
45 * return != count then sets errno.
46 * Returns count if complete success.
47 */
48
49#ifndef TRANSFER_BUF_SIZE
50#define TRANSFER_BUF_SIZE (128*1024)
51#endif
52
53static ssize_t default_sys_recvfile(int fromfd,
54 int tofd,
55 SMB_OFF_T offset,
56 size_t count)
57{
58 int saved_errno = 0;
59 size_t total = 0;
60 size_t bufsize = MIN(TRANSFER_BUF_SIZE,count);
61 size_t total_written = 0;
62 char *buffer = NULL;
63
64 DEBUG(10,("default_sys_recvfile: from = %d, to = %d, "
65 "offset=%.0f, count = %lu\n",
66 fromfd, tofd, (double)offset,
67 (unsigned long)count));
68
69 if (count == 0) {
70 return 0;
71 }
72
73 if (tofd != -1 && offset != (SMB_OFF_T)-1) {
74 if (sys_lseek(tofd, offset, SEEK_SET) == -1) {
75 if (errno != ESPIPE) {
76 return -1;
77 }
78 }
79 }
80
81 buffer = SMB_MALLOC_ARRAY(char, bufsize);
82 if (buffer == NULL) {
83 return -1;
84 }
85
86 while (total < count) {
87 size_t num_written = 0;
88 ssize_t read_ret;
89 size_t toread = MIN(bufsize,count - total);
90
91 /* Read from socket - ignore EINTR. */
92 read_ret = sys_read(fromfd, buffer, toread);
93 if (read_ret <= 0) {
94 /* EOF or socket error. */
95 free(buffer);
96 return -1;
97 }
98
99 num_written = 0;
100
101 while (num_written < read_ret) {
102 ssize_t write_ret;
103
104 if (tofd == -1) {
105 write_ret = read_ret;
106 } else {
107 /* Write to file - ignore EINTR. */
108 write_ret = sys_write(tofd,
109 buffer + num_written,
110 read_ret - num_written);
111
112 if (write_ret <= 0) {
113 /* write error - stop writing. */
114 tofd = -1;
115 saved_errno = errno;
116 continue;
117 }
118 }
119
120 num_written += (size_t)write_ret;
121 total_written += (size_t)write_ret;
122 }
123
124 total += read_ret;
125 }
126
127 free(buffer);
128 if (saved_errno) {
129 /* Return the correct write error. */
130 errno = saved_errno;
131 }
132 return (ssize_t)total_written;
133}
134
135#if defined(HAVE_LINUX_SPLICE)
136
137/*
138 * Try and use the Linux system call to do this.
139 * Remember we only return -1 if the socket read
140 * failed. Else we return the number of bytes
141 * actually written. We always read count bytes
142 * from the network in the case of return != -1.
143 */
144
145
146ssize_t sys_recvfile(int fromfd,
147 int tofd,
148 SMB_OFF_T offset,
149 size_t count)
150{
151 static int pipefd[2] = { -1, -1 };
152 static bool try_splice_call = false;
153 size_t total_written = 0;
154 loff_t splice_offset = offset;
155
156 DEBUG(10,("sys_recvfile: from = %d, to = %d, "
157 "offset=%.0f, count = %lu\n",
158 fromfd, tofd, (double)offset,
159 (unsigned long)count));
160
161 if (count == 0) {
162 return 0;
163 }
164
165 /*
166 * Older Linux kernels have splice for sendfile,
167 * but it fails for recvfile. Ensure we only try
168 * this once and always fall back to the userspace
169 * implementation if recvfile splice fails. JRA.
170 */
171
172 if (!try_splice_call) {
173 return default_sys_recvfile(fromfd,
174 tofd,
175 offset,
176 count);
177 }
178
179 if ((pipefd[0] == -1) && (pipe(pipefd) == -1)) {
180 try_splice_call = false;
181 return default_sys_recvfile(fromfd, tofd, offset, count);
182 }
183
184 while (count > 0) {
185 int nread, to_write;
186
187 nread = splice(fromfd, NULL, pipefd[1], NULL,
188 MIN(count, 16384), SPLICE_F_MOVE);
189 if (nread == -1) {
190 if (errno == EINTR) {
191 continue;
192 }
193 if (total_written == 0 &&
194 (errno == EBADF || errno == EINVAL)) {
195 try_splice_call = false;
196 return default_sys_recvfile(fromfd, tofd,
197 offset, count);
198 }
199 break;
200 }
201
202 to_write = nread;
203 while (to_write > 0) {
204 int thistime;
205 thistime = splice(pipefd[0], NULL, tofd,
206 &splice_offset, to_write,
207 SPLICE_F_MOVE);
208 if (thistime == -1) {
209 goto done;
210 }
211 to_write -= thistime;
212 }
213
214 total_written += nread;
215 count -= nread;
216 }
217
218 done:
219 if (total_written < count) {
220 int saved_errno = errno;
221 if (drain_socket(fromfd, count-total_written) !=
222 count-total_written) {
223 /* socket is dead. */
224 return -1;
225 }
226 errno = saved_errno;
227 }
228
229 return total_written;
230}
231#else
232
233/*****************************************************************
234 No recvfile system call - use the default 128 chunk implementation.
235*****************************************************************/
236
237ssize_t sys_recvfile(int fromfd,
238 int tofd,
239 SMB_OFF_T offset,
240 size_t count)
241{
242 return default_sys_recvfile(fromfd, tofd, offset, count);
243}
244#endif
245
246/*****************************************************************
247 Throw away "count" bytes from the client socket.
248*****************************************************************/
249
250ssize_t drain_socket(int sockfd, size_t count)
251{
252 return default_sys_recvfile(sockfd, -1, (SMB_OFF_T)-1, count);
253}
Note: See TracBrowser for help on using the repository browser.