source: vendor/3.6.23/source3/lib/sendfile.c

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

Samba Server: update vendor to 3.6.0

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