Use new common function.
[Samba/gebeck_regimport.git] / source3 / lib / sendfile.c
blob196ef6889b910f37fe6c06984d0ba846ba31600b
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)
253 #include <sys/types.h>
254 #include <sys/socket.h>
255 #include <sys/uio.h>
257 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
259 size_t total=0;
260 struct sf_hdtr hdr;
261 struct iovec hdtrl;
262 size_t hdr_len = 0;
264 hdr.headers = &hdtrl;
265 hdr.hdr_cnt = 1;
266 hdr.trailers = NULL;
267 hdr.trl_cnt = 0;
269 /* Set up the header iovec. */
270 if (header) {
271 hdtrl.iov_base = (void *)header->data;
272 hdtrl.iov_len = hdr_len = header->length;
273 } else {
274 hdtrl.iov_base = NULL;
275 hdtrl.iov_len = 0;
278 total = count;
279 while (total + hdtrl.iov_len) {
280 off_t nwritten;
281 int ret;
284 * FreeBSD sendfile returns 0 on success, -1 on error.
285 * Remember, the tofd and fromfd are reversed..... :-).
286 * nwritten includes the header data sent.
289 do {
290 ret = sendfile(fromfd, tofd, offset, total, &hdr, &nwritten, 0);
291 #if defined(EWOULDBLOCK)
292 } while (ret == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
293 #else
294 } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
295 #endif
296 if (ret == -1)
297 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.iov_len bytes.
306 * We change nwritten to be the number of file bytes written.
309 if (hdtrl.iov_base && hdtrl.iov_len) {
310 if (nwritten >= hdtrl.iov_len) {
311 nwritten -= hdtrl.iov_len;
312 hdtrl.iov_base = NULL;
313 hdtrl.iov_len = 0;
314 } else {
315 hdtrl.iov_base =
316 (void *)((caddr_t)hdtrl.iov_base + nwritten);
317 hdtrl.iov_len -= nwritten;
318 nwritten = 0;
321 total -= nwritten;
322 offset += nwritten;
324 return count + hdr_len;
327 #elif defined(AIX_SENDFILE_API)
329 /* BEGIN AIX SEND_FILE */
331 /* Contributed by William Jojo <jojowil@hvcc.edu> */
332 #include <sys/socket.h>
334 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
336 struct sf_parms hdtrl;
338 /* Set up the header/trailer struct params. */
339 if (header) {
340 hdtrl.header_data = header->data;
341 hdtrl.header_length = header->length;
342 } else {
343 hdtrl.header_data = NULL;
344 hdtrl.header_length = 0;
346 hdtrl.trailer_data = NULL;
347 hdtrl.trailer_length = 0;
349 hdtrl.file_descriptor = fromfd;
350 hdtrl.file_offset = offset;
351 hdtrl.file_bytes = count;
353 while ( hdtrl.file_bytes + hdtrl.header_length ) {
354 ssize_t ret;
357 Return Value
359 There are three possible return values from send_file:
361 Value Description
363 -1 an error has occurred, errno contains the error code.
365 0 the command has completed successfully.
367 1 the command was completed partially, some data has been
368 transmitted but the command has to return for some reason,
369 for example, the command was interrupted by signals.
371 do {
372 ret = send_file(&tofd, &hdtrl, 0);
373 #if defined(EWOULDBLOCK)
374 } while ((ret == 1) || (ret == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)));
375 #else
376 } while ((ret == 1) || (ret == -1 && (errno == EINTR || errno == EAGAIN)));
377 #endif
378 if ( ret == -1 )
379 return -1;
382 return count + header->length;
384 /* END AIX SEND_FILE */
386 #else /* No sendfile implementation. Return error. */
388 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
390 /* No sendfile syscall. */
391 errno = ENOSYS;
392 return -1;
394 #endif