vfs_ceph: use consistent code style when setting errno
[Samba.git] / source3 / lib / sendfile.c
blob69c89d29f3190d5fbce52167faa3e093659b70eb
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"
27 #include "system/filesys.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, off_t offset, size_t count)
39 size_t total=0;
40 ssize_t ret = -1;
41 size_t hdr_len = 0;
42 int old_flags = 0;
43 bool socket_flags_changed = false;
46 * Send the header first.
47 * Use MSG_MORE to cork the TCP output until sendfile is called.
50 if (header) {
51 hdr_len = header->length;
52 while (total < hdr_len) {
53 ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE);
54 if (ret == -1) {
55 if (errno == EAGAIN || errno == EWOULDBLOCK) {
57 * send() must complete before we can
58 * send any other outgoing data on the
59 * socket. Ensure socket is in blocking
60 * mode. For SMB2 by default the socket
61 * is in non-blocking mode.
63 old_flags = fcntl(tofd, F_GETFL, 0);
64 ret = set_blocking(tofd, true);
65 if (ret == -1) {
66 goto out;
68 socket_flags_changed = true;
69 continue;
71 goto out;
73 total += ret;
77 total = count;
78 while (total) {
79 ssize_t nwritten;
80 do {
81 nwritten = sendfile(tofd, fromfd, &offset, total);
82 } while (nwritten == -1 && errno == EINTR);
83 if (nwritten == -1) {
84 if (errno == EAGAIN || errno == EWOULDBLOCK) {
85 if (socket_flags_changed) {
87 * We're already in blocking
88 * mode. This is an error.
90 ret = -1;
91 goto out;
95 * Sendfile must complete before we can
96 * send any other outgoing data on the socket.
97 * Ensure socket is in blocking mode.
98 * For SMB2 by default the socket is in
99 * non-blocking mode.
101 old_flags = fcntl(tofd, F_GETFL, 0);
102 ret = set_blocking(tofd, true);
103 if (ret == -1) {
104 goto out;
106 socket_flags_changed = true;
107 continue;
110 if (errno == ENOSYS || errno == EINVAL) {
111 /* Ok - we're in a world of pain here. We just sent
112 * the header, but the sendfile failed. We have to
113 * emulate the sendfile at an upper layer before we
114 * disable it's use. So we do something really ugly.
115 * We set the errno to a strange value so we can detect
116 * this at the upper level and take care of it without
117 * layer violation. JRA.
119 errno = EINTR; /* Normally we can never return this. */
121 ret = -1;
122 goto out;
124 if (nwritten == 0) {
126 * EOF, return a short read
128 ret = hdr_len + (count - total);
129 goto out;
131 total -= nwritten;
134 ret = count + hdr_len;
136 out:
138 if (socket_flags_changed) {
139 int saved_errno = errno;
140 int err;
142 /* Restore the old state of the socket. */
143 err = fcntl(tofd, F_SETFL, old_flags);
144 if (err == -1) {
145 return -1;
147 if (ret == -1) {
148 errno = saved_errno;
152 return ret;
155 #elif defined(SOLARIS_SENDFILE_API)
158 * Solaris sendfile code written by Pierre Belanger <belanger@pobox.com>.
161 #include <sys/sendfile.h>
163 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
165 int sfvcnt;
166 size_t total, xferred;
167 struct sendfilevec vec[2];
168 ssize_t hdr_len = 0;
169 int old_flags = 0;
170 ssize_t ret = -1;
171 bool socket_flags_changed = false;
173 if (header) {
174 sfvcnt = 2;
176 vec[0].sfv_fd = SFV_FD_SELF;
177 vec[0].sfv_flag = 0;
178 vec[0].sfv_off = (off_t)header->data;
179 vec[0].sfv_len = hdr_len = header->length;
181 vec[1].sfv_fd = fromfd;
182 vec[1].sfv_flag = 0;
183 vec[1].sfv_off = offset;
184 vec[1].sfv_len = count;
186 } else {
187 sfvcnt = 1;
189 vec[0].sfv_fd = fromfd;
190 vec[0].sfv_flag = 0;
191 vec[0].sfv_off = offset;
192 vec[0].sfv_len = count;
195 total = count + hdr_len;
197 while (total) {
198 ssize_t nwritten;
201 * Although not listed in the API error returns, this is almost certainly
202 * a slow system call and will be interrupted by a signal with EINTR. JRA.
205 xferred = 0;
207 nwritten = sendfilev(tofd, vec, sfvcnt, &xferred);
208 if (nwritten == -1 && errno == EINTR) {
209 if (xferred == 0)
210 continue; /* Nothing written yet. */
211 else
212 nwritten = xferred;
215 if (nwritten == -1) {
216 if (errno == EAGAIN || errno == EWOULDBLOCK) {
218 * Sendfile must complete before we can
219 * send any other outgoing data on the socket.
220 * Ensure socket is in blocking mode.
221 * For SMB2 by default the socket is in
222 * non-blocking mode.
224 old_flags = fcntl(tofd, F_GETFL, 0);
225 ret = set_blocking(tofd, true);
226 if (ret == -1) {
227 goto out;
229 socket_flags_changed = true;
230 continue;
232 ret = -1;
233 goto out;
235 if (nwritten == 0) {
236 ret = -1;
237 goto out; /* I think we're at EOF here... */
241 * If this was a short (signal interrupted) write we may need
242 * to subtract it from the header data, or null out the header
243 * data altogether if we wrote more than vec[0].sfv_len bytes.
244 * We move vec[1].* to vec[0].* and set sfvcnt to 1
247 if (sfvcnt == 2 && nwritten >= vec[0].sfv_len) {
248 vec[1].sfv_off += nwritten - vec[0].sfv_len;
249 vec[1].sfv_len -= nwritten - vec[0].sfv_len;
251 /* Move vec[1].* to vec[0].* and set sfvcnt to 1 */
252 vec[0] = vec[1];
253 sfvcnt = 1;
254 } else {
255 vec[0].sfv_off += nwritten;
256 vec[0].sfv_len -= nwritten;
258 total -= nwritten;
260 ret = count + hdr_len;
262 out:
264 if (socket_flags_changed) {
265 int saved_errno;
266 int err;
268 if (ret == -1) {
269 saved_errno = errno;
271 /* Restore the old state of the socket. */
272 err = fcntl(tofd, F_SETFL, old_flags);
273 if (err == -1) {
274 return -1;
276 if (ret == -1) {
277 errno = saved_errno;
281 return ret;
284 #elif defined(HPUX_SENDFILE_API)
286 #include <sys/socket.h>
287 #include <sys/uio.h>
289 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
291 size_t total=0;
292 struct iovec hdtrl[2];
293 size_t hdr_len = 0;
294 int old_flags = 0;
295 ssize_t ret = -1;
296 bool socket_flags_changed = false;
298 if (header) {
299 /* Set up the header/trailer iovec. */
300 hdtrl[0].iov_base = (void *)header->data;
301 hdtrl[0].iov_len = hdr_len = header->length;
302 } else {
303 hdtrl[0].iov_base = NULL;
304 hdtrl[0].iov_len = hdr_len = 0;
306 hdtrl[1].iov_base = NULL;
307 hdtrl[1].iov_len = 0;
309 total = count;
310 while (total + hdtrl[0].iov_len) {
311 ssize_t nwritten;
314 * HPUX guarantees that if any data was written before
315 * a signal interrupt then sendfile returns the number of
316 * bytes written (which may be less than requested) not -1.
317 * nwritten includes the header data sent.
320 do {
321 nwritten = sendfile(tofd, fromfd, offset, total, &hdtrl[0], 0);
322 } while (nwritten == -1 && errno == EINTR);
323 if (nwritten == -1) {
324 if (errno == EAGAIN || errno == EWOULDBLOCK) {
326 * Sendfile must complete before we can
327 * send any other outgoing data on the socket.
328 * Ensure socket is in blocking mode.
329 * For SMB2 by default the socket is in
330 * non-blocking mode.
332 old_flags = fcntl(tofd, F_GETFL, 0);
333 ret = set_blocking(tofd, true);
334 if (ret == -1) {
335 goto out;
337 socket_flags_changed = true;
338 continue;
340 ret = -1;
341 goto out;
343 if (nwritten == 0) {
344 ret = -1; /* I think we're at EOF here... */
345 goto out;
349 * If this was a short (signal interrupted) write we may need
350 * to subtract it from the header data, or null out the header
351 * data altogether if we wrote more than hdtrl[0].iov_len bytes.
352 * We change nwritten to be the number of file bytes written.
355 if (hdtrl[0].iov_base && hdtrl[0].iov_len) {
356 if (nwritten >= hdtrl[0].iov_len) {
357 nwritten -= hdtrl[0].iov_len;
358 hdtrl[0].iov_base = NULL;
359 hdtrl[0].iov_len = 0;
360 } else {
361 /* iov_base is defined as a void *... */
362 hdtrl[0].iov_base = (void *)(((char *)hdtrl[0].iov_base) + nwritten);
363 hdtrl[0].iov_len -= nwritten;
364 nwritten = 0;
367 total -= nwritten;
368 offset += nwritten;
370 ret = count + hdr_len;
372 out:
374 if (socket_flags_changed) {
375 int saved_errno;
376 int err;
378 if (ret == -1) {
379 saved_errno = errno;
381 /* Restore the old state of the socket. */
382 err = fcntl(tofd, F_SETFL, old_flags);
383 if (err == -1) {
384 return -1;
386 if (ret == -1) {
387 errno = saved_errno;
391 return ret;
394 #elif defined(FREEBSD_SENDFILE_API) || defined(DARWIN_SENDFILE_API)
396 #include <sys/types.h>
397 #include <sys/socket.h>
398 #include <sys/uio.h>
400 ssize_t sys_sendfile(int tofd, int fromfd,
401 const DATA_BLOB *header, off_t offset, size_t count)
403 struct sf_hdtr sf_header = {0};
404 struct iovec io_header = {0};
405 int old_flags = 0;
407 off_t nwritten;
408 ssize_t ret = -1;
409 bool socket_flags_changed = false;
411 if (header) {
412 sf_header.headers = &io_header;
413 sf_header.hdr_cnt = 1;
414 io_header.iov_base = header->data;
415 io_header.iov_len = header->length;
416 sf_header.trailers = NULL;
417 sf_header.trl_cnt = 0;
420 while (count != 0) {
422 nwritten = count;
423 #if defined(DARWIN_SENDFILE_API)
424 /* Darwin recycles nwritten as a value-result parameter, apart from that this
425 sendfile implementation is quite the same as the FreeBSD one */
426 ret = sendfile(fromfd, tofd, offset, &nwritten, &sf_header, 0);
427 #else
428 ret = sendfile(fromfd, tofd, offset, count, &sf_header, &nwritten, 0);
429 #endif
430 if (ret == -1 && errno != EINTR) {
431 if (errno == EAGAIN || errno == EWOULDBLOCK) {
433 * Sendfile must complete before we can
434 * send any other outgoing data on the socket.
435 * Ensure socket is in blocking mode.
436 * For SMB2 by default the socket is in
437 * non-blocking mode.
439 old_flags = fcntl(tofd, F_GETFL, 0);
440 ret = set_blocking(tofd, true);
441 if (ret == -1) {
442 goto out;
444 socket_flags_changed = true;
445 continue;
447 /* Send failed, we are toast. */
448 ret = -1;
449 goto out;
452 if (nwritten == 0) {
453 /* EOF of offset is after EOF. */
454 break;
457 if (sf_header.hdr_cnt) {
458 if (io_header.iov_len <= nwritten) {
459 /* Entire header was sent. */
460 sf_header.headers = NULL;
461 sf_header.hdr_cnt = 0;
462 nwritten -= io_header.iov_len;
463 } else {
464 /* Partial header was sent. */
465 io_header.iov_len -= nwritten;
466 io_header.iov_base =
467 ((uint8_t *)io_header.iov_base) + nwritten;
468 nwritten = 0;
472 offset += nwritten;
473 count -= nwritten;
476 ret = nwritten;
478 out:
480 if (socket_flags_changed) {
481 int saved_errno;
482 int err;
484 if (ret == -1) {
485 saved_errno = errno;
487 /* Restore the old state of the socket. */
488 err = fcntl(tofd, F_SETFL, old_flags);
489 if (err == -1) {
490 return -1;
492 if (ret == -1) {
493 errno = saved_errno;
497 return ret;
500 #elif defined(AIX_SENDFILE_API)
502 /* BEGIN AIX SEND_FILE */
504 /* Contributed by William Jojo <jojowil@hvcc.edu> */
505 #include <sys/socket.h>
507 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
509 struct sf_parms hdtrl;
510 int old_flags = 0;
511 ssize_t ret = -1;
512 bool socket_flags_changed = false;
514 /* Set up the header/trailer struct params. */
515 if (header) {
516 hdtrl.header_data = header->data;
517 hdtrl.header_length = header->length;
518 } else {
519 hdtrl.header_data = NULL;
520 hdtrl.header_length = 0;
522 hdtrl.trailer_data = NULL;
523 hdtrl.trailer_length = 0;
525 hdtrl.file_descriptor = fromfd;
526 hdtrl.file_offset = offset;
527 hdtrl.file_bytes = count;
529 while ( hdtrl.file_bytes + hdtrl.header_length ) {
531 Return Value
533 There are three possible return values from send_file:
535 Value Description
537 -1 an error has occurred, errno contains the error code.
539 0 the command has completed successfully.
541 1 the command was completed partially, some data has been
542 transmitted but the command has to return for some reason,
543 for example, the command was interrupted by signals.
545 do {
546 ret = send_file(&tofd, &hdtrl, 0);
547 } while ((ret == 1) || (ret == -1 && errno == EINTR));
548 if ( ret == -1 ) {
549 if (errno == EAGAIN || errno == EWOULDBLOCK) {
551 * Sendfile must complete before we can
552 * send any other outgoing data on the socket.
553 * Ensure socket is in blocking mode.
554 * For SMB2 by default the socket is in
555 * non-blocking mode.
557 old_flags = fcntl(tofd, F_GETFL, 0);
558 ret = set_blocking(tofd, true);
559 if (ret == -1) {
560 goto out;
562 socket_flags_changed = true;
563 continue;
565 goto out;
569 ret = count + header->length;
571 out:
573 if (socket_flags_changed) {
574 int saved_errno;
575 int err;
577 if (ret == -1) {
578 saved_errno = errno;
580 /* Restore the old state of the socket. */
581 err = fcntl(tofd, F_SETFL, old_flags);
582 if (err == -1) {
583 return -1;
585 if (ret == -1) {
586 errno = saved_errno;
590 return ret;
592 /* END AIX SEND_FILE */
594 #else /* No sendfile implementation. Return error. */
596 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
598 /* No sendfile syscall. */
599 errno = ENOSYS;
600 return -1;
602 #endif