source: branches/samba-3.5.x/source3/lib/sendfile.c@ 850

Last change on this file since 850 was 414, checked in by Herwig Bauernfeind, 15 years ago

Samba 3.5.0: Initial import

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