source: branches/samba-3.3.x/source/lib/recvfile.c@ 206

Last change on this file since 206 was 206, checked in by Herwig Bauernfeind, 16 years ago

Import Samba 3.3 branch at 3.0.0 level (psmedley's port)

File size: 5.3 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
28/* Do this on our own in TRANSFER_BUF_SIZE chunks.
29 * It's safe to make direct syscalls to lseek/write here
30 * as we're below the Samba vfs layer.
31 *
32 * If tofd is -1 we just drain the incoming socket of count
33 * bytes without writing to the outgoing fd.
34 * If a write fails we do the same (to cope with disk full)
35 * errors.
36 *
37 * Returns -1 on short reads from fromfd (read error)
38 * and sets errno.
39 *
40 * Returns number of bytes written to 'tofd'
41 * or thrown away if 'tofd == -1'.
42 * return != count then sets errno.
43 * Returns count if complete success.
44 */
45
46#ifndef TRANSFER_BUF_SIZE
47#define TRANSFER_BUF_SIZE (128*1024)
48#endif
49
50static ssize_t default_sys_recvfile(int fromfd,
51 int tofd,
52 SMB_OFF_T offset,
53 size_t count)
54{
55 int saved_errno = 0;
56 size_t total = 0;
57 size_t bufsize = MIN(TRANSFER_BUF_SIZE,count);
58 size_t total_written = 0;
59 char *buffer = NULL;
60
61 DEBUG(10,("default_sys_recvfile: from = %d, to = %d, "
62 "offset=%.0f, count = %lu\n",
63 fromfd, tofd, (double)offset,
64 (unsigned long)count));
65
66 if (count == 0) {
67 return 0;
68 }
69
70 if (tofd != -1 && offset != (SMB_OFF_T)-1) {
71 if (sys_lseek(tofd, offset, SEEK_SET) == -1) {
72 if (errno != ESPIPE) {
73 return -1;
74 }
75 }
76 }
77
78 buffer = SMB_MALLOC_ARRAY(char, bufsize);
79 if (buffer == NULL) {
80 return -1;
81 }
82
83 while (total < count) {
84 size_t num_written = 0;
85 ssize_t read_ret;
86 size_t toread = MIN(bufsize,count - total);
87
88 /* Read from socket - ignore EINTR. */
89 read_ret = sys_read(fromfd, buffer, toread);
90 if (read_ret <= 0) {
91 /* EOF or socket error. */
92 free(buffer);
93 return -1;
94 }
95
96 num_written = 0;
97
98 while (num_written < read_ret) {
99 ssize_t write_ret;
100
101 if (tofd == -1) {
102 write_ret = read_ret;
103 } else {
104 /* Write to file - ignore EINTR. */
105 write_ret = sys_write(tofd,
106 buffer + num_written,
107 read_ret - num_written);
108
109 if (write_ret <= 0) {
110 /* write error - stop writing. */
111 tofd = -1;
112 saved_errno = errno;
113 continue;
114 }
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 bool try_splice_call = true;
149 size_t total_written = 0;
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 while (total_written < count) {
175 ssize_t ret = splice(fromfd,
176 NULL,
177 tofd,
178 &offset,
179 count,
180 0);
181 if (ret == -1) {
182 if (errno != EINTR) {
183 if (total_written == 0 &&
184 (errno == EBADF || errno == EINVAL)) {
185 try_splice_call = false;
186 return default_sys_recvfile(fromfd,
187 tofd,
188 offset,
189 count);
190 }
191 break;
192 }
193 continue;
194 }
195 total_written += ret;
196 count -= ret;
197 }
198
199 if (total_written < count) {
200 int saved_errno = errno;
201 if (drain_socket(fromfd, count-total_written) !=
202 count-total_written) {
203 /* socket is dead. */
204 return -1;
205 }
206 errno = saved_errno;
207 }
208
209 return total_written;
210}
211#else
212
213/*****************************************************************
214 No recvfile system call - use the default 128 chunk implementation.
215*****************************************************************/
216
217ssize_t sys_recvfile(int fromfd,
218 int tofd,
219 SMB_OFF_T offset,
220 size_t count)
221{
222 return default_sys_recvfile(fromfd, tofd, offset, count);
223}
224#endif
225
226/*****************************************************************
227 Throw away "count" bytes from the client socket.
228*****************************************************************/
229
230ssize_t drain_socket(int sockfd, size_t count)
231{
232 return default_sys_recvfile(sockfd, -1, (SMB_OFF_T)-1, count);
233}
Note: See TracBrowser for help on using the repository browser.