source: branches/samba-3.3.x/source/lib/sendfile.c@ 435

Last change on this file since 435 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: 11.4 KB
Line 
1/*
2 Unix SMB/Netbios implementation.
3 Version 2.2.x / 3.0.x
4 sendfile implementations.
5 Copyright (C) Jeremy Allison 2002.
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 sendfile 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#if defined(LINUX_SENDFILE_API)
29
30#include <sys/sendfile.h>
31
32#ifndef MSG_MORE
33#define MSG_MORE 0x8000
34#endif
35
36ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
37{
38 size_t total=0;
39 ssize_t ret;
40 size_t hdr_len = 0;
41
42 /*
43 * Send the header first.
44 * Use MSG_MORE to cork the TCP output until sendfile is called.
45 */
46
47 if (header) {
48 hdr_len = header->length;
49 while (total < hdr_len) {
50 ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE);
51 if (ret == -1)
52 return -1;
53 total += ret;
54 }
55 }
56
57 total = count;
58 while (total) {
59 ssize_t nwritten;
60 do {
61#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILE64)
62 nwritten = sendfile64(tofd, fromfd, &offset, total);
63#else
64 nwritten = sendfile(tofd, fromfd, &offset, total);
65#endif
66 } while (nwritten == -1 && errno == EINTR);
67 if (nwritten == -1) {
68 if (errno == ENOSYS || errno == EINVAL) {
69 /* Ok - we're in a world of pain here. We just sent
70 * the header, but the sendfile failed. We have to
71 * emulate the sendfile at an upper layer before we
72 * disable it's use. So we do something really ugly.
73 * We set the errno to a strange value so we can detect
74 * this at the upper level and take care of it without
75 * layer violation. JRA.
76 */
77 errno = EINTR; /* Normally we can never return this. */
78 }
79 return -1;
80 }
81 if (nwritten == 0)
82 return -1; /* I think we're at EOF here... */
83 total -= nwritten;
84 }
85 return count + hdr_len;
86}
87
88#elif defined(LINUX_BROKEN_SENDFILE_API)
89
90/*
91 * We must use explicit 32 bit types here. This code path means Linux
92 * won't do proper 64-bit sendfile. JRA.
93 */
94
95extern int32 sendfile (int out_fd, int in_fd, int32 *offset, uint32 count);
96
97
98#ifndef MSG_MORE
99#define MSG_MORE 0x8000
100#endif
101
102ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
103{
104 size_t total=0;
105 ssize_t ret;
106 ssize_t hdr_len = 0;
107 uint32 small_total = 0;
108 int32 small_offset;
109
110 /*
111 * Fix for broken Linux 2.4 systems with no working sendfile64().
112 * If the offset+count > 2 GB then pretend we don't have the
113 * system call sendfile at all. The upper layer catches this
114 * and uses a normal read. JRA.
115 */
116
117 if ((sizeof(SMB_OFF_T) >= 8) && (offset + count > (SMB_OFF_T)0x7FFFFFFF)) {
118 errno = ENOSYS;
119 return -1;
120 }
121
122 /*
123 * Send the header first.
124 * Use MSG_MORE to cork the TCP output until sendfile is called.
125 */
126
127 if (header) {
128 hdr_len = header->length;
129 while (total < hdr_len) {
130 ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE);
131 if (ret == -1)
132 return -1;
133 total += ret;
134 }
135 }
136
137 small_total = (uint32)count;
138 small_offset = (int32)offset;
139
140 while (small_total) {
141 int32 nwritten;
142 do {
143 nwritten = sendfile(tofd, fromfd, &small_offset, small_total);
144 } while (nwritten == -1 && errno == EINTR);
145 if (nwritten == -1) {
146 if (errno == ENOSYS || errno == EINVAL) {
147 /* Ok - we're in a world of pain here. We just sent
148 * the header, but the sendfile failed. We have to
149 * emulate the sendfile at an upper layer before we
150 * disable it's use. So we do something really ugly.
151 * We set the errno to a strange value so we can detect
152 * this at the upper level and take care of it without
153 * layer violation. JRA.
154 */
155 errno = EINTR; /* Normally we can never return this. */
156 }
157 return -1;
158 }
159 if (nwritten == 0)
160 return -1; /* I think we're at EOF here... */
161 small_total -= nwritten;
162 }
163 return count + hdr_len;
164}
165
166
167#elif defined(SOLARIS_SENDFILE_API)
168
169/*
170 * Solaris sendfile code written by Pierre Belanger <belanger@pobox.com>.
171 */
172
173#include <sys/sendfile.h>
174
175ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
176{
177 int sfvcnt;
178 size_t total, xferred;
179 struct sendfilevec vec[2];
180 ssize_t hdr_len = 0;
181
182 if (header) {
183 sfvcnt = 2;
184
185 vec[0].sfv_fd = SFV_FD_SELF;
186 vec[0].sfv_flag = 0;
187 vec[0].sfv_off = (off_t)header->data;
188 vec[0].sfv_len = hdr_len = header->length;
189
190 vec[1].sfv_fd = fromfd;
191 vec[1].sfv_flag = 0;
192 vec[1].sfv_off = offset;
193 vec[1].sfv_len = count;
194
195 } else {
196 sfvcnt = 1;
197
198 vec[0].sfv_fd = fromfd;
199 vec[0].sfv_flag = 0;
200 vec[0].sfv_off = offset;
201 vec[0].sfv_len = count;
202 }
203
204 total = count + hdr_len;
205
206 while (total) {
207 ssize_t nwritten;
208
209 /*
210 * Although not listed in the API error returns, this is almost certainly
211 * a slow system call and will be interrupted by a signal with EINTR. JRA.
212 */
213
214 xferred = 0;
215
216#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILEV64)
217 nwritten = sendfilev64(tofd, vec, sfvcnt, &xferred);
218#else
219 nwritten = sendfilev(tofd, vec, sfvcnt, &xferred);
220#endif
221 if (nwritten == -1 && errno == EINTR) {
222 if (xferred == 0)
223 continue; /* Nothing written yet. */
224 else
225 nwritten = xferred;
226 }
227
228 if (nwritten == -1)
229 return -1;
230 if (nwritten == 0)
231 return -1; /* I think we're at EOF here... */
232
233 /*
234 * If this was a short (signal interrupted) write we may need
235 * to subtract it from the header data, or null out the header
236 * data altogether if we wrote more than vec[0].sfv_len bytes.
237 * We move vec[1].* to vec[0].* and set sfvcnt to 1
238 */
239
240 if (sfvcnt == 2 && nwritten >= vec[0].sfv_len) {
241 vec[1].sfv_off += nwritten - vec[0].sfv_len;
242 vec[1].sfv_len -= nwritten - vec[0].sfv_len;
243
244 /* Move vec[1].* to vec[0].* and set sfvcnt to 1 */
245 vec[0] = vec[1];
246 sfvcnt = 1;
247 } else {
248 vec[0].sfv_off += nwritten;
249 vec[0].sfv_len -= nwritten;
250 }
251 total -= nwritten;
252 }
253 return count + hdr_len;
254}
255
256#elif defined(HPUX_SENDFILE_API)
257
258#include <sys/socket.h>
259#include <sys/uio.h>
260
261ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
262{
263 size_t total=0;
264 struct iovec hdtrl[2];
265 size_t hdr_len = 0;
266
267 if (header) {
268 /* Set up the header/trailer iovec. */
269 hdtrl[0].iov_base = header->data;
270 hdtrl[0].iov_len = hdr_len = header->length;
271 } else {
272 hdtrl[0].iov_base = NULL;
273 hdtrl[0].iov_len = hdr_len = 0;
274 }
275 hdtrl[1].iov_base = NULL;
276 hdtrl[1].iov_len = 0;
277
278 total = count;
279 while (total + hdtrl[0].iov_len) {
280 ssize_t nwritten;
281
282 /*
283 * HPUX guarantees that if any data was written before
284 * a signal interrupt then sendfile returns the number of
285 * bytes written (which may be less than requested) not -1.
286 * nwritten includes the header data sent.
287 */
288
289 do {
290#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILE64)
291 nwritten = sendfile64(tofd, fromfd, offset, total, &hdtrl[0], 0);
292#else
293 nwritten = sendfile(tofd, fromfd, offset, total, &hdtrl[0], 0);
294#endif
295 } while (nwritten == -1 && errno == EINTR);
296 if (nwritten == -1)
297 return -1;
298 if (nwritten == 0)
299 return -1; /* I think we're at EOF here... */
300
301 /*
302 * If this was a short (signal interrupted) write we may need
303 * to subtract it from the header data, or null out the header
304 * data altogether if we wrote more than hdtrl[0].iov_len bytes.
305 * We change nwritten to be the number of file bytes written.
306 */
307
308 if (hdtrl[0].iov_base && hdtrl[0].iov_len) {
309 if (nwritten >= hdtrl[0].iov_len) {
310 nwritten -= hdtrl[0].iov_len;
311 hdtrl[0].iov_base = NULL;
312 hdtrl[0].iov_len = 0;
313 } else {
314 /* iov_base is defined as a void *... */
315 hdtrl[0].iov_base = ((char *)hdtrl[0].iov_base) + nwritten;
316 hdtrl[0].iov_len -= nwritten;
317 nwritten = 0;
318 }
319 }
320 total -= nwritten;
321 offset += nwritten;
322 }
323 return count + hdr_len;
324}
325
326#elif defined(FREEBSD_SENDFILE_API)
327
328#include <sys/types.h>
329#include <sys/socket.h>
330#include <sys/uio.h>
331
332ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
333{
334 size_t total=0;
335 struct sf_hdtr hdr;
336 struct iovec hdtrl;
337 size_t hdr_len = 0;
338
339 hdr.headers = &hdtrl;
340 hdr.hdr_cnt = 1;
341 hdr.trailers = NULL;
342 hdr.trl_cnt = 0;
343
344 /* Set up the header iovec. */
345 if (header) {
346 hdtrl.iov_base = header->data;
347 hdtrl.iov_len = hdr_len = header->length;
348 } else {
349 hdtrl.iov_base = NULL;
350 hdtrl.iov_len = 0;
351 }
352
353 total = count;
354 while (total + hdtrl.iov_len) {
355 SMB_OFF_T nwritten;
356 int ret;
357
358 /*
359 * FreeBSD sendfile returns 0 on success, -1 on error.
360 * Remember, the tofd and fromfd are reversed..... :-).
361 * nwritten includes the header data sent.
362 */
363
364 do {
365 ret = sendfile(fromfd, tofd, offset, total, &hdr, &nwritten, 0);
366 } while (ret == -1 && errno == EINTR);
367 if (ret == -1)
368 return -1;
369
370 if (nwritten == 0)
371 return -1; /* I think we're at EOF here... */
372
373 /*
374 * If this was a short (signal interrupted) write we may need
375 * to subtract it from the header data, or null out the header
376 * data altogether if we wrote more than hdtrl.iov_len bytes.
377 * We change nwritten to be the number of file bytes written.
378 */
379
380 if (hdtrl.iov_base && hdtrl.iov_len) {
381 if (nwritten >= hdtrl.iov_len) {
382 nwritten -= hdtrl.iov_len;
383 hdtrl.iov_base = NULL;
384 hdtrl.iov_len = 0;
385 } else {
386 hdtrl.iov_base =
387 (caddr_t)hdtrl.iov_base + nwritten;
388 hdtrl.iov_len -= nwritten;
389 nwritten = 0;
390 }
391 }
392 total -= nwritten;
393 offset += nwritten;
394 }
395 return count + hdr_len;
396}
397
398#elif defined(AIX_SENDFILE_API)
399
400/* BEGIN AIX SEND_FILE */
401
402/* Contributed by William Jojo <jojowil@hvcc.edu> */
403#include <sys/socket.h>
404
405ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
406{
407 struct sf_parms hdtrl;
408
409 /* Set up the header/trailer struct params. */
410 if (header) {
411 hdtrl.header_data = header->data;
412 hdtrl.header_length = header->length;
413 } else {
414 hdtrl.header_data = NULL;
415 hdtrl.header_length = 0;
416 }
417 hdtrl.trailer_data = NULL;
418 hdtrl.trailer_length = 0;
419
420 hdtrl.file_descriptor = fromfd;
421 hdtrl.file_offset = offset;
422 hdtrl.file_bytes = count;
423
424 while ( hdtrl.file_bytes + hdtrl.header_length ) {
425 ssize_t ret;
426
427 /*
428 Return Value
429
430 There are three possible return values from send_file:
431
432 Value Description
433
434 -1 an error has occurred, errno contains the error code.
435
436 0 the command has completed successfully.
437
438 1 the command was completed partially, some data has been
439 transmitted but the command has to return for some reason,
440 for example, the command was interrupted by signals.
441 */
442 do {
443 ret = send_file(&tofd, &hdtrl, 0);
444 } while ( (ret == 1) || (ret == -1 && errno == EINTR) );
445 if ( ret == -1 )
446 return -1;
447 }
448
449 return count + header->length;
450}
451/* END AIX SEND_FILE */
452
453#else /* No sendfile implementation. Return error. */
454
455ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
456{
457 /* No sendfile syscall. */
458 errno = ENOSYS;
459 return -1;
460}
461#endif
Note: See TracBrowser for help on using the repository browser.