Fix more POSIX path lstat calls. Fix bug where close can return
[Samba.git] / source / lib / sendfile.c
blob11582af61ecc6a9beed4f7dec1bd83e984903e8e
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)
82 return -1; /* I think we're at EOF here... */
83 total -= nwritten;
85 return count + hdr_len;
88 #elif defined(LINUX_BROKEN_SENDFILE_API)
91 * We must use explicit 32 bit types here. This code path means Linux
92 * won't do proper 64-bit sendfile. JRA.
95 extern int32 sendfile (int out_fd, int in_fd, int32 *offset, uint32 count);
98 #ifndef MSG_MORE
99 #define MSG_MORE 0x8000
100 #endif
102 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
104 size_t total=0;
105 ssize_t ret;
106 ssize_t hdr_len = 0;
107 uint32 small_total = 0;
108 int32 small_offset;
111 * Fix for broken Linux 2.4 systems with no working sendfile64().
112 * If the offset+count > 2 GB then pretend we don't have the
113 * system call sendfile at all. The upper layer catches this
114 * and uses a normal read. JRA.
117 if ((sizeof(SMB_OFF_T) >= 8) && (offset + count > (SMB_OFF_T)0x7FFFFFFF)) {
118 errno = ENOSYS;
119 return -1;
123 * Send the header first.
124 * Use MSG_MORE to cork the TCP output until sendfile is called.
127 if (header) {
128 hdr_len = header->length;
129 while (total < hdr_len) {
130 ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE);
131 if (ret == -1)
132 return -1;
133 total += ret;
137 small_total = (uint32)count;
138 small_offset = (int32)offset;
140 while (small_total) {
141 int32 nwritten;
142 do {
143 nwritten = sendfile(tofd, fromfd, &small_offset, small_total);
144 } while (nwritten == -1 && errno == EINTR);
145 if (nwritten == -1) {
146 if (errno == ENOSYS || errno == EINVAL) {
147 /* Ok - we're in a world of pain here. We just sent
148 * the header, but the sendfile failed. We have to
149 * emulate the sendfile at an upper layer before we
150 * disable it's use. So we do something really ugly.
151 * We set the errno to a strange value so we can detect
152 * this at the upper level and take care of it without
153 * layer violation. JRA.
155 errno = EINTR; /* Normally we can never return this. */
157 return -1;
159 if (nwritten == 0)
160 return -1; /* I think we're at EOF here... */
161 small_total -= nwritten;
163 return count + hdr_len;
167 #elif defined(SOLARIS_SENDFILE_API)
170 * Solaris sendfile code written by Pierre Belanger <belanger@pobox.com>.
173 #include <sys/sendfile.h>
175 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
177 int sfvcnt;
178 size_t total, xferred;
179 struct sendfilevec vec[2];
180 ssize_t hdr_len = 0;
182 if (header) {
183 sfvcnt = 2;
185 vec[0].sfv_fd = SFV_FD_SELF;
186 vec[0].sfv_flag = 0;
187 vec[0].sfv_off = (off_t)header->data;
188 vec[0].sfv_len = hdr_len = header->length;
190 vec[1].sfv_fd = fromfd;
191 vec[1].sfv_flag = 0;
192 vec[1].sfv_off = offset;
193 vec[1].sfv_len = count;
195 } else {
196 sfvcnt = 1;
198 vec[0].sfv_fd = fromfd;
199 vec[0].sfv_flag = 0;
200 vec[0].sfv_off = offset;
201 vec[0].sfv_len = count;
204 total = count + hdr_len;
206 while (total) {
207 ssize_t nwritten;
210 * Although not listed in the API error returns, this is almost certainly
211 * a slow system call and will be interrupted by a signal with EINTR. JRA.
214 xferred = 0;
216 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILEV64)
217 nwritten = sendfilev64(tofd, vec, sfvcnt, &xferred);
218 #else
219 nwritten = sendfilev(tofd, vec, sfvcnt, &xferred);
220 #endif
221 if (nwritten == -1 && errno == EINTR) {
222 if (xferred == 0)
223 continue; /* Nothing written yet. */
224 else
225 nwritten = xferred;
228 if (nwritten == -1)
229 return -1;
230 if (nwritten == 0)
231 return -1; /* I think we're at EOF here... */
234 * If this was a short (signal interrupted) write we may need
235 * to subtract it from the header data, or null out the header
236 * data altogether if we wrote more than vec[0].sfv_len bytes.
237 * We move vec[1].* to vec[0].* and set sfvcnt to 1
240 if (sfvcnt == 2 && nwritten >= vec[0].sfv_len) {
241 vec[1].sfv_off += nwritten - vec[0].sfv_len;
242 vec[1].sfv_len -= nwritten - vec[0].sfv_len;
244 /* Move vec[1].* to vec[0].* and set sfvcnt to 1 */
245 vec[0] = vec[1];
246 sfvcnt = 1;
247 } else {
248 vec[0].sfv_off += nwritten;
249 vec[0].sfv_len -= nwritten;
251 total -= nwritten;
253 return count + hdr_len;
256 #elif defined(HPUX_SENDFILE_API)
258 #include <sys/socket.h>
259 #include <sys/uio.h>
261 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
263 size_t total=0;
264 struct iovec hdtrl[2];
265 size_t hdr_len = 0;
267 if (header) {
268 /* Set up the header/trailer iovec. */
269 hdtrl[0].iov_base = header->data;
270 hdtrl[0].iov_len = hdr_len = header->length;
271 } else {
272 hdtrl[0].iov_base = NULL;
273 hdtrl[0].iov_len = hdr_len = 0;
275 hdtrl[1].iov_base = NULL;
276 hdtrl[1].iov_len = 0;
278 total = count;
279 while (total + hdtrl[0].iov_len) {
280 ssize_t nwritten;
283 * HPUX guarantees that if any data was written before
284 * a signal interrupt then sendfile returns the number of
285 * bytes written (which may be less than requested) not -1.
286 * nwritten includes the header data sent.
289 do {
290 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILE64)
291 nwritten = sendfile64(tofd, fromfd, offset, total, &hdtrl[0], 0);
292 #else
293 nwritten = sendfile(tofd, fromfd, offset, total, &hdtrl[0], 0);
294 #endif
295 } while (nwritten == -1 && errno == EINTR);
296 if (nwritten == -1)
297 return -1;
298 if (nwritten == 0)
299 return -1; /* I think we're at EOF here... */
302 * If this was a short (signal interrupted) write we may need
303 * to subtract it from the header data, or null out the header
304 * data altogether if we wrote more than hdtrl[0].iov_len bytes.
305 * We change nwritten to be the number of file bytes written.
308 if (hdtrl[0].iov_base && hdtrl[0].iov_len) {
309 if (nwritten >= hdtrl[0].iov_len) {
310 nwritten -= hdtrl[0].iov_len;
311 hdtrl[0].iov_base = NULL;
312 hdtrl[0].iov_len = 0;
313 } else {
314 /* iov_base is defined as a void *... */
315 hdtrl[0].iov_base = ((char *)hdtrl[0].iov_base) + nwritten;
316 hdtrl[0].iov_len -= nwritten;
317 nwritten = 0;
320 total -= nwritten;
321 offset += nwritten;
323 return count + hdr_len;
326 #elif defined(FREEBSD_SENDFILE_API)
328 #include <sys/types.h>
329 #include <sys/socket.h>
330 #include <sys/uio.h>
332 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
334 size_t total=0;
335 struct sf_hdtr hdr;
336 struct iovec hdtrl;
337 size_t hdr_len = 0;
339 hdr.headers = &hdtrl;
340 hdr.hdr_cnt = 1;
341 hdr.trailers = NULL;
342 hdr.trl_cnt = 0;
344 /* Set up the header iovec. */
345 if (header) {
346 hdtrl.iov_base = header->data;
347 hdtrl.iov_len = hdr_len = header->length;
348 } else {
349 hdtrl.iov_base = NULL;
350 hdtrl.iov_len = 0;
353 total = count;
354 while (total + hdtrl.iov_len) {
355 SMB_OFF_T nwritten;
356 int ret;
359 * FreeBSD sendfile returns 0 on success, -1 on error.
360 * Remember, the tofd and fromfd are reversed..... :-).
361 * nwritten includes the header data sent.
364 do {
365 ret = sendfile(fromfd, tofd, offset, total, &hdr, &nwritten, 0);
366 } while (ret == -1 && errno == EINTR);
367 if (ret == -1)
368 return -1;
370 if (nwritten == 0)
371 return -1; /* I think we're at EOF here... */
374 * If this was a short (signal interrupted) write we may need
375 * to subtract it from the header data, or null out the header
376 * data altogether if we wrote more than hdtrl.iov_len bytes.
377 * We change nwritten to be the number of file bytes written.
380 if (hdtrl.iov_base && hdtrl.iov_len) {
381 if (nwritten >= hdtrl.iov_len) {
382 nwritten -= hdtrl.iov_len;
383 hdtrl.iov_base = NULL;
384 hdtrl.iov_len = 0;
385 } else {
386 hdtrl.iov_base += nwritten;
387 hdtrl.iov_len -= nwritten;
388 nwritten = 0;
391 total -= nwritten;
392 offset += nwritten;
394 return count + hdr_len;
397 #elif defined(AIX_SENDFILE_API)
399 /* BEGIN AIX SEND_FILE */
401 /* Contributed by William Jojo <jojowil@hvcc.edu> */
402 #include <sys/socket.h>
404 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
406 struct sf_parms hdtrl;
408 /* Set up the header/trailer struct params. */
409 if (header) {
410 hdtrl.header_data = header->data;
411 hdtrl.header_length = header->length;
412 } else {
413 hdtrl.header_data = NULL;
414 hdtrl.header_length = 0;
416 hdtrl.trailer_data = NULL;
417 hdtrl.trailer_length = 0;
419 hdtrl.file_descriptor = fromfd;
420 hdtrl.file_offset = offset;
421 hdtrl.file_bytes = count;
423 while ( hdtrl.file_bytes + hdtrl.header_length ) {
424 ssize_t ret;
427 Return Value
429 There are three possible return values from send_file:
431 Value Description
433 -1 an error has occurred, errno contains the error code.
435 0 the command has completed successfully.
437 1 the command was completed partially, some data has been
438 transmitted but the command has to return for some reason,
439 for example, the command was interrupted by signals.
441 do {
442 ret = send_file(&tofd, &hdtrl, 0);
443 } while ( (ret == 1) || (ret == -1 && errno == EINTR) );
444 if ( ret == -1 )
445 return -1;
448 return count + header->length;
450 /* END AIX SEND_FILE */
452 #else /* No sendfile implementation. Return error. */
454 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
456 /* No sendfile syscall. */
457 errno = ENOSYS;
458 return -1;
460 #endif