s3-talloc Change TALLOC_ZERO_P() to talloc_zero()
[nivanova/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 #if defined(EWOULDBLOCK)
67                 } while (nwritten == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
68 #else
69                 } while (nwritten == -1 && (errno == EINTR || errno == EAGAIN));
70 #endif
71                 if (nwritten == -1) {
72                         if (errno == ENOSYS || errno == EINVAL) {
73                                 /* Ok - we're in a world of pain here. We just sent
74                                  * the header, but the sendfile failed. We have to
75                                  * emulate the sendfile at an upper layer before we
76                                  * disable it's use. So we do something really ugly.
77                                  * We set the errno to a strange value so we can detect
78                                  * this at the upper level and take care of it without
79                                  * layer violation. JRA.
80                                  */
81                                 errno = EINTR; /* Normally we can never return this. */
82                         }
83                         return -1;
84                 }
85                 if (nwritten == 0) {
86                         /*
87                          * EOF, return a short read
88                          */
89                         return hdr_len + (count - total);
90                 }
91                 total -= nwritten;
92         }
93         return count + hdr_len;
94 }
95
96 #elif defined(LINUX_BROKEN_SENDFILE_API)
97
98 /*
99  * We must use explicit 32 bit types here. This code path means Linux
100  * won't do proper 64-bit sendfile. JRA.
101  */
102
103 extern int32 sendfile (int out_fd, int in_fd, int32 *offset, uint32 count);
104
105
106 #ifndef MSG_MORE
107 #define MSG_MORE 0x8000
108 #endif
109
110 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
111 {
112         size_t total=0;
113         ssize_t ret;
114         ssize_t hdr_len = 0;
115         uint32 small_total = 0;
116         int32 small_offset;
117
118         /* 
119          * Fix for broken Linux 2.4 systems with no working sendfile64().
120          * If the offset+count > 2 GB then pretend we don't have the
121          * system call sendfile at all. The upper layer catches this
122          * and uses a normal read. JRA.
123          */
124
125         if ((sizeof(SMB_OFF_T) >= 8) && (offset + count > (SMB_OFF_T)0x7FFFFFFF)) {
126                 errno = ENOSYS;
127                 return -1;
128         }
129
130         /*
131          * Send the header first.
132          * Use MSG_MORE to cork the TCP output until sendfile is called.
133          */
134
135         if (header) {
136                 hdr_len = header->length;
137                 while (total < hdr_len) {
138                         ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE);
139                         if (ret == -1)
140                                 return -1;
141                         total += ret;
142                 }
143         }
144
145         small_total = (uint32)count;
146         small_offset = (int32)offset;
147
148         while (small_total) {
149                 int32 nwritten;
150                 do {
151                         nwritten = sendfile(tofd, fromfd, &small_offset, small_total);
152 #if defined(EWOULDBLOCK)
153                 } while (nwritten == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
154 #else
155                 } while (nwritten == -1 && (errno == EINTR || errno == EAGAIN));
156 #endif
157                 if (nwritten == -1) {
158                         if (errno == ENOSYS || errno == EINVAL) {
159                                 /* Ok - we're in a world of pain here. We just sent
160                                  * the header, but the sendfile failed. We have to
161                                  * emulate the sendfile at an upper layer before we
162                                  * disable it's use. So we do something really ugly.
163                                  * We set the errno to a strange value so we can detect
164                                  * this at the upper level and take care of it without
165                                  * layer violation. JRA.
166                                  */
167                                 errno = EINTR; /* Normally we can never return this. */
168                         }
169                         return -1;
170                 }
171                 if (nwritten == 0) {
172                         /*
173                          * EOF, return a short read
174                          */
175                         return hdr_len + (((uint32)count) - small_total);
176                 }
177                 small_total -= nwritten;
178         }
179         return count + hdr_len;
180 }
181
182
183 #elif defined(SOLARIS_SENDFILE_API)
184
185 /*
186  * Solaris sendfile code written by Pierre Belanger <belanger@pobox.com>.
187  */
188
189 #include <sys/sendfile.h>
190
191 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
192 {
193         int sfvcnt;
194         size_t total, xferred;
195         struct sendfilevec vec[2];
196         ssize_t hdr_len = 0;
197
198         if (header) {
199                 sfvcnt = 2;
200
201                 vec[0].sfv_fd = SFV_FD_SELF;
202                 vec[0].sfv_flag = 0;
203                 vec[0].sfv_off = (off_t)header->data;
204                 vec[0].sfv_len = hdr_len = header->length;
205
206                 vec[1].sfv_fd = fromfd;
207                 vec[1].sfv_flag = 0;
208                 vec[1].sfv_off = offset;
209                 vec[1].sfv_len = count;
210
211         } else {
212                 sfvcnt = 1;
213
214                 vec[0].sfv_fd = fromfd;
215                 vec[0].sfv_flag = 0;
216                 vec[0].sfv_off = offset;
217                 vec[0].sfv_len = count;
218         }
219
220         total = count + hdr_len;
221
222         while (total) {
223                 ssize_t nwritten;
224
225                 /*
226                  * Although not listed in the API error returns, this is almost certainly
227                  * a slow system call and will be interrupted by a signal with EINTR. JRA.
228                  */
229
230                 xferred = 0;
231
232 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILEV64)
233                         nwritten = sendfilev64(tofd, vec, sfvcnt, &xferred);
234 #else
235                         nwritten = sendfilev(tofd, vec, sfvcnt, &xferred);
236 #endif
237 #if defined(EWOULDBLOCK)
238                 if  (nwritten == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) {
239 #else
240                 if (nwritten == -1 && (errno == EINTR || errno == EAGAIN)) {
241 #endif
242                         if (xferred == 0)
243                                 continue; /* Nothing written yet. */
244                         else
245                                 nwritten = xferred;
246                 }
247
248                 if (nwritten == -1)
249                         return -1;
250                 if (nwritten == 0)
251                         return -1; /* I think we're at EOF here... */
252
253                 /*
254                  * If this was a short (signal interrupted) write we may need
255                  * to subtract it from the header data, or null out the header
256                  * data altogether if we wrote more than vec[0].sfv_len bytes.
257                  * We move vec[1].* to vec[0].* and set sfvcnt to 1
258                  */
259
260                 if (sfvcnt == 2 && nwritten >= vec[0].sfv_len) {
261                         vec[1].sfv_off += nwritten - vec[0].sfv_len;
262                         vec[1].sfv_len -= nwritten - vec[0].sfv_len;
263
264                         /* Move vec[1].* to vec[0].* and set sfvcnt to 1 */
265                         vec[0] = vec[1];
266                         sfvcnt = 1;
267                 } else {
268                         vec[0].sfv_off += nwritten;
269                         vec[0].sfv_len -= nwritten;
270                 }
271                 total -= nwritten;
272         }
273         return count + hdr_len;
274 }
275
276 #elif defined(HPUX_SENDFILE_API)
277
278 #include <sys/socket.h>
279 #include <sys/uio.h>
280
281 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
282 {
283         size_t total=0;
284         struct iovec hdtrl[2];
285         size_t hdr_len = 0;
286
287         if (header) {
288                 /* Set up the header/trailer iovec. */
289                 hdtrl[0].iov_base = (void *)header->data;
290                 hdtrl[0].iov_len = hdr_len = header->length;
291         } else {
292                 hdtrl[0].iov_base = NULL;
293                 hdtrl[0].iov_len = hdr_len = 0;
294         }
295         hdtrl[1].iov_base = NULL;
296         hdtrl[1].iov_len = 0;
297
298         total = count;
299         while (total + hdtrl[0].iov_len) {
300                 ssize_t nwritten;
301
302                 /*
303                  * HPUX guarantees that if any data was written before
304                  * a signal interrupt then sendfile returns the number of
305                  * bytes written (which may be less than requested) not -1.
306                  * nwritten includes the header data sent.
307                  */
308
309                 do {
310 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILE64)
311                         nwritten = sendfile64(tofd, fromfd, offset, total, &hdtrl[0], 0);
312 #else
313                         nwritten = sendfile(tofd, fromfd, offset, total, &hdtrl[0], 0);
314 #endif
315 #if defined(EWOULDBLOCK)
316                 } while (nwritten == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
317 #else
318                 } while (nwritten == -1 && (errno == EINTR || errno == EAGAIN));
319 #endif
320                 if (nwritten == -1)
321                         return -1;
322                 if (nwritten == 0)
323                         return -1; /* I think we're at EOF here... */
324
325                 /*
326                  * If this was a short (signal interrupted) write we may need
327                  * to subtract it from the header data, or null out the header
328                  * data altogether if we wrote more than hdtrl[0].iov_len bytes.
329                  * We change nwritten to be the number of file bytes written.
330                  */
331
332                 if (hdtrl[0].iov_base && hdtrl[0].iov_len) {
333                         if (nwritten >= hdtrl[0].iov_len) {
334                                 nwritten -= hdtrl[0].iov_len;
335                                 hdtrl[0].iov_base = NULL;
336                                 hdtrl[0].iov_len = 0;
337                         } else {
338                                 /* iov_base is defined as a void *... */
339                                 hdtrl[0].iov_base = (void *)(((char *)hdtrl[0].iov_base) + nwritten);
340                                 hdtrl[0].iov_len -= nwritten;
341                                 nwritten = 0;
342                         }
343                 }
344                 total -= nwritten;
345                 offset += nwritten;
346         }
347         return count + hdr_len;
348 }
349
350 #elif defined(FREEBSD_SENDFILE_API)
351
352 #include <sys/types.h>
353 #include <sys/socket.h>
354 #include <sys/uio.h>
355
356 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
357 {
358         size_t total=0;
359         struct sf_hdtr hdr;
360         struct iovec hdtrl;
361         size_t hdr_len = 0;
362
363         hdr.headers = &hdtrl;
364         hdr.hdr_cnt = 1;
365         hdr.trailers = NULL;
366         hdr.trl_cnt = 0;
367
368         /* Set up the header iovec. */
369         if (header) {
370                 hdtrl.iov_base = (void *)header->data;
371                 hdtrl.iov_len = hdr_len = header->length;
372         } else {
373                 hdtrl.iov_base = NULL;
374                 hdtrl.iov_len = 0;
375         }
376
377         total = count;
378         while (total + hdtrl.iov_len) {
379                 SMB_OFF_T nwritten;
380                 int ret;
381
382                 /*
383                  * FreeBSD sendfile returns 0 on success, -1 on error.
384                  * Remember, the tofd and fromfd are reversed..... :-).
385                  * nwritten includes the header data sent.
386                  */
387
388                 do {
389                         ret = sendfile(fromfd, tofd, offset, total, &hdr, &nwritten, 0);
390 #if defined(EWOULDBLOCK)
391                 } while (ret == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
392 #else
393                 } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
394 #endif
395                 if (ret == -1)
396                         return -1;
397
398                 if (nwritten == 0)
399                         return -1; /* I think we're at EOF here... */
400
401                 /*
402                  * If this was a short (signal interrupted) write we may need
403                  * to subtract it from the header data, or null out the header
404                  * data altogether if we wrote more than hdtrl.iov_len bytes.
405                  * We change nwritten to be the number of file bytes written.
406                  */
407
408                 if (hdtrl.iov_base && hdtrl.iov_len) {
409                         if (nwritten >= hdtrl.iov_len) {
410                                 nwritten -= hdtrl.iov_len;
411                                 hdtrl.iov_base = NULL;
412                                 hdtrl.iov_len = 0;
413                         } else {
414                                 hdtrl.iov_base =
415                                     (void *)((caddr_t)hdtrl.iov_base + nwritten);
416                                 hdtrl.iov_len -= nwritten;
417                                 nwritten = 0;
418                         }
419                 }
420                 total -= nwritten;
421                 offset += nwritten;
422         }
423         return count + hdr_len;
424 }
425
426 #elif defined(AIX_SENDFILE_API)
427
428 /* BEGIN AIX SEND_FILE */
429
430 /* Contributed by William Jojo <jojowil@hvcc.edu> */
431 #include <sys/socket.h>
432
433 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
434 {
435         struct sf_parms hdtrl;
436
437         /* Set up the header/trailer struct params. */
438         if (header) {
439                 hdtrl.header_data = header->data;
440                 hdtrl.header_length = header->length;
441         } else {
442                 hdtrl.header_data = NULL;
443                 hdtrl.header_length = 0;
444         }
445         hdtrl.trailer_data = NULL;
446         hdtrl.trailer_length = 0;
447
448         hdtrl.file_descriptor = fromfd;
449         hdtrl.file_offset = offset;
450         hdtrl.file_bytes = count;
451
452         while ( hdtrl.file_bytes + hdtrl.header_length ) {
453                 ssize_t ret;
454
455                 /*
456                  Return Value
457
458                  There are three possible return values from send_file:
459
460                  Value Description
461
462                  -1 an error has occurred, errno contains the error code.
463
464                  0 the command has completed successfully.
465
466                  1 the command was completed partially, some data has been
467                  transmitted but the command has to return for some reason,
468                  for example, the command was interrupted by signals.
469                 */
470                 do {
471                         ret = send_file(&tofd, &hdtrl, 0);
472 #if defined(EWOULDBLOCK)
473                 } while ((ret == 1) || (ret == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)));
474 #else
475                 } while ((ret == 1) || (ret == -1 && (errno == EINTR || errno == EAGAIN)));
476 #endif
477                 if ( ret == -1 )
478                         return -1;
479         }
480
481         return count + header->length;
482 }
483 /* END AIX SEND_FILE */
484
485 #else /* No sendfile implementation. Return error. */
486
487 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
488 {
489         /* No sendfile syscall. */
490         errno = ENOSYS;
491         return -1;
492 }
493 #endif