s4 dns: Forward questions we can't answer to another server
[Samba/bb.git] / source3 / lib / sendfile.c
blobc2099bc8b1b04bc62751262243bd79853de238f7
1 /*
2 Unix SMB/Netbios implementation.
3 Version 2.2.x / 3.0.x
4 sendfile implementations.
5 Copyright (C) Jeremy Allison 2002.
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.
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/>.
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.
26 #include "includes.h"
28 #if defined(LINUX_SENDFILE_API)
30 #include <sys/sendfile.h>
32 #ifndef MSG_MORE
33 #define MSG_MORE 0x8000
34 #endif
36 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
38 size_t total=0;
39 ssize_t ret;
40 size_t hdr_len = 0;
43 * Send the header first.
44 * Use MSG_MORE to cork the TCP output until sendfile is called.
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;
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.
81 errno = EINTR; /* Normally we can never return this. */
83 return -1;
85 if (nwritten == 0) {
87 * EOF, return a short read
89 return hdr_len + (count - total);
91 total -= nwritten;
93 return count + hdr_len;
96 #elif defined(LINUX_BROKEN_SENDFILE_API)
99 * We must use explicit 32 bit types here. This code path means Linux
100 * won't do proper 64-bit sendfile. JRA.
103 extern int32 sendfile (int out_fd, int in_fd, int32 *offset, uint32 count);
106 #ifndef MSG_MORE
107 #define MSG_MORE 0x8000
108 #endif
110 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
112 size_t total=0;
113 ssize_t ret;
114 ssize_t hdr_len = 0;
115 uint32 small_total = 0;
116 int32 small_offset;
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.
125 if ((sizeof(SMB_OFF_T) >= 8) && (offset + count > (SMB_OFF_T)0x7FFFFFFF)) {
126 errno = ENOSYS;
127 return -1;
131 * Send the header first.
132 * Use MSG_MORE to cork the TCP output until sendfile is called.
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;
145 small_total = (uint32)count;
146 small_offset = (int32)offset;
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.
167 errno = EINTR; /* Normally we can never return this. */
169 return -1;
171 if (nwritten == 0) {
173 * EOF, return a short read
175 return hdr_len + (((uint32)count) - small_total);
177 small_total -= nwritten;
179 return count + hdr_len;
183 #elif defined(SOLARIS_SENDFILE_API)
186 * Solaris sendfile code written by Pierre Belanger <belanger@pobox.com>.
189 #include <sys/sendfile.h>
191 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
193 int sfvcnt;
194 size_t total, xferred;
195 struct sendfilevec vec[2];
196 ssize_t hdr_len = 0;
198 if (header) {
199 sfvcnt = 2;
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;
206 vec[1].sfv_fd = fromfd;
207 vec[1].sfv_flag = 0;
208 vec[1].sfv_off = offset;
209 vec[1].sfv_len = count;
211 } else {
212 sfvcnt = 1;
214 vec[0].sfv_fd = fromfd;
215 vec[0].sfv_flag = 0;
216 vec[0].sfv_off = offset;
217 vec[0].sfv_len = count;
220 total = count + hdr_len;
222 while (total) {
223 ssize_t nwritten;
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.
230 xferred = 0;
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;
248 if (nwritten == -1)
249 return -1;
250 if (nwritten == 0)
251 return -1; /* I think we're at EOF here... */
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
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;
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;
271 total -= nwritten;
273 return count + hdr_len;
276 #elif defined(HPUX_SENDFILE_API)
278 #include <sys/socket.h>
279 #include <sys/uio.h>
281 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
283 size_t total=0;
284 struct iovec hdtrl[2];
285 size_t hdr_len = 0;
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;
295 hdtrl[1].iov_base = NULL;
296 hdtrl[1].iov_len = 0;
298 total = count;
299 while (total + hdtrl[0].iov_len) {
300 ssize_t nwritten;
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.
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... */
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.
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;
344 total -= nwritten;
345 offset += nwritten;
347 return count + hdr_len;
350 #elif defined(FREEBSD_SENDFILE_API)
352 #include <sys/types.h>
353 #include <sys/socket.h>
354 #include <sys/uio.h>
356 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
358 size_t total=0;
359 struct sf_hdtr hdr;
360 struct iovec hdtrl;
361 size_t hdr_len = 0;
363 hdr.headers = &hdtrl;
364 hdr.hdr_cnt = 1;
365 hdr.trailers = NULL;
366 hdr.trl_cnt = 0;
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;
377 total = count;
378 while (total + hdtrl.iov_len) {
379 SMB_OFF_T nwritten;
380 int ret;
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.
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;
398 if (nwritten == 0)
399 return -1; /* I think we're at EOF here... */
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.
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;
420 total -= nwritten;
421 offset += nwritten;
423 return count + hdr_len;
426 #elif defined(AIX_SENDFILE_API)
428 /* BEGIN AIX SEND_FILE */
430 /* Contributed by William Jojo <jojowil@hvcc.edu> */
431 #include <sys/socket.h>
433 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
435 struct sf_parms hdtrl;
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;
445 hdtrl.trailer_data = NULL;
446 hdtrl.trailer_length = 0;
448 hdtrl.file_descriptor = fromfd;
449 hdtrl.file_offset = offset;
450 hdtrl.file_bytes = count;
452 while ( hdtrl.file_bytes + hdtrl.header_length ) {
453 ssize_t ret;
456 Return Value
458 There are three possible return values from send_file:
460 Value Description
462 -1 an error has occurred, errno contains the error code.
464 0 the command has completed successfully.
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.
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;
481 return count + header->length;
483 /* END AIX SEND_FILE */
485 #else /* No sendfile implementation. Return error. */
487 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
489 /* No sendfile syscall. */
490 errno = ENOSYS;
491 return -1;
493 #endif