selftest/flapping: more samba4.rpc.samr.large-dc.two subtests are flakey
[Samba/bb.git] / source3 / lib / sendfile.c
blobd5fd5a6ab5903f5ea708e302eb123e8495ba0524
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, 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 nwritten = sendfile(tofd, fromfd, &offset, total);
62 #if defined(EWOULDBLOCK)
63 } while (nwritten == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
64 #else
65 } while (nwritten == -1 && (errno == EINTR || errno == EAGAIN));
66 #endif
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(SOLARIS_SENDFILE_API)
95 * Solaris sendfile code written by Pierre Belanger <belanger@pobox.com>.
98 #include <sys/sendfile.h>
100 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
102 int sfvcnt;
103 size_t total, xferred;
104 struct sendfilevec vec[2];
105 ssize_t hdr_len = 0;
107 if (header) {
108 sfvcnt = 2;
110 vec[0].sfv_fd = SFV_FD_SELF;
111 vec[0].sfv_flag = 0;
112 vec[0].sfv_off = (off_t)header->data;
113 vec[0].sfv_len = hdr_len = header->length;
115 vec[1].sfv_fd = fromfd;
116 vec[1].sfv_flag = 0;
117 vec[1].sfv_off = offset;
118 vec[1].sfv_len = count;
120 } else {
121 sfvcnt = 1;
123 vec[0].sfv_fd = fromfd;
124 vec[0].sfv_flag = 0;
125 vec[0].sfv_off = offset;
126 vec[0].sfv_len = count;
129 total = count + hdr_len;
131 while (total) {
132 ssize_t nwritten;
135 * Although not listed in the API error returns, this is almost certainly
136 * a slow system call and will be interrupted by a signal with EINTR. JRA.
139 xferred = 0;
141 nwritten = sendfilev(tofd, vec, sfvcnt, &xferred);
142 #if defined(EWOULDBLOCK)
143 if (nwritten == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) {
144 #else
145 if (nwritten == -1 && (errno == EINTR || errno == EAGAIN)) {
146 #endif
147 if (xferred == 0)
148 continue; /* Nothing written yet. */
149 else
150 nwritten = xferred;
153 if (nwritten == -1)
154 return -1;
155 if (nwritten == 0)
156 return -1; /* I think we're at EOF here... */
159 * If this was a short (signal interrupted) write we may need
160 * to subtract it from the header data, or null out the header
161 * data altogether if we wrote more than vec[0].sfv_len bytes.
162 * We move vec[1].* to vec[0].* and set sfvcnt to 1
165 if (sfvcnt == 2 && nwritten >= vec[0].sfv_len) {
166 vec[1].sfv_off += nwritten - vec[0].sfv_len;
167 vec[1].sfv_len -= nwritten - vec[0].sfv_len;
169 /* Move vec[1].* to vec[0].* and set sfvcnt to 1 */
170 vec[0] = vec[1];
171 sfvcnt = 1;
172 } else {
173 vec[0].sfv_off += nwritten;
174 vec[0].sfv_len -= nwritten;
176 total -= nwritten;
178 return count + hdr_len;
181 #elif defined(HPUX_SENDFILE_API)
183 #include <sys/socket.h>
184 #include <sys/uio.h>
186 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
188 size_t total=0;
189 struct iovec hdtrl[2];
190 size_t hdr_len = 0;
192 if (header) {
193 /* Set up the header/trailer iovec. */
194 hdtrl[0].iov_base = (void *)header->data;
195 hdtrl[0].iov_len = hdr_len = header->length;
196 } else {
197 hdtrl[0].iov_base = NULL;
198 hdtrl[0].iov_len = hdr_len = 0;
200 hdtrl[1].iov_base = NULL;
201 hdtrl[1].iov_len = 0;
203 total = count;
204 while (total + hdtrl[0].iov_len) {
205 ssize_t nwritten;
208 * HPUX guarantees that if any data was written before
209 * a signal interrupt then sendfile returns the number of
210 * bytes written (which may be less than requested) not -1.
211 * nwritten includes the header data sent.
214 do {
215 nwritten = sendfile(tofd, fromfd, offset, total, &hdtrl[0], 0);
216 #if defined(EWOULDBLOCK)
217 } while (nwritten == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
218 #else
219 } while (nwritten == -1 && (errno == EINTR || errno == EAGAIN));
220 #endif
221 if (nwritten == -1)
222 return -1;
223 if (nwritten == 0)
224 return -1; /* I think we're at EOF here... */
227 * If this was a short (signal interrupted) write we may need
228 * to subtract it from the header data, or null out the header
229 * data altogether if we wrote more than hdtrl[0].iov_len bytes.
230 * We change nwritten to be the number of file bytes written.
233 if (hdtrl[0].iov_base && hdtrl[0].iov_len) {
234 if (nwritten >= hdtrl[0].iov_len) {
235 nwritten -= hdtrl[0].iov_len;
236 hdtrl[0].iov_base = NULL;
237 hdtrl[0].iov_len = 0;
238 } else {
239 /* iov_base is defined as a void *... */
240 hdtrl[0].iov_base = (void *)(((char *)hdtrl[0].iov_base) + nwritten);
241 hdtrl[0].iov_len -= nwritten;
242 nwritten = 0;
245 total -= nwritten;
246 offset += nwritten;
248 return count + hdr_len;
251 #elif defined(FREEBSD_SENDFILE_API) || defined(DARWIN_SENDFILE_API)
253 #include <sys/types.h>
254 #include <sys/socket.h>
255 #include <sys/uio.h>
257 ssize_t sys_sendfile(int tofd, int fromfd,
258 const DATA_BLOB *header, off_t offset, size_t count)
260 struct sf_hdtr sf_header = {0};
261 struct iovec io_header = {0};
263 off_t nwritten;
264 int ret;
266 if (header) {
267 sf_header.headers = &io_header;
268 sf_header.hdr_cnt = 1;
269 io_header.iov_base = header->data;
270 io_header.iov_len = header->length;
271 sf_header.trailers = NULL;
272 sf_header.trl_cnt = 0;
275 while (count != 0) {
277 nwritten = count;
278 #if defined(DARWIN_SENDFILE_API)
279 /* Darwin recycles nwritten as a value-result parameter, apart from that this
280 sendfile implementation is quite the same as the FreeBSD one */
281 ret = sendfile(fromfd, tofd, offset, &nwritten, &sf_header, 0);
282 #else
283 ret = sendfile(fromfd, tofd, offset, count, &sf_header, &nwritten, 0);
284 #endif
285 #if defined(EWOULDBLOCK)
286 if (ret == -1 && errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) {
287 #else
288 if (ret == -1 && errno != EINTR && errno != EAGAIN) {
289 #endif
290 /* Send failed, we are toast. */
291 return -1;
294 if (nwritten == 0) {
295 /* EOF of offset is after EOF. */
296 break;
299 if (sf_header.hdr_cnt) {
300 if (io_header.iov_len <= nwritten) {
301 /* Entire header was sent. */
302 sf_header.headers = NULL;
303 sf_header.hdr_cnt = 0;
304 nwritten -= io_header.iov_len;
305 } else {
306 /* Partial header was sent. */
307 io_header.iov_len -= nwritten;
308 io_header.iov_base =
309 ((uint8_t *)io_header.iov_base) + nwritten;
310 nwritten = 0;
314 offset += nwritten;
315 count -= nwritten;
318 return nwritten;
321 #elif defined(AIX_SENDFILE_API)
323 /* BEGIN AIX SEND_FILE */
325 /* Contributed by William Jojo <jojowil@hvcc.edu> */
326 #include <sys/socket.h>
328 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
330 struct sf_parms hdtrl;
332 /* Set up the header/trailer struct params. */
333 if (header) {
334 hdtrl.header_data = header->data;
335 hdtrl.header_length = header->length;
336 } else {
337 hdtrl.header_data = NULL;
338 hdtrl.header_length = 0;
340 hdtrl.trailer_data = NULL;
341 hdtrl.trailer_length = 0;
343 hdtrl.file_descriptor = fromfd;
344 hdtrl.file_offset = offset;
345 hdtrl.file_bytes = count;
347 while ( hdtrl.file_bytes + hdtrl.header_length ) {
348 ssize_t ret;
351 Return Value
353 There are three possible return values from send_file:
355 Value Description
357 -1 an error has occurred, errno contains the error code.
359 0 the command has completed successfully.
361 1 the command was completed partially, some data has been
362 transmitted but the command has to return for some reason,
363 for example, the command was interrupted by signals.
365 do {
366 ret = send_file(&tofd, &hdtrl, 0);
367 #if defined(EWOULDBLOCK)
368 } while ((ret == 1) || (ret == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)));
369 #else
370 } while ((ret == 1) || (ret == -1 && (errno == EINTR || errno == EAGAIN)));
371 #endif
372 if ( ret == -1 )
373 return -1;
376 return count + header->length;
378 /* END AIX SEND_FILE */
380 #else /* No sendfile implementation. Return error. */
382 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
384 /* No sendfile syscall. */
385 errno = ENOSYS;
386 return -1;
388 #endif