s3:modules: Update getdate.y to work with newer bison versions
[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                 } while (nwritten == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
63                 if (nwritten == -1) {
64                         if (errno == ENOSYS || errno == EINVAL) {
65                                 /* Ok - we're in a world of pain here. We just sent
66                                  * the header, but the sendfile failed. We have to
67                                  * emulate the sendfile at an upper layer before we
68                                  * disable it's use. So we do something really ugly.
69                                  * We set the errno to a strange value so we can detect
70                                  * this at the upper level and take care of it without
71                                  * layer violation. JRA.
72                                  */
73                                 errno = EINTR; /* Normally we can never return this. */
74                         }
75                         return -1;
76                 }
77                 if (nwritten == 0) {
78                         /*
79                          * EOF, return a short read
80                          */
81                         return hdr_len + (count - total);
82                 }
83                 total -= nwritten;
84         }
85         return count + hdr_len;
86 }
87
88 #elif defined(SOLARIS_SENDFILE_API)
89
90 /*
91  * Solaris sendfile code written by Pierre Belanger <belanger@pobox.com>.
92  */
93
94 #include <sys/sendfile.h>
95
96 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
97 {
98         int sfvcnt;
99         size_t total, xferred;
100         struct sendfilevec vec[2];
101         ssize_t hdr_len = 0;
102
103         if (header) {
104                 sfvcnt = 2;
105
106                 vec[0].sfv_fd = SFV_FD_SELF;
107                 vec[0].sfv_flag = 0;
108                 vec[0].sfv_off = (off_t)header->data;
109                 vec[0].sfv_len = hdr_len = header->length;
110
111                 vec[1].sfv_fd = fromfd;
112                 vec[1].sfv_flag = 0;
113                 vec[1].sfv_off = offset;
114                 vec[1].sfv_len = count;
115
116         } else {
117                 sfvcnt = 1;
118
119                 vec[0].sfv_fd = fromfd;
120                 vec[0].sfv_flag = 0;
121                 vec[0].sfv_off = offset;
122                 vec[0].sfv_len = count;
123         }
124
125         total = count + hdr_len;
126
127         while (total) {
128                 ssize_t nwritten;
129
130                 /*
131                  * Although not listed in the API error returns, this is almost certainly
132                  * a slow system call and will be interrupted by a signal with EINTR. JRA.
133                  */
134
135                 xferred = 0;
136
137                         nwritten = sendfilev(tofd, vec, sfvcnt, &xferred);
138                 if  (nwritten == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) {
139                         if (xferred == 0)
140                                 continue; /* Nothing written yet. */
141                         else
142                                 nwritten = xferred;
143                 }
144
145                 if (nwritten == -1)
146                         return -1;
147                 if (nwritten == 0)
148                         return -1; /* I think we're at EOF here... */
149
150                 /*
151                  * If this was a short (signal interrupted) write we may need
152                  * to subtract it from the header data, or null out the header
153                  * data altogether if we wrote more than vec[0].sfv_len bytes.
154                  * We move vec[1].* to vec[0].* and set sfvcnt to 1
155                  */
156
157                 if (sfvcnt == 2 && nwritten >= vec[0].sfv_len) {
158                         vec[1].sfv_off += nwritten - vec[0].sfv_len;
159                         vec[1].sfv_len -= nwritten - vec[0].sfv_len;
160
161                         /* Move vec[1].* to vec[0].* and set sfvcnt to 1 */
162                         vec[0] = vec[1];
163                         sfvcnt = 1;
164                 } else {
165                         vec[0].sfv_off += nwritten;
166                         vec[0].sfv_len -= nwritten;
167                 }
168                 total -= nwritten;
169         }
170         return count + hdr_len;
171 }
172
173 #elif defined(HPUX_SENDFILE_API)
174
175 #include <sys/socket.h>
176 #include <sys/uio.h>
177
178 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
179 {
180         size_t total=0;
181         struct iovec hdtrl[2];
182         size_t hdr_len = 0;
183
184         if (header) {
185                 /* Set up the header/trailer iovec. */
186                 hdtrl[0].iov_base = (void *)header->data;
187                 hdtrl[0].iov_len = hdr_len = header->length;
188         } else {
189                 hdtrl[0].iov_base = NULL;
190                 hdtrl[0].iov_len = hdr_len = 0;
191         }
192         hdtrl[1].iov_base = NULL;
193         hdtrl[1].iov_len = 0;
194
195         total = count;
196         while (total + hdtrl[0].iov_len) {
197                 ssize_t nwritten;
198
199                 /*
200                  * HPUX guarantees that if any data was written before
201                  * a signal interrupt then sendfile returns the number of
202                  * bytes written (which may be less than requested) not -1.
203                  * nwritten includes the header data sent.
204                  */
205
206                 do {
207                         nwritten = sendfile(tofd, fromfd, offset, total, &hdtrl[0], 0);
208                 } while (nwritten == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
209                 if (nwritten == -1)
210                         return -1;
211                 if (nwritten == 0)
212                         return -1; /* I think we're at EOF here... */
213
214                 /*
215                  * If this was a short (signal interrupted) write we may need
216                  * to subtract it from the header data, or null out the header
217                  * data altogether if we wrote more than hdtrl[0].iov_len bytes.
218                  * We change nwritten to be the number of file bytes written.
219                  */
220
221                 if (hdtrl[0].iov_base && hdtrl[0].iov_len) {
222                         if (nwritten >= hdtrl[0].iov_len) {
223                                 nwritten -= hdtrl[0].iov_len;
224                                 hdtrl[0].iov_base = NULL;
225                                 hdtrl[0].iov_len = 0;
226                         } else {
227                                 /* iov_base is defined as a void *... */
228                                 hdtrl[0].iov_base = (void *)(((char *)hdtrl[0].iov_base) + nwritten);
229                                 hdtrl[0].iov_len -= nwritten;
230                                 nwritten = 0;
231                         }
232                 }
233                 total -= nwritten;
234                 offset += nwritten;
235         }
236         return count + hdr_len;
237 }
238
239 #elif defined(FREEBSD_SENDFILE_API) || defined(DARWIN_SENDFILE_API)
240
241 #include <sys/types.h>
242 #include <sys/socket.h>
243 #include <sys/uio.h>
244
245 ssize_t sys_sendfile(int tofd, int fromfd,
246             const DATA_BLOB *header, off_t offset, size_t count)
247 {
248         struct sf_hdtr  sf_header = {0};
249         struct iovec    io_header = {0};
250
251         off_t   nwritten;
252         int     ret;
253
254         if (header) {
255                 sf_header.headers = &io_header;
256                 sf_header.hdr_cnt = 1;
257                 io_header.iov_base = header->data;
258                 io_header.iov_len = header->length;
259                 sf_header.trailers = NULL;
260                 sf_header.trl_cnt = 0;
261         }
262
263         while (count != 0) {
264
265                 nwritten = count;
266 #if defined(DARWIN_SENDFILE_API)
267                 /* Darwin recycles nwritten as a value-result parameter, apart from that this
268                    sendfile implementation is quite the same as the FreeBSD one */
269                 ret = sendfile(fromfd, tofd, offset, &nwritten, &sf_header, 0);
270 #else
271                 ret = sendfile(fromfd, tofd, offset, count, &sf_header, &nwritten, 0);
272 #endif
273                 if (ret == -1 && errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) {
274                         /* Send failed, we are toast. */
275                         return -1;
276                 }
277
278                 if (nwritten == 0) {
279                         /* EOF of offset is after EOF. */
280                         break;
281                 }
282
283                 if (sf_header.hdr_cnt) {
284                         if (io_header.iov_len <= nwritten) {
285                                 /* Entire header was sent. */
286                                 sf_header.headers = NULL;
287                                 sf_header.hdr_cnt = 0;
288                                 nwritten -= io_header.iov_len;
289                         } else {
290                                 /* Partial header was sent. */
291                                 io_header.iov_len -= nwritten;
292                                 io_header.iov_base =
293                                     ((uint8_t *)io_header.iov_base) + nwritten;
294                                 nwritten = 0;
295                         }
296                 }
297
298                 offset += nwritten;
299                 count -= nwritten;
300         }
301
302         return nwritten;
303 }
304
305 #elif defined(AIX_SENDFILE_API)
306
307 /* BEGIN AIX SEND_FILE */
308
309 /* Contributed by William Jojo <jojowil@hvcc.edu> */
310 #include <sys/socket.h>
311
312 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
313 {
314         struct sf_parms hdtrl;
315
316         /* Set up the header/trailer struct params. */
317         if (header) {
318                 hdtrl.header_data = header->data;
319                 hdtrl.header_length = header->length;
320         } else {
321                 hdtrl.header_data = NULL;
322                 hdtrl.header_length = 0;
323         }
324         hdtrl.trailer_data = NULL;
325         hdtrl.trailer_length = 0;
326
327         hdtrl.file_descriptor = fromfd;
328         hdtrl.file_offset = offset;
329         hdtrl.file_bytes = count;
330
331         while ( hdtrl.file_bytes + hdtrl.header_length ) {
332                 ssize_t ret;
333
334                 /*
335                  Return Value
336
337                  There are three possible return values from send_file:
338
339                  Value Description
340
341                  -1 an error has occurred, errno contains the error code.
342
343                  0 the command has completed successfully.
344
345                  1 the command was completed partially, some data has been
346                  transmitted but the command has to return for some reason,
347                  for example, the command was interrupted by signals.
348                 */
349                 do {
350                         ret = send_file(&tofd, &hdtrl, 0);
351                 } while ((ret == 1) || (ret == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)));
352                 if ( ret == -1 )
353                         return -1;
354         }
355
356         return count + header->length;
357 }
358 /* END AIX SEND_FILE */
359
360 #else /* No sendfile implementation. Return error. */
361
362 ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count)
363 {
364         /* No sendfile syscall. */
365         errno = ENOSYS;
366         return -1;
367 }
368 #endif