/src/httpd/srclib/apr/buckets/apr_buckets_file.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Licensed to the Apache Software Foundation (ASF) under one or more |
2 | | * contributor license agreements. See the NOTICE file distributed with |
3 | | * this work for additional information regarding copyright ownership. |
4 | | * The ASF licenses this file to You under the Apache License, Version 2.0 |
5 | | * (the "License"); you may not use this file except in compliance with |
6 | | * the License. You may obtain a copy of the License at |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | |
17 | | #include "apr.h" |
18 | | #include "apr_general.h" |
19 | | #include "apr_file_io.h" |
20 | | #include "apr_buckets.h" |
21 | | |
22 | | #if APR_HAS_MMAP |
23 | | #include "apr_mmap.h" |
24 | | |
25 | | /* mmap support for static files based on ideas from John Heidemann's |
26 | | * patch against 1.0.5. See |
27 | | * <http://www.isi.edu/~johnh/SOFTWARE/APACHE/index.html>. |
28 | | */ |
29 | | |
30 | | #endif /* APR_HAS_MMAP */ |
31 | | |
32 | | static void file_bucket_destroy(void *data) |
33 | 317 | { |
34 | 317 | apr_bucket_file *f = data; |
35 | | |
36 | 317 | if (apr_bucket_shared_destroy(f)) { |
37 | | /* no need to close the file here; it will get |
38 | | * done automatically when the pool gets cleaned up */ |
39 | 317 | apr_bucket_free(f); |
40 | 317 | } |
41 | 317 | } |
42 | | |
43 | | #if APR_HAS_MMAP |
44 | | static int file_make_mmap(apr_bucket *e, apr_size_t filelength, |
45 | | apr_off_t fileoffset, apr_pool_t *p) |
46 | 0 | { |
47 | 0 | apr_bucket_file *a = e->data; |
48 | 0 | apr_mmap_t *mm; |
49 | |
|
50 | 0 | if (!a->can_mmap) { |
51 | 0 | return 0; |
52 | 0 | } |
53 | | |
54 | 0 | if (filelength > APR_MMAP_LIMIT) { |
55 | 0 | if (apr_mmap_create(&mm, a->fd, fileoffset, APR_MMAP_LIMIT, |
56 | 0 | APR_MMAP_READ, p) != APR_SUCCESS) |
57 | 0 | { |
58 | 0 | return 0; |
59 | 0 | } |
60 | 0 | apr_bucket_split(e, APR_MMAP_LIMIT); |
61 | 0 | filelength = APR_MMAP_LIMIT; |
62 | 0 | } |
63 | 0 | else if ((filelength < APR_MMAP_THRESHOLD) || |
64 | 0 | (apr_mmap_create(&mm, a->fd, fileoffset, filelength, |
65 | 0 | APR_MMAP_READ, p) != APR_SUCCESS)) |
66 | 0 | { |
67 | 0 | return 0; |
68 | 0 | } |
69 | 0 | apr_bucket_mmap_make(e, mm, 0, filelength); |
70 | 0 | file_bucket_destroy(a); |
71 | 0 | return 1; |
72 | 0 | } |
73 | | #endif |
74 | | |
75 | | static apr_status_t file_bucket_read(apr_bucket *e, const char **str, |
76 | | apr_size_t *len, apr_read_type_e block) |
77 | 0 | { |
78 | 0 | apr_bucket_file *a = e->data; |
79 | 0 | apr_file_t *f = a->fd; |
80 | 0 | apr_bucket *b = NULL; |
81 | 0 | char *buf; |
82 | 0 | apr_status_t rv; |
83 | 0 | apr_size_t filelength = e->length; /* bytes remaining in file past offset */ |
84 | 0 | apr_off_t fileoffset = e->start; |
85 | 0 | #if APR_HAS_THREADS && !APR_HAS_XTHREAD_FILES |
86 | 0 | apr_int32_t flags; |
87 | 0 | #endif |
88 | |
|
89 | 0 | #if APR_HAS_MMAP |
90 | 0 | if (file_make_mmap(e, filelength, fileoffset, a->readpool)) { |
91 | 0 | return apr_bucket_read(e, str, len, block); |
92 | 0 | } |
93 | 0 | #endif |
94 | | |
95 | 0 | #if APR_HAS_THREADS && !APR_HAS_XTHREAD_FILES |
96 | 0 | if ((flags = apr_file_flags_get(f)) & APR_FOPEN_XTHREAD) { |
97 | | /* this file descriptor is shared across multiple threads and |
98 | | * this OS doesn't support that natively, so as a workaround |
99 | | * we must reopen the file into a->readpool */ |
100 | 0 | const char *fname; |
101 | 0 | apr_file_name_get(&fname, f); |
102 | |
|
103 | 0 | rv = apr_file_open(&f, fname, (flags & ~APR_FOPEN_XTHREAD), 0, a->readpool); |
104 | 0 | if (rv != APR_SUCCESS) |
105 | 0 | return rv; |
106 | | |
107 | 0 | a->fd = f; |
108 | 0 | } |
109 | 0 | #endif |
110 | | |
111 | 0 | *str = NULL; /* in case we die prematurely */ |
112 | 0 | *len = (filelength > a->read_size) ? a->read_size : filelength; |
113 | 0 | buf = apr_bucket_alloc(*len, e->list); |
114 | | |
115 | | /* Handle offset ... */ |
116 | 0 | rv = apr_file_seek(f, APR_SET, &fileoffset); |
117 | 0 | if (rv != APR_SUCCESS) { |
118 | 0 | apr_bucket_free(buf); |
119 | 0 | return rv; |
120 | 0 | } |
121 | 0 | rv = apr_file_read(f, buf, len); |
122 | 0 | if (rv != APR_SUCCESS && rv != APR_EOF) { |
123 | 0 | apr_bucket_free(buf); |
124 | 0 | return rv; |
125 | 0 | } |
126 | 0 | filelength -= *len; |
127 | | /* |
128 | | * Change the current bucket to refer to what we read, |
129 | | * even if we read nothing because we hit EOF. |
130 | | */ |
131 | 0 | apr_bucket_heap_make(e, buf, *len, apr_bucket_free); |
132 | | |
133 | | /* If we have more to read from the file, then create another bucket */ |
134 | 0 | if (filelength > 0 && rv != APR_EOF) { |
135 | | /* for efficiency, we can just build a new apr_bucket struct |
136 | | * to wrap around the existing file bucket */ |
137 | 0 | b = apr_bucket_alloc(sizeof(*b), e->list); |
138 | 0 | b->start = fileoffset + (*len); |
139 | 0 | b->length = filelength; |
140 | 0 | b->data = a; |
141 | 0 | b->type = &apr_bucket_type_file; |
142 | 0 | b->free = apr_bucket_free; |
143 | 0 | b->list = e->list; |
144 | 0 | APR_BUCKET_INSERT_AFTER(e, b); |
145 | 0 | } |
146 | 0 | else { |
147 | 0 | file_bucket_destroy(a); |
148 | 0 | } |
149 | |
|
150 | 0 | *str = buf; |
151 | 0 | return rv; |
152 | 0 | } |
153 | | |
154 | | APR_DECLARE(apr_bucket *) apr_bucket_file_make(apr_bucket *b, apr_file_t *fd, |
155 | | apr_off_t offset, |
156 | | apr_size_t len, apr_pool_t *p) |
157 | 317 | { |
158 | 317 | apr_bucket_file *f; |
159 | | |
160 | 317 | f = apr_bucket_alloc(sizeof(*f), b->list); |
161 | 317 | f->fd = fd; |
162 | 317 | f->readpool = p; |
163 | 317 | #if APR_HAS_MMAP |
164 | 317 | f->can_mmap = 1; |
165 | 317 | #endif |
166 | 317 | f->read_size = APR_BUCKET_BUFF_SIZE; |
167 | | |
168 | 317 | b = apr_bucket_shared_make(b, f, offset, len); |
169 | 317 | b->type = &apr_bucket_type_file; |
170 | | |
171 | 317 | return b; |
172 | 317 | } |
173 | | |
174 | | APR_DECLARE(apr_bucket *) apr_bucket_file_create(apr_file_t *fd, |
175 | | apr_off_t offset, |
176 | | apr_size_t len, apr_pool_t *p, |
177 | | apr_bucket_alloc_t *list) |
178 | 317 | { |
179 | 317 | apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); |
180 | | |
181 | 317 | APR_BUCKET_INIT(b); |
182 | 317 | b->free = apr_bucket_free; |
183 | 317 | b->list = list; |
184 | 317 | return apr_bucket_file_make(b, fd, offset, len, p); |
185 | 317 | } |
186 | | |
187 | | APR_DECLARE(apr_status_t) apr_bucket_file_enable_mmap(apr_bucket *e, |
188 | | int enabled) |
189 | 0 | { |
190 | 0 | #if APR_HAS_MMAP |
191 | 0 | apr_bucket_file *a = e->data; |
192 | 0 | a->can_mmap = enabled; |
193 | 0 | return APR_SUCCESS; |
194 | | #else |
195 | | return APR_ENOTIMPL; |
196 | | #endif /* APR_HAS_MMAP */ |
197 | 0 | } |
198 | | |
199 | | APR_DECLARE(apr_status_t) apr_bucket_file_set_buf_size(apr_bucket *e, |
200 | | apr_size_t size) |
201 | 0 | { |
202 | 0 | apr_bucket_file *a = e->data; |
203 | |
|
204 | 0 | if (size <= APR_BUCKET_BUFF_SIZE) { |
205 | 0 | a->read_size = APR_BUCKET_BUFF_SIZE; |
206 | 0 | } |
207 | 0 | else { |
208 | 0 | apr_size_t floor = apr_bucket_alloc_aligned_floor(e->list, size); |
209 | 0 | a->read_size = (size < floor) ? size : floor; |
210 | 0 | } |
211 | |
|
212 | 0 | return APR_SUCCESS; |
213 | 0 | } |
214 | | |
215 | | static apr_status_t file_bucket_setaside(apr_bucket *b, apr_pool_t *reqpool) |
216 | 0 | { |
217 | 0 | apr_bucket_file *a = b->data; |
218 | 0 | apr_file_t *fd = NULL; |
219 | 0 | apr_file_t *f = a->fd; |
220 | 0 | apr_pool_t *curpool = apr_file_pool_get(f); |
221 | |
|
222 | 0 | if (apr_pool_is_ancestor(curpool, reqpool)) { |
223 | 0 | return APR_SUCCESS; |
224 | 0 | } |
225 | | |
226 | | /* If the file is shared/split accross multiple buckets, this bucket can't |
227 | | * take exclusive ownership with apr_file_setaside() (thus invalidating the |
228 | | * f->filedes), let's apr_file_dup() in this case instead. |
229 | | */ |
230 | 0 | if (a->refcount.refcount > 1) { |
231 | 0 | apr_bucket_file *new; |
232 | 0 | apr_status_t rv; |
233 | |
|
234 | 0 | rv = apr_file_dup(&fd, f, reqpool); |
235 | 0 | if (rv != APR_SUCCESS) { |
236 | 0 | return rv; |
237 | 0 | } |
238 | | |
239 | 0 | new = apr_bucket_alloc(sizeof(*new), b->list); |
240 | 0 | memcpy(new, a, sizeof(*new)); |
241 | 0 | new->refcount.refcount = 1; |
242 | |
|
243 | 0 | a->refcount.refcount--; |
244 | 0 | a = b->data = new; |
245 | 0 | } |
246 | 0 | else { |
247 | 0 | apr_file_setaside(&fd, f, reqpool); |
248 | 0 | } |
249 | 0 | a->fd = fd; |
250 | 0 | a->readpool = reqpool; |
251 | 0 | return APR_SUCCESS; |
252 | 0 | } |
253 | | |
254 | | APR_DECLARE_DATA const apr_bucket_type_t apr_bucket_type_file = { |
255 | | "FILE", 5, APR_BUCKET_DATA, |
256 | | file_bucket_destroy, |
257 | | file_bucket_read, |
258 | | file_bucket_setaside, |
259 | | apr_bucket_shared_split, |
260 | | apr_bucket_shared_copy |
261 | | }; |