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

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

Samba Server: updated trunk to 3.6.0

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