Initial import from FreeBSD RELENG_4:
[dragonfly.git] / lib / libc_r / uthread / uthread_sendfile.c
1 /*
2  * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>.
3  * All rights reserved.
4  * 
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice(s), this list of conditions and the following disclaimer as
10  *    the first lines of this file unmodified other than the possible
11  *    addition of one or more copyright notices.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice(s), this list of conditions and the following disclaimer in
14  *    the documentation and/or other materials provided with the
15  *    distribution.
16  * 
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  * $FreeBSD: src/lib/libc_r/uthread/uthread_sendfile.c,v 1.2.2.10 2002/10/22 14:44:03 fjoe Exp $
30  */
31
32 #include <sys/fcntl.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <sys/uio.h>
36 #include <errno.h>
37 #include <pthread.h>
38 #include "pthread_private.h"
39
40 int
41 sendfile(int fd, int s, off_t offset, size_t nbytes, struct sf_hdtr *hdtr,
42     off_t *sbytes, int flags)
43 {
44         struct pthread  *curthread = _get_curthread();
45         int     type, blocking;
46         int     ret = 0;
47         ssize_t wvret, num = 0;
48         off_t   n, nwritten = 0;
49
50         /*
51          * Write the headers if any.
52          * If some data is written but not all we must return here.
53          */
54         if ((hdtr != NULL) && (hdtr->headers != NULL)) {
55                 if ((wvret = writev(s, hdtr->headers, hdtr->hdr_cnt)) == -1) {
56                         ret = -1;
57                         goto ERROR;
58                 } else {
59                         int i;
60                         ssize_t hdrtot;
61
62                         nwritten += wvret;
63
64                         for (i = 0, hdrtot = 0; i < hdtr->hdr_cnt; i++)
65                                 hdrtot += hdtr->headers[i].iov_len;
66                         if (wvret < hdrtot)
67                                 goto SHORT_WRITE;
68                 }
69         }
70         
71         /* Lock the descriptors. */
72         if ((ret = _FD_LOCK(fd, FD_READ, NULL)) != 0) {
73                 ret = -1;
74                 errno = EBADF;
75                 goto ERROR;
76         }
77         if ((ret = _FD_LOCK(s, FD_WRITE, NULL)) != 0) {
78                 ret = -1;
79                 errno = EBADF;
80                 goto ERROR_1;
81         }
82         
83         /* Check the descriptor access modes. */
84         type = _thread_fd_getflags(fd) & O_ACCMODE;
85         if (type != O_RDONLY && type != O_RDWR) {
86                 /* File is not open for read. */
87                 ret = -1;
88                 errno = EBADF;
89                 goto ERROR_2;
90         }
91         type = _thread_fd_getflags(s) & O_ACCMODE;
92         if (type != O_WRONLY && type != O_RDWR) {
93                 /* File is not open for write. */
94                 ret = -1;
95                 errno = EBADF;
96                 goto ERROR_2;
97         }
98
99         /* Check if file operations are to block */
100         blocking = ((_thread_fd_getflags(s) & O_NONBLOCK) == 0);
101
102         /*
103          * Loop while no error occurs and until the expected number of bytes are
104          * written.
105          */
106         for (;;) {
107                 /* Perform a non-blocking sendfile syscall. */
108                 ret = __sys_sendfile(fd, s, offset + num, nbytes - num,
109                     NULL, &n, flags);
110
111                 /*
112                  * We have to handle the sideways return path of sendfile.
113                  *
114                  * If the result is 0, we're done.
115                  * If the result is anything else check the errno.
116                  * If the errno is not EGAIN return the error.
117                  * Otherwise, take into account how much
118                  * sendfile may have written for us because sendfile can
119                  * return EAGAIN even though it has written data.
120                  *
121                  * We don't clear 'ret' because the sendfile(2) syscall
122                  * would not have either.
123                  */
124                 if (ret == 0) {
125                         /* Writing completed. */
126                         num += n;
127                         break;
128                 } else if ((ret == -1) && (errno == EAGAIN)) {
129                         /*
130                          * Some bytes were written but there are still more to
131                          * write.
132                          */
133
134                         /* Update the count of bytes written. */
135                         num += n;
136
137                         /*
138                          * If we're not blocking then return.
139                          */
140                         if (!blocking) {
141                                 _FD_UNLOCK(s, FD_WRITE);
142                                 _FD_UNLOCK(fd, FD_READ);
143                                 goto SHORT_WRITE;
144                         }
145
146                         /*
147                          * Otherwise wait on the fd.
148                          */
149                         curthread->data.fd.fd = fd;
150                         _thread_kern_set_timeout(NULL);
151
152                         /* Reset the interrupted operation flag. */
153                         curthread->interrupted = 0;
154
155                         _thread_kern_sched_state(PS_FDW_WAIT, __FILE__,
156                             __LINE__);
157
158                         if (curthread->interrupted) {
159                                 /* Interrupted by a signal.  Return an error. */
160                                 break;
161                         }
162                 } else {
163                         /* Incomplete non-blocking syscall, or error. */
164                         break;
165                 }
166         }
167
168   ERROR_2:
169         _FD_UNLOCK(s, FD_WRITE);
170   ERROR_1:
171         _FD_UNLOCK(fd, FD_READ);
172   ERROR:
173         if (ret == 0) {
174                 /* Write the trailers, if any. */
175                 if ((hdtr != NULL) && (hdtr->trailers != NULL)) {
176                         if ((wvret = writev(s, hdtr->trailers, hdtr->trl_cnt))
177                             == -1)
178                                 ret = -1;
179                         else
180                                 nwritten += wvret;
181                 }
182         }
183   SHORT_WRITE:
184         if (sbytes != NULL) {
185                 /*
186                  * Number of bytes written in headers/trailers, plus in the main
187                  * sendfile() loop.
188                  */
189                 *sbytes = nwritten + num;
190         }
191         return (ret);
192 }