Make 3.0.x in sync with 3.2 here.
[Samba.git] / source / lib / sendfile.c
blobf4f6e16526f31f7485370be02f9738d24f3a0059
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 2 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, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 * This file handles the OS dependent sendfile implementations.
23 * The API is such that it returns -1 on error, else returns the
24 * number of bytes written.
27 #include "includes.h"
29 #if defined(LINUX_SENDFILE_API)
31 #include <sys/sendfile.h>
33 #ifndef MSG_MORE
34 #define MSG_MORE 0x8000
35 #endif
37 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
39 size_t total=0;
40 ssize_t ret;
41 size_t hdr_len = 0;
44 * Send the header first.
45 * Use MSG_MORE to cork the TCP output until sendfile is called.
48 if (header) {
49 hdr_len = header->length;
50 while (total < hdr_len) {
51 ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE);
52 if (ret == -1)
53 return -1;
54 total += ret;
58 total = count;
59 while (total) {
60 ssize_t nwritten;
61 do {
62 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILE64)
63 nwritten = sendfile64(tofd, fromfd, &offset, total);
64 #else
65 nwritten = sendfile(tofd, fromfd, &offset, total);
66 #endif
67 } while (nwritten == -1 && errno == EINTR);
68 if (nwritten == -1) {
69 if (errno == ENOSYS) {
70 /* Ok - we're in a world of pain here. We just sent
71 * the header, but the sendfile failed. We have to
72 * emulate the sendfile at an upper layer before we
73 * disable it's use. So we do something really ugly.
74 * We set the errno to a strange value so we can detect
75 * this at the upper level and take care of it without
76 * layer violation. JRA.
78 errno = EINTR; /* Normally we can never return this. */
80 return -1;
82 if (nwritten == 0)
83 return -1; /* I think we're at EOF here... */
84 total -= nwritten;
86 return count + hdr_len;
89 #elif defined(LINUX_BROKEN_SENDFILE_API)
92 * We must use explicit 32 bit types here. This code path means Linux
93 * won't do proper 64-bit sendfile. JRA.
96 extern int32 sendfile (int out_fd, int in_fd, int32 *offset, uint32 count);
99 #ifndef MSG_MORE
100 #define MSG_MORE 0x8000
101 #endif
103 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
105 size_t total=0;
106 ssize_t ret;
107 ssize_t hdr_len = 0;
108 uint32 small_total = 0;
109 int32 small_offset;
112 * Fix for broken Linux 2.4 systems with no working sendfile64().
113 * If the offset+count > 2 GB then pretend we don't have the
114 * system call sendfile at all. The upper layer catches this
115 * and uses a normal read. JRA.
118 if ((sizeof(SMB_OFF_T) >= 8) && (offset + count > (SMB_OFF_T)0x7FFFFFFF)) {
119 errno = ENOSYS;
120 return -1;
124 * Send the header first.
125 * Use MSG_MORE to cork the TCP output until sendfile is called.
128 if (header) {
129 hdr_len = header->length;
130 while (total < hdr_len) {
131 ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE);
132 if (ret == -1)
133 return -1;
134 total += ret;
138 small_total = (uint32)count;
139 small_offset = (int32)offset;
141 while (small_total) {
142 int32 nwritten;
143 do {
144 nwritten = sendfile(tofd, fromfd, &small_offset, small_total);
145 } while (nwritten == -1 && errno == EINTR);
146 if (nwritten == -1) {
147 if (errno == ENOSYS) {
148 /* Ok - we're in a world of pain here. We just sent
149 * the header, but the sendfile failed. We have to
150 * emulate the sendfile at an upper layer before we
151 * disable it's use. So we do something really ugly.
152 * We set the errno to a strange value so we can detect
153 * this at the upper level and take care of it without
154 * layer violation. JRA.
156 errno = EINTR; /* Normally we can never return this. */
158 return -1;
160 if (nwritten == 0)
161 return -1; /* I think we're at EOF here... */
162 small_total -= nwritten;
164 return count + hdr_len;
168 #elif defined(SOLARIS_SENDFILE_API)
171 * Solaris sendfile code written by Pierre Belanger <belanger@pobox.com>.
174 #include <sys/sendfile.h>
176 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
178 int sfvcnt;
179 size_t total, xferred;
180 struct sendfilevec vec[2];
181 ssize_t hdr_len = 0;
183 if (header) {
184 sfvcnt = 2;
186 vec[0].sfv_fd = SFV_FD_SELF;
187 vec[0].sfv_flag = 0;
188 vec[0].sfv_off = (off_t)header->data;
189 vec[0].sfv_len = hdr_len = header->length;
191 vec[1].sfv_fd = fromfd;
192 vec[1].sfv_flag = 0;
193 vec[1].sfv_off = offset;
194 vec[1].sfv_len = count;
196 } else {
197 sfvcnt = 1;
199 vec[0].sfv_fd = fromfd;
200 vec[0].sfv_flag = 0;
201 vec[0].sfv_off = offset;
202 vec[0].sfv_len = count;
205 total = count + hdr_len;
207 while (total) {
208 ssize_t nwritten;
211 * Although not listed in the API error returns, this is almost certainly
212 * a slow system call and will be interrupted by a signal with EINTR. JRA.
215 xferred = 0;
217 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILEV64)
218 nwritten = sendfilev64(tofd, vec, sfvcnt, &xferred);
219 #else
220 nwritten = sendfilev(tofd, vec, sfvcnt, &xferred);
221 #endif
222 if (nwritten == -1 && errno == EINTR) {
223 if (xferred == 0)
224 continue; /* Nothing written yet. */
225 else
226 nwritten = xferred;
229 if (nwritten == -1)
230 return -1;
231 if (nwritten == 0)
232 return -1; /* I think we're at EOF here... */
235 * If this was a short (signal interrupted) write we may need
236 * to subtract it from the header data, or null out the header
237 * data altogether if we wrote more than vec[0].sfv_len bytes.
238 * We move vec[1].* to vec[0].* and set sfvcnt to 1
241 if (sfvcnt == 2 && nwritten >= vec[0].sfv_len) {
242 vec[1].sfv_off += nwritten - vec[0].sfv_len;
243 vec[1].sfv_len -= nwritten - vec[0].sfv_len;
245 /* Move vec[1].* to vec[0].* and set sfvcnt to 1 */
246 vec[0] = vec[1];
247 sfvcnt = 1;
248 } else {
249 vec[0].sfv_off += nwritten;
250 vec[0].sfv_len -= nwritten;
252 total -= nwritten;
254 return count + hdr_len;
257 #elif defined(HPUX_SENDFILE_API)
259 #include <sys/socket.h>
260 #include <sys/uio.h>
262 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
264 size_t total=0;
265 struct iovec hdtrl[2];
266 size_t hdr_len = 0;
268 if (header) {
269 /* Set up the header/trailer iovec. */
270 hdtrl[0].iov_base = header->data;
271 hdtrl[0].iov_len = hdr_len = header->length;
272 } else {
273 hdtrl[0].iov_base = NULL;
274 hdtrl[0].iov_len = hdr_len = 0;
276 hdtrl[1].iov_base = NULL;
277 hdtrl[1].iov_len = 0;
279 total = count;
280 while (total + hdtrl[0].iov_len) {
281 ssize_t nwritten;
284 * HPUX guarantees that if any data was written before
285 * a signal interrupt then sendfile returns the number of
286 * bytes written (which may be less than requested) not -1.
287 * nwritten includes the header data sent.
290 do {
291 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILE64)
292 nwritten = sendfile64(tofd, fromfd, offset, total, &hdtrl[0], 0);
293 #else
294 nwritten = sendfile(tofd, fromfd, offset, total, &hdtrl[0], 0);
295 #endif
296 } while (nwritten == -1 && errno == EINTR);
297 if (nwritten == -1)
298 return -1;
299 if (nwritten == 0)
300 return -1; /* I think we're at EOF here... */
303 * If this was a short (signal interrupted) write we may need
304 * to subtract it from the header data, or null out the header
305 * data altogether if we wrote more than hdtrl[0].iov_len bytes.
306 * We change nwritten to be the number of file bytes written.
309 if (hdtrl[0].iov_base && hdtrl[0].iov_len) {
310 if (nwritten >= hdtrl[0].iov_len) {
311 nwritten -= hdtrl[0].iov_len;
312 hdtrl[0].iov_base = NULL;
313 hdtrl[0].iov_len = 0;
314 } else {
315 /* iov_base is defined as a void *... */
316 hdtrl[0].iov_base = ((char *)hdtrl[0].iov_base) + nwritten;
317 hdtrl[0].iov_len -= nwritten;
318 nwritten = 0;
321 total -= nwritten;
322 offset += nwritten;
324 return count + hdr_len;
327 #elif defined(FREEBSD_SENDFILE_API)
329 #include <sys/types.h>
330 #include <sys/socket.h>
331 #include <sys/uio.h>
333 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
335 size_t total=0;
336 struct sf_hdtr hdr;
337 struct iovec hdtrl;
338 size_t hdr_len = 0;
340 hdr.headers = &hdtrl;
341 hdr.hdr_cnt = 1;
342 hdr.trailers = NULL;
343 hdr.trl_cnt = 0;
345 /* Set up the header iovec. */
346 if (header) {
347 hdtrl.iov_base = header->data;
348 hdtrl.iov_len = hdr_len = header->length;
349 } else {
350 hdtrl.iov_base = NULL;
351 hdtrl.iov_len = 0;
354 total = count;
355 while (total + hdtrl.iov_len) {
356 SMB_OFF_T nwritten;
357 int ret;
360 * FreeBSD sendfile returns 0 on success, -1 on error.
361 * Remember, the tofd and fromfd are reversed..... :-).
362 * nwritten includes the header data sent.
365 do {
366 ret = sendfile(fromfd, tofd, offset, total, &hdr, &nwritten, 0);
367 } while (ret == -1 && errno == EINTR);
368 if (ret == -1)
369 return -1;
371 if (nwritten == 0)
372 return -1; /* I think we're at EOF here... */
375 * If this was a short (signal interrupted) write we may need
376 * to subtract it from the header data, or null out the header
377 * data altogether if we wrote more than hdtrl.iov_len bytes.
378 * We change nwritten to be the number of file bytes written.
381 if (hdtrl.iov_base && hdtrl.iov_len) {
382 if (nwritten >= hdtrl.iov_len) {
383 nwritten -= hdtrl.iov_len;
384 hdtrl.iov_base = NULL;
385 hdtrl.iov_len = 0;
386 } else {
387 hdtrl.iov_base += nwritten;
388 hdtrl.iov_len -= nwritten;
389 nwritten = 0;
392 total -= nwritten;
393 offset += nwritten;
395 return count + hdr_len;
398 #elif defined(AIX_SENDFILE_API)
400 /* BEGIN AIX SEND_FILE */
402 /* Contributed by William Jojo <jojowil@hvcc.edu> */
403 #include <sys/socket.h>
405 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
407 struct sf_parms hdtrl;
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;
417 hdtrl.trailer_data = NULL;
418 hdtrl.trailer_length = 0;
420 hdtrl.file_descriptor = fromfd;
421 hdtrl.file_offset = offset;
422 hdtrl.file_bytes = count;
424 while ( hdtrl.file_bytes + hdtrl.header_length ) {
425 ssize_t ret;
428 Return Value
430 There are three possible return values from send_file:
432 Value Description
434 -1 an error has occurred, errno contains the error code.
436 0 the command has completed successfully.
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.
442 do {
443 ret = send_file(&tofd, &hdtrl, 0);
444 } while ( (ret == 1) || (ret == -1 && errno == EINTR) );
445 if ( ret == -1 )
446 return -1;
449 return count + header->length;
451 /* END AIX SEND_FILE */
453 #else /* No sendfile implementation. Return error. */
455 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
457 /* No sendfile syscall. */
458 errno = ENOSYS;
459 return -1;
461 #endif