s3/docs: Fix typo.
[Samba/gebeck_regimport.git] / source3 / lib / sendfile.c
blob3003246dd0f5adbbb86420afec50695749683467
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 } 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.
77 errno = EINTR; /* Normally we can never return this. */
79 return -1;
81 if (nwritten == 0) {
83 * EOF, return a short read
85 return hdr_len + (count - total);
87 total -= nwritten;
89 return count + hdr_len;
92 #elif defined(LINUX_BROKEN_SENDFILE_API)
95 * We must use explicit 32 bit types here. This code path means Linux
96 * won't do proper 64-bit sendfile. JRA.
99 extern int32 sendfile (int out_fd, int in_fd, int32 *offset, uint32 count);
102 #ifndef MSG_MORE
103 #define MSG_MORE 0x8000
104 #endif
106 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
108 size_t total=0;
109 ssize_t ret;
110 ssize_t hdr_len = 0;
111 uint32 small_total = 0;
112 int32 small_offset;
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.
121 if ((sizeof(SMB_OFF_T) >= 8) && (offset + count > (SMB_OFF_T)0x7FFFFFFF)) {
122 errno = ENOSYS;
123 return -1;
127 * Send the header first.
128 * Use MSG_MORE to cork the TCP output until sendfile is called.
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;
141 small_total = (uint32)count;
142 small_offset = (int32)offset;
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.
159 errno = EINTR; /* Normally we can never return this. */
161 return -1;
163 if (nwritten == 0) {
165 * EOF, return a short read
167 return hdr_len + (((uint32)count) - small_total);
169 small_total -= nwritten;
171 return count + hdr_len;
175 #elif defined(SOLARIS_SENDFILE_API)
178 * Solaris sendfile code written by Pierre Belanger <belanger@pobox.com>.
181 #include <sys/sendfile.h>
183 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
185 int sfvcnt;
186 size_t total, xferred;
187 struct sendfilevec vec[2];
188 ssize_t hdr_len = 0;
190 if (header) {
191 sfvcnt = 2;
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;
198 vec[1].sfv_fd = fromfd;
199 vec[1].sfv_flag = 0;
200 vec[1].sfv_off = offset;
201 vec[1].sfv_len = count;
203 } else {
204 sfvcnt = 1;
206 vec[0].sfv_fd = fromfd;
207 vec[0].sfv_flag = 0;
208 vec[0].sfv_off = offset;
209 vec[0].sfv_len = count;
212 total = count + hdr_len;
214 while (total) {
215 ssize_t nwritten;
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.
222 xferred = 0;
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;
236 if (nwritten == -1)
237 return -1;
238 if (nwritten == 0)
239 return -1; /* I think we're at EOF here... */
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
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;
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;
259 total -= nwritten;
261 return count + hdr_len;
264 #elif defined(HPUX_SENDFILE_API)
266 #include <sys/socket.h>
267 #include <sys/uio.h>
269 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
271 size_t total=0;
272 struct iovec hdtrl[2];
273 size_t hdr_len = 0;
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;
283 hdtrl[1].iov_base = NULL;
284 hdtrl[1].iov_len = 0;
286 total = count;
287 while (total + hdtrl[0].iov_len) {
288 ssize_t nwritten;
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.
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... */
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.
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;
328 total -= nwritten;
329 offset += nwritten;
331 return count + hdr_len;
334 #elif defined(FREEBSD_SENDFILE_API)
336 #include <sys/types.h>
337 #include <sys/socket.h>
338 #include <sys/uio.h>
340 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
342 size_t total=0;
343 struct sf_hdtr hdr;
344 struct iovec hdtrl;
345 size_t hdr_len = 0;
347 hdr.headers = &hdtrl;
348 hdr.hdr_cnt = 1;
349 hdr.trailers = NULL;
350 hdr.trl_cnt = 0;
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;
361 total = count;
362 while (total + hdtrl.iov_len) {
363 SMB_OFF_T nwritten;
364 int ret;
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.
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;
378 if (nwritten == 0)
379 return -1; /* I think we're at EOF here... */
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.
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;
400 total -= nwritten;
401 offset += nwritten;
403 return count + hdr_len;
406 #elif defined(AIX_SENDFILE_API)
408 /* BEGIN AIX SEND_FILE */
410 /* Contributed by William Jojo <jojowil@hvcc.edu> */
411 #include <sys/socket.h>
413 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
415 struct sf_parms hdtrl;
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;
425 hdtrl.trailer_data = NULL;
426 hdtrl.trailer_length = 0;
428 hdtrl.file_descriptor = fromfd;
429 hdtrl.file_offset = offset;
430 hdtrl.file_bytes = count;
432 while ( hdtrl.file_bytes + hdtrl.header_length ) {
433 ssize_t ret;
436 Return Value
438 There are three possible return values from send_file:
440 Value Description
442 -1 an error has occurred, errno contains the error code.
444 0 the command has completed successfully.
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.
450 do {
451 ret = send_file(&tofd, &hdtrl, 0);
452 } while ( (ret == 1) || (ret == -1 && errno == EINTR) );
453 if ( ret == -1 )
454 return -1;
457 return count + header->length;
459 /* END AIX SEND_FILE */
461 #else /* No sendfile implementation. Return error. */
463 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
465 /* No sendfile syscall. */
466 errno = ENOSYS;
467 return -1;
469 #endif