1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) Nginx, Inc.
5  */
6 
7 
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_event.h>
11 
12 
13 #if (NGX_TEST_BUILD_SOLARIS_SENDFILEV)
14 
15 /* Solaris declarations */
16 
17 typedef struct sendfilevec {
18     int     sfv_fd;
19     u_int   sfv_flag;
20     off_t   sfv_off;
21     size_t  sfv_len;
22 } sendfilevec_t;
23 
24 #define SFV_FD_SELF  -2
25 
sendfilev(int fd,const struct sendfilevec * vec,int sfvcnt,size_t * xferred)26 static ssize_t sendfilev(int fd, const struct sendfilevec *vec,
27     int sfvcnt, size_t *xferred)
28 {
29     return -1;
30 }
31 
32 ngx_chain_t *ngx_solaris_sendfilev_chain(ngx_connection_t *c, ngx_chain_t *in,
33     off_t limit);
34 
35 #endif
36 
37 
38 #define NGX_SENDFILEVECS  NGX_IOVS_PREALLOCATE
39 
40 
41 ngx_chain_t *
ngx_solaris_sendfilev_chain(ngx_connection_t * c,ngx_chain_t * in,off_t limit)42 ngx_solaris_sendfilev_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
43 {
44     int             fd;
45     u_char         *prev;
46     off_t           size, send, prev_send, aligned, fprev;
47     size_t          sent;
48     ssize_t         n;
49     ngx_int_t       eintr;
50     ngx_err_t       err;
51     ngx_buf_t      *file;
52     ngx_uint_t      nsfv;
53     sendfilevec_t  *sfv, sfvs[NGX_SENDFILEVECS];
54     ngx_event_t    *wev;
55     ngx_chain_t    *cl;
56 
57     wev = c->write;
58 
59     if (!wev->ready) {
60         return in;
61     }
62 
63     if (!c->sendfile) {
64         return ngx_writev_chain(c, in, limit);
65     }
66 
67 
68     /* the maximum limit size is the maximum size_t value - the page size */
69 
70     if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {
71         limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;
72     }
73 
74 
75     send = 0;
76 
77     for ( ;; ) {
78         fd = SFV_FD_SELF;
79         prev = NULL;
80         fprev = 0;
81         file = NULL;
82         sfv = NULL;
83         eintr = 0;
84         sent = 0;
85         prev_send = send;
86 
87         nsfv = 0;
88 
89         /* create the sendfilevec and coalesce the neighbouring bufs */
90 
91         for (cl = in; cl && send < limit; cl = cl->next) {
92 
93             if (ngx_buf_special(cl->buf)) {
94                 continue;
95             }
96 
97             if (ngx_buf_in_memory_only(cl->buf)) {
98                 fd = SFV_FD_SELF;
99 
100                 size = cl->buf->last - cl->buf->pos;
101 
102                 if (send + size > limit) {
103                     size = limit - send;
104                 }
105 
106                 if (prev == cl->buf->pos) {
107                     sfv->sfv_len += (size_t) size;
108 
109                 } else {
110                     if (nsfv == NGX_SENDFILEVECS) {
111                         break;
112                     }
113 
114                     sfv = &sfvs[nsfv++];
115 
116                     sfv->sfv_fd = SFV_FD_SELF;
117                     sfv->sfv_flag = 0;
118                     sfv->sfv_off = (off_t) (uintptr_t) cl->buf->pos;
119                     sfv->sfv_len = (size_t) size;
120                 }
121 
122                 prev = cl->buf->pos + (size_t) size;
123                 send += size;
124 
125             } else {
126                 prev = NULL;
127 
128                 size = cl->buf->file_last - cl->buf->file_pos;
129 
130                 if (send + size > limit) {
131                     size = limit - send;
132 
133                     aligned = (cl->buf->file_pos + size + ngx_pagesize - 1)
134                                & ~((off_t) ngx_pagesize - 1);
135 
136                     if (aligned <= cl->buf->file_last) {
137                         size = aligned - cl->buf->file_pos;
138                     }
139                 }
140 
141                 if (fd == cl->buf->file->fd && fprev == cl->buf->file_pos) {
142                     sfv->sfv_len += (size_t) size;
143 
144                 } else {
145                     if (nsfv == NGX_SENDFILEVECS) {
146                         break;
147                     }
148 
149                     sfv = &sfvs[nsfv++];
150 
151                     fd = cl->buf->file->fd;
152                     sfv->sfv_fd = fd;
153                     sfv->sfv_flag = 0;
154                     sfv->sfv_off = cl->buf->file_pos;
155                     sfv->sfv_len = (size_t) size;
156                 }
157 
158                 file = cl->buf;
159                 fprev = cl->buf->file_pos + size;
160                 send += size;
161             }
162         }
163 
164         n = sendfilev(c->fd, sfvs, nsfv, &sent);
165 
166         if (n == -1) {
167             err = ngx_errno;
168 
169             switch (err) {
170             case NGX_EAGAIN:
171                 break;
172 
173             case NGX_EINTR:
174                 eintr = 1;
175                 break;
176 
177             default:
178                 wev->error = 1;
179                 ngx_connection_error(c, err, "sendfilev() failed");
180                 return NGX_CHAIN_ERROR;
181             }
182 
183             ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err,
184                           "sendfilev() sent only %uz bytes", sent);
185 
186         } else if (n == 0 && sent == 0) {
187 
188             /*
189              * sendfilev() is documented to return -1 with errno
190              * set to EINVAL if svf_len is greater than the file size,
191              * but at least Solaris 11 returns 0 instead
192              */
193 
194             if (file) {
195                 ngx_log_error(NGX_LOG_ALERT, c->log, 0,
196                         "sendfilev() reported that \"%s\" was truncated at %O",
197                         file->file->name.data, file->file_pos);
198 
199             } else {
200                 ngx_log_error(NGX_LOG_ALERT, c->log, 0,
201                               "sendfilev() returned 0 with memory buffers");
202             }
203 
204             return NGX_CHAIN_ERROR;
205         }
206 
207         ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
208                        "sendfilev: %z %z", n, sent);
209 
210         c->sent += sent;
211 
212         in = ngx_chain_update_sent(in, sent);
213 
214         if (eintr) {
215             send = prev_send + sent;
216             continue;
217         }
218 
219         if (send - prev_send != (off_t) sent) {
220             wev->ready = 0;
221             return in;
222         }
223 
224         if (send >= limit || in == NULL) {
225             return in;
226         }
227     }
228 }
229