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