add fchownat(2) system call
[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  * $DragonFly: src/lib/libc_r/uthread/uthread_sendfile.c,v 1.2 2003/06/17 04:26:48 dillon Exp $
31  */
32
33 #include <sys/fcntl.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/uio.h>
37 #include <errno.h>
38 #include <pthread.h>
39 #include "pthread_private.h"
40
41 int
42 sendfile(int fd, int s, off_t offset, size_t nbytes, struct sf_hdtr *hdtr,
43     off_t *sbytes, int flags)
44 {
45         struct pthread  *curthread = _get_curthread();
46         int     type, blocking;
47         int     ret = 0;
48         ssize_t wvret, num = 0;
49         off_t   n, nwritten = 0;
50
51         /*
52          * Write the headers if any.
53          * If some data is written but not all we must return here.
54          */
55         if ((hdtr != NULL) && (hdtr->headers != NULL)) {
56                 if ((wvret = writev(s, hdtr->headers, hdtr->hdr_cnt)) == -1) {
57                         ret = -1;
58                         goto ERROR;
59                 } else {
60                         int i;
61                         ssize_t hdrtot;
62
63                         nwritten += wvret;
64
65                         for (i = 0, hdrtot = 0; i < hdtr->hdr_cnt; i++)
66                                 hdrtot += hdtr->headers[i].iov_len;
67                         if (wvret < hdrtot)
68                                 goto SHORT_WRITE;
69                 }
70         }
71         
72         /* Lock the descriptors. */
73         if ((ret = _FD_LOCK(fd, FD_READ, NULL)) != 0) {
74                 ret = -1;
75                 errno = EBADF;
76                 goto ERROR;
77         }
78         if ((ret = _FD_LOCK(s, FD_WRITE, NULL)) != 0) {
79                 ret = -1;
80                 errno = EBADF;
81                 goto ERROR_1;
82         }
83         
84         /* Check the descriptor access modes. */
85         type = _thread_fd_getflags(fd) & O_ACCMODE;
86         if (type != O_RDONLY && type != O_RDWR) {
87                 /* File is not open for read. */
88                 ret = -1;
89                 errno = EBADF;
90                 goto ERROR_2;
91         }
92         type = _thread_fd_getflags(s) & O_ACCMODE;
93         if (type != O_WRONLY && type != O_RDWR) {
94                 /* File is not open for write. */
95                 ret = -1;
96                 errno = EBADF;
97                 goto ERROR_2;
98         }
99
100         /* Check if file operations are to block */
101         blocking = ((_thread_fd_getflags(s) & O_NONBLOCK) == 0);
102
103         /*
104          * Loop while no error occurs and until the expected number of bytes are
105          * written.
106          */
107         for (;;) {
108                 /* Perform a non-blocking sendfile syscall. */
109                 ret = __sys_sendfile(fd, s, offset + num, nbytes - num,
110                     NULL, &n, flags);
111
112                 /*
113                  * We have to handle the sideways return path of sendfile.
114                  *
115                  * If the result is 0, we're done.
116                  * If the result is anything else check the errno.
117                  * If the errno is not EGAIN return the error.
118                  * Otherwise, take into account how much
119                  * sendfile may have written for us because sendfile can
120                  * return EAGAIN even though it has written data.
121                  *
122                  * We don't clear 'ret' because the sendfile(2) syscall
123                  * would not have either.
124                  */
125                 if (ret == 0) {
126                         /* Writing completed. */
127                         num += n;
128                         break;
129                 } else if ((ret == -1) && (errno == EAGAIN)) {
130                         /*
131                          * Some bytes were written but there are still more to
132                          * write.
133                          */
134
135                         /* Update the count of bytes written. */
136                         num += n;
137
138                         /*
139                          * If we're not blocking then return.
140                          */
141                         if (!blocking) {
142                                 _FD_UNLOCK(s, FD_WRITE);
143                                 _FD_UNLOCK(fd, FD_READ);
144                                 goto SHORT_WRITE;
145                         }
146
147                         /*
148                          * Otherwise wait on the fd.
149                          */
150                         curthread->data.fd.fd = fd;
151                         _thread_kern_set_timeout(NULL);
152
153                         /* Reset the interrupted operation flag. */
154                         curthread->interrupted = 0;
155
156                         _thread_kern_sched_state(PS_FDW_WAIT, __FILE__,
157                             __LINE__);
158
159                         if (curthread->interrupted) {
160                                 /* Interrupted by a signal.  Return an error. */
161                                 break;
162                         }
163                 } else {
164                         /* Incomplete non-blocking syscall, or error. */
165                         break;
166                 }
167         }
168
169   ERROR_2:
170         _FD_UNLOCK(s, FD_WRITE);
171   ERROR_1:
172         _FD_UNLOCK(fd, FD_READ);
173   ERROR:
174         if (ret == 0) {
175                 /* Write the trailers, if any. */
176                 if ((hdtr != NULL) && (hdtr->trailers != NULL)) {
177                         if ((wvret = writev(s, hdtr->trailers, hdtr->trl_cnt))
178                             == -1)
179                                 ret = -1;
180                         else
181                                 nwritten += wvret;
182                 }
183         }
184   SHORT_WRITE:
185         if (sbytes != NULL) {
186                 /*
187                  * Number of bytes written in headers/trailers, plus in the main
188                  * sendfile() loop.
189                  */
190                 *sbytes = nwritten + num;
191         }
192         return (ret);
193 }