r23801: The FSF has moved around a lot. This fixes their Mass Ave address.
[bbaumbach/samba-autobuild/.git] / source3 / lib / sendfile.c
1 /*
2  Unix SMB/Netbios implementation.
3  Version 2.2.x / 3.0.x
4  sendfile implementations.
5  Copyright (C) Jeremy Allison 2002.
6
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.
15
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/>.
18 */
19
20 /*
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.
24  */
25
26 #include "includes.h"
27
28 #if defined(LINUX_SENDFILE_API)
29
30 #include <sys/sendfile.h>
31
32 #ifndef MSG_MORE
33 #define MSG_MORE 0x8000
34 #endif
35
36 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
37 {
38         size_t total=0;
39         ssize_t ret;
40         size_t hdr_len = 0;
41
42         /*
43          * Send the header first.
44          * Use MSG_MORE to cork the TCP output until sendfile is called.
45          */
46
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;
54                 }
55         }
56
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) {
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.
76                                  */
77                                 errno = EINTR; /* Normally we can never return this. */
78                         }
79                         return -1;
80                 }
81                 if (nwritten == 0)
82                         return -1; /* I think we're at EOF here... */
83                 total -= nwritten;
84         }
85         return count + hdr_len;
86 }
87
88 #elif defined(LINUX_BROKEN_SENDFILE_API)
89
90 /*
91  * We must use explicit 32 bit types here. This code path means Linux
92  * won't do proper 64-bit sendfile. JRA.
93  */
94
95 extern int32 sendfile (int out_fd, int in_fd, int32 *offset, uint32 count);
96
97
98 #ifndef MSG_MORE
99 #define MSG_MORE 0x8000
100 #endif
101
102 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
103 {
104         size_t total=0;
105         ssize_t ret;
106         ssize_t hdr_len = 0;
107         uint32 small_total = 0;
108         int32 small_offset;
109
110         /* 
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.
115          */
116
117         if ((sizeof(SMB_OFF_T) >= 8) && (offset + count > (SMB_OFF_T)0x7FFFFFFF)) {
118                 errno = ENOSYS;
119                 return -1;
120         }
121
122         /*
123          * Send the header first.
124          * Use MSG_MORE to cork the TCP output until sendfile is called.
125          */
126
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;
134                 }
135         }
136
137         small_total = (uint32)count;
138         small_offset = (int32)offset;
139
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) {
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.
154                                  */
155                                 errno = EINTR; /* Normally we can never return this. */
156                         }
157                         return -1;
158                 }
159                 if (nwritten == 0)
160                         return -1; /* I think we're at EOF here... */
161                 small_total -= nwritten;
162         }
163         return count + hdr_len;
164 }
165
166
167 #elif defined(SOLARIS_SENDFILE_API)
168
169 /*
170  * Solaris sendfile code written by Pierre Belanger <belanger@pobox.com>.
171  */
172
173 #include <sys/sendfile.h>
174
175 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
176 {
177         int sfvcnt;
178         size_t total, xferred;
179         struct sendfilevec vec[2];
180         ssize_t hdr_len = 0;
181
182         if (header) {
183                 sfvcnt = 2;
184
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;
189
190                 vec[1].sfv_fd = fromfd;
191                 vec[1].sfv_flag = 0;
192                 vec[1].sfv_off = offset;
193                 vec[1].sfv_len = count;
194
195         } else {
196                 sfvcnt = 1;
197
198                 vec[0].sfv_fd = fromfd;
199                 vec[0].sfv_flag = 0;
200                 vec[0].sfv_off = offset;
201                 vec[0].sfv_len = count;
202         }
203
204         total = count + hdr_len;
205
206         while (total) {
207                 ssize_t nwritten;
208
209                 /*
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.
212                  */
213
214                 xferred = 0;
215
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;
226                 }
227
228                 if (nwritten == -1)
229                         return -1;
230                 if (nwritten == 0)
231                         return -1; /* I think we're at EOF here... */
232
233                 /*
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
238                  */
239
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;
243
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;
250                 }
251                 total -= nwritten;
252         }
253         return count + hdr_len;
254 }
255
256 #elif defined(HPUX_SENDFILE_API)
257
258 #include <sys/socket.h>
259 #include <sys/uio.h>
260
261 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
262 {
263         size_t total=0;
264         struct iovec hdtrl[2];
265         size_t hdr_len = 0;
266
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;
274         }
275         hdtrl[1].iov_base = NULL;
276         hdtrl[1].iov_len = 0;
277
278         total = count;
279         while (total + hdtrl[0].iov_len) {
280                 ssize_t nwritten;
281
282                 /*
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.
287                  */
288
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... */
300
301                 /*
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.
306                  */
307
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;
318                         }
319                 }
320                 total -= nwritten;
321                 offset += nwritten;
322         }
323         return count + hdr_len;
324 }
325
326 #elif defined(FREEBSD_SENDFILE_API)
327
328 #include <sys/types.h>
329 #include <sys/socket.h>
330 #include <sys/uio.h>
331
332 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
333 {
334         size_t total=0;
335         struct sf_hdtr hdr;
336         struct iovec hdtrl;
337         size_t hdr_len = 0;
338
339         hdr.headers = &hdtrl;
340         hdr.hdr_cnt = 1;
341         hdr.trailers = NULL;
342         hdr.trl_cnt = 0;
343
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;
351         }
352
353         total = count;
354         while (total + hdtrl.iov_len) {
355                 SMB_OFF_T nwritten;
356                 int ret;
357
358                 /*
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.
362                  */
363
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;
369
370                 if (nwritten == 0)
371                         return -1; /* I think we're at EOF here... */
372
373                 /*
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.
378                  */
379
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;
389                         }
390                 }
391                 total -= nwritten;
392                 offset += nwritten;
393         }
394         return count + hdr_len;
395 }
396
397 #elif defined(AIX_SENDFILE_API)
398
399 /* BEGIN AIX SEND_FILE */
400
401 /* Contributed by William Jojo <jojowil@hvcc.edu> */
402 #include <sys/socket.h>
403
404 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
405 {
406         struct sf_parms hdtrl;
407
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;
415         }
416         hdtrl.trailer_data = NULL;
417         hdtrl.trailer_length = 0;
418
419         hdtrl.file_descriptor = fromfd;
420         hdtrl.file_offset = offset;
421         hdtrl.file_bytes = count;
422
423         while ( hdtrl.file_bytes + hdtrl.header_length ) {
424                 ssize_t ret;
425
426                 /*
427                  Return Value
428
429                  There are three possible return values from send_file:
430
431                  Value Description
432
433                  -1 an error has occurred, errno contains the error code.
434
435                  0 the command has completed successfully.
436
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.
440                 */
441                 do {
442                         ret = send_file(&tofd, &hdtrl, 0);
443                 } while ( (ret == 1) || (ret == -1 && errno == EINTR) );
444                 if ( ret == -1 )
445                         return -1;
446         }
447
448         return count + header->length;
449 }
450 /* END AIX SEND_FILE */
451
452 #else /* No sendfile implementation. Return error. */
453
454 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
455 {
456         /* No sendfile syscall. */
457         errno = ENOSYS;
458         return -1;
459 }
460 #endif