Mercurial > hg > nginx
annotate src/core/ngx_open_file_cache.c @ 4515:8bb695c05870 stable-1.0
Merge of r4498:
Fix of rbtree lookup on hash collisions.
Previous code incorrectly assumed that nodes with identical keys are linked
together. This might not be true after tree rebalance.
Patch by Lanshun Zhou.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Mon, 05 Mar 2012 13:17:56 +0000 |
parents | 4919fb357a5d |
children |
rev | line source |
---|---|
1453 | 1 |
2 /* | |
3 * Copyright (C) Igor Sysoev | |
4450
4919fb357a5d
Merge of r4406, r4413: copyrights updated.
Maxim Dounin <mdounin@mdounin.ru>
parents:
4161
diff
changeset
|
4 * Copyright (C) Nginx, Inc. |
1453 | 5 */ |
6 | |
7 | |
8 #include <ngx_config.h> | |
9 #include <ngx_core.h> | |
10 #include <ngx_event.h> | |
11 | |
12 | |
13 /* | |
14 * open file cache caches | |
15 * open file handles with stat() info; | |
16 * directories stat() info; | |
17 * files and directories errors: not found, access denied, etc. | |
18 */ | |
19 | |
20 | |
3178 | 21 #define NGX_MIN_READ_AHEAD (128 * 1024) |
22 | |
23 | |
1453 | 24 static void ngx_open_file_cache_cleanup(void *data); |
1775 | 25 static ngx_int_t ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, |
26 ngx_log_t *log); | |
27 static void ngx_open_file_add_event(ngx_open_file_cache_t *cache, | |
28 ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log); | |
1453 | 29 static void ngx_open_file_cleanup(void *data); |
30 static void ngx_close_cached_file(ngx_open_file_cache_t *cache, | |
1772 | 31 ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log); |
1775 | 32 static void ngx_open_file_del_event(ngx_cached_open_file_t *file); |
1453 | 33 static void ngx_expire_old_cached_files(ngx_open_file_cache_t *cache, |
34 ngx_uint_t n, ngx_log_t *log); | |
35 static void ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp, | |
36 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); | |
1775 | 37 static ngx_cached_open_file_t * |
38 ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name, | |
39 uint32_t hash); | |
1453 | 40 static void ngx_open_file_cache_remove(ngx_event_t *ev); |
41 | |
42 | |
43 ngx_open_file_cache_t * | |
44 ngx_open_file_cache_init(ngx_pool_t *pool, ngx_uint_t max, time_t inactive) | |
45 { | |
46 ngx_pool_cleanup_t *cln; | |
47 ngx_open_file_cache_t *cache; | |
48 | |
49 cache = ngx_palloc(pool, sizeof(ngx_open_file_cache_t)); | |
50 if (cache == NULL) { | |
51 return NULL; | |
52 } | |
53 | |
1761 | 54 ngx_rbtree_init(&cache->rbtree, &cache->sentinel, |
1743
4fc402c3ec73
optimize rbtree initialization and insert
Igor Sysoev <igor@sysoev.ru>
parents:
1458
diff
changeset
|
55 ngx_open_file_cache_rbtree_insert_value); |
1453 | 56 |
1765 | 57 ngx_queue_init(&cache->expire_queue); |
58 | |
1453 | 59 cache->current = 0; |
60 cache->max = max; | |
61 cache->inactive = inactive; | |
62 | |
63 cln = ngx_pool_cleanup_add(pool, 0); | |
64 if (cln == NULL) { | |
65 return NULL; | |
66 } | |
67 | |
68 cln->handler = ngx_open_file_cache_cleanup; | |
69 cln->data = cache; | |
70 | |
71 return cache; | |
72 } | |
73 | |
74 | |
75 static void | |
76 ngx_open_file_cache_cleanup(void *data) | |
77 { | |
78 ngx_open_file_cache_t *cache = data; | |
79 | |
1765 | 80 ngx_queue_t *q; |
1453 | 81 ngx_cached_open_file_t *file; |
82 | |
83 ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, | |
84 "open file cache cleanup"); | |
85 | |
86 for ( ;; ) { | |
87 | |
1765 | 88 if (ngx_queue_empty(&cache->expire_queue)) { |
1766 | 89 break; |
1453 | 90 } |
91 | |
1765 | 92 q = ngx_queue_last(&cache->expire_queue); |
93 | |
94 file = ngx_queue_data(q, ngx_cached_open_file_t, queue); | |
95 | |
96 ngx_queue_remove(q); | |
1453 | 97 |
98 ngx_rbtree_delete(&cache->rbtree, &file->node); | |
99 | |
100 cache->current--; | |
101 | |
102 ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, | |
103 "delete cached open file: %s", file->name); | |
104 | |
105 if (!file->err && !file->is_dir) { | |
106 file->close = 1; | |
107 file->count = 0; | |
1772 | 108 ngx_close_cached_file(cache, file, 0, ngx_cycle->log); |
1453 | 109 |
110 } else { | |
111 ngx_free(file->name); | |
112 ngx_free(file); | |
113 } | |
114 } | |
115 | |
116 if (cache->current) { | |
117 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, | |
118 "%d items still leave in open file cache", | |
119 cache->current); | |
120 } | |
121 | |
122 if (cache->rbtree.root != cache->rbtree.sentinel) { | |
123 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, | |
124 "rbtree still is not empty in open file cache"); | |
125 | |
126 } | |
127 } | |
128 | |
129 | |
130 ngx_int_t | |
131 ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name, | |
132 ngx_open_file_info_t *of, ngx_pool_t *pool) | |
133 { | |
134 time_t now; | |
135 uint32_t hash; | |
136 ngx_int_t rc; | |
2756
09cab3f8d92e
*) of.test_only to not open file if only stat() is enough
Igor Sysoev <igor@sysoev.ru>
parents:
2629
diff
changeset
|
137 ngx_file_info_t fi; |
1453 | 138 ngx_pool_cleanup_t *cln; |
139 ngx_cached_open_file_t *file; | |
140 ngx_pool_cleanup_file_t *clnf; | |
141 ngx_open_file_cache_cleanup_t *ofcln; | |
142 | |
2069
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
143 of->fd = NGX_INVALID_FILE; |
1453 | 144 of->err = 0; |
145 | |
146 if (cache == NULL) { | |
147 | |
2756
09cab3f8d92e
*) of.test_only to not open file if only stat() is enough
Igor Sysoev <igor@sysoev.ru>
parents:
2629
diff
changeset
|
148 if (of->test_only) { |
09cab3f8d92e
*) of.test_only to not open file if only stat() is enough
Igor Sysoev <igor@sysoev.ru>
parents:
2629
diff
changeset
|
149 |
2782
4bd7825fab80
uniform ngx_file_info() interface with ngx_fd_info()
Igor Sysoev <igor@sysoev.ru>
parents:
2756
diff
changeset
|
150 if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) { |
2756
09cab3f8d92e
*) of.test_only to not open file if only stat() is enough
Igor Sysoev <igor@sysoev.ru>
parents:
2629
diff
changeset
|
151 of->err = ngx_errno; |
09cab3f8d92e
*) of.test_only to not open file if only stat() is enough
Igor Sysoev <igor@sysoev.ru>
parents:
2629
diff
changeset
|
152 of->failed = ngx_file_info_n; |
09cab3f8d92e
*) of.test_only to not open file if only stat() is enough
Igor Sysoev <igor@sysoev.ru>
parents:
2629
diff
changeset
|
153 return NGX_ERROR; |
09cab3f8d92e
*) of.test_only to not open file if only stat() is enough
Igor Sysoev <igor@sysoev.ru>
parents:
2629
diff
changeset
|
154 } |
09cab3f8d92e
*) of.test_only to not open file if only stat() is enough
Igor Sysoev <igor@sysoev.ru>
parents:
2629
diff
changeset
|
155 |
09cab3f8d92e
*) of.test_only to not open file if only stat() is enough
Igor Sysoev <igor@sysoev.ru>
parents:
2629
diff
changeset
|
156 of->uniq = ngx_file_uniq(&fi); |
09cab3f8d92e
*) of.test_only to not open file if only stat() is enough
Igor Sysoev <igor@sysoev.ru>
parents:
2629
diff
changeset
|
157 of->mtime = ngx_file_mtime(&fi); |
09cab3f8d92e
*) of.test_only to not open file if only stat() is enough
Igor Sysoev <igor@sysoev.ru>
parents:
2629
diff
changeset
|
158 of->size = ngx_file_size(&fi); |
3899
e7cd13b7f759
Use more precise stat.st_blocks to account cache size on Unix
Igor Sysoev <igor@sysoev.ru>
parents:
3497
diff
changeset
|
159 of->fs_size = ngx_file_fs_size(&fi); |
2756
09cab3f8d92e
*) of.test_only to not open file if only stat() is enough
Igor Sysoev <igor@sysoev.ru>
parents:
2629
diff
changeset
|
160 of->is_dir = ngx_is_dir(&fi); |
09cab3f8d92e
*) of.test_only to not open file if only stat() is enough
Igor Sysoev <igor@sysoev.ru>
parents:
2629
diff
changeset
|
161 of->is_file = ngx_is_file(&fi); |
09cab3f8d92e
*) of.test_only to not open file if only stat() is enough
Igor Sysoev <igor@sysoev.ru>
parents:
2629
diff
changeset
|
162 of->is_link = ngx_is_link(&fi); |
09cab3f8d92e
*) of.test_only to not open file if only stat() is enough
Igor Sysoev <igor@sysoev.ru>
parents:
2629
diff
changeset
|
163 of->is_exec = ngx_is_exec(&fi); |
09cab3f8d92e
*) of.test_only to not open file if only stat() is enough
Igor Sysoev <igor@sysoev.ru>
parents:
2629
diff
changeset
|
164 |
09cab3f8d92e
*) of.test_only to not open file if only stat() is enough
Igor Sysoev <igor@sysoev.ru>
parents:
2629
diff
changeset
|
165 return NGX_OK; |
09cab3f8d92e
*) of.test_only to not open file if only stat() is enough
Igor Sysoev <igor@sysoev.ru>
parents:
2629
diff
changeset
|
166 } |
09cab3f8d92e
*) of.test_only to not open file if only stat() is enough
Igor Sysoev <igor@sysoev.ru>
parents:
2629
diff
changeset
|
167 |
1453 | 168 cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t)); |
169 if (cln == NULL) { | |
170 return NGX_ERROR; | |
171 } | |
172 | |
173 rc = ngx_open_and_stat_file(name->data, of, pool->log); | |
174 | |
175 if (rc == NGX_OK && !of->is_dir) { | |
176 cln->handler = ngx_pool_cleanup_file; | |
177 clnf = cln->data; | |
178 | |
179 clnf->fd = of->fd; | |
180 clnf->name = name->data; | |
181 clnf->log = pool->log; | |
182 } | |
183 | |
184 return rc; | |
185 } | |
186 | |
187 cln = ngx_pool_cleanup_add(pool, sizeof(ngx_open_file_cache_cleanup_t)); | |
188 if (cln == NULL) { | |
189 return NGX_ERROR; | |
190 } | |
191 | |
192 now = ngx_time(); | |
193 | |
1775 | 194 hash = ngx_crc32_long(name->data, name->len); |
195 | |
196 file = ngx_open_file_lookup(cache, name, hash); | |
197 | |
198 if (file) { | |
199 | |
200 file->uses++; | |
201 | |
1987 | 202 ngx_queue_remove(&file->queue); |
203 | |
1775 | 204 if (file->fd == NGX_INVALID_FILE && file->err == 0 && !file->is_dir) { |
205 | |
206 /* file was not used often enough to keep open */ | |
207 | |
208 rc = ngx_open_and_stat_file(name->data, of, pool->log); | |
1453 | 209 |
1775 | 210 if (rc != NGX_OK && (of->err == 0 || !of->errors)) { |
211 goto failed; | |
212 } | |
213 | |
214 goto add_event; | |
1453 | 215 } |
216 | |
2070 | 217 if (file->use_event |
2063
67a29af877ed
initialize of.uniq in ngx_open_cached_file()
Igor Sysoev <igor@sysoev.ru>
parents:
2006
diff
changeset
|
218 || (file->event == NULL |
67a29af877ed
initialize of.uniq in ngx_open_cached_file()
Igor Sysoev <igor@sysoev.ru>
parents:
2006
diff
changeset
|
219 && (of->uniq == 0 || of->uniq == file->uniq) |
67a29af877ed
initialize of.uniq in ngx_open_cached_file()
Igor Sysoev <igor@sysoev.ru>
parents:
2006
diff
changeset
|
220 && now - file->created < of->valid)) |
1775 | 221 { |
222 if (file->err == 0) { | |
1772 | 223 |
1775 | 224 of->fd = file->fd; |
225 of->uniq = file->uniq; | |
226 of->mtime = file->mtime; | |
227 of->size = file->size; | |
1453 | 228 |
1775 | 229 of->is_dir = file->is_dir; |
230 of->is_file = file->is_file; | |
231 of->is_link = file->is_link; | |
232 of->is_exec = file->is_exec; | |
2246
987831d73bd8
cache directio flag in open file cache
Igor Sysoev <igor@sysoev.ru>
parents:
2231
diff
changeset
|
233 of->is_directio = file->is_directio; |
1453 | 234 |
1775 | 235 if (!file->is_dir) { |
236 file->count++; | |
237 ngx_open_file_add_event(cache, file, of, pool->log); | |
1453 | 238 } |
239 | |
1775 | 240 } else { |
241 of->err = file->err; | |
2783
87c088e6956a
set of.failed for cached error, the bug has been introduced in r2757
Igor Sysoev <igor@sysoev.ru>
parents:
2782
diff
changeset
|
242 of->failed = ngx_open_file_n; |
1775 | 243 } |
244 | |
245 goto found; | |
246 } | |
247 | |
248 ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0, | |
249 "retest open file: %s, fd:%d, c:%d, e:%d", | |
250 file->name, file->fd, file->count, file->err); | |
1453 | 251 |
1775 | 252 if (file->is_dir) { |
253 | |
254 /* | |
255 * chances that directory became file are very small | |
256 * so test_dir flag allows to use a single syscall | |
257 * in ngx_file_info() instead of three syscalls | |
258 */ | |
259 | |
260 of->test_dir = 1; | |
261 } | |
262 | |
2069
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
263 of->fd = file->fd; |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
264 of->uniq = file->uniq; |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
265 |
1775 | 266 rc = ngx_open_and_stat_file(name->data, of, pool->log); |
1453 | 267 |
1775 | 268 if (rc != NGX_OK && (of->err == 0 || !of->errors)) { |
269 goto failed; | |
270 } | |
271 | |
272 if (of->is_dir) { | |
273 | |
274 if (file->is_dir || file->err) { | |
275 goto update; | |
276 } | |
277 | |
278 /* file became directory */ | |
1453 | 279 |
1775 | 280 } else if (of->err == 0) { /* file */ |
281 | |
282 if (file->is_dir || file->err) { | |
283 goto add_event; | |
284 } | |
1453 | 285 |
2070 | 286 if (of->uniq == file->uniq) { |
1453 | 287 |
1775 | 288 if (file->event) { |
289 file->use_event = 1; | |
1453 | 290 } |
291 | |
4161 | 292 of->is_directio = file->is_directio; |
293 | |
294 goto update; | |
1775 | 295 } |
1453 | 296 |
1775 | 297 /* file was changed */ |
1453 | 298 |
1775 | 299 } else { /* error to cache */ |
1453 | 300 |
1775 | 301 if (file->err || file->is_dir) { |
302 goto update; | |
1453 | 303 } |
304 | |
1775 | 305 /* file was removed, etc. */ |
306 } | |
307 | |
308 if (file->count == 0) { | |
309 | |
310 ngx_open_file_del_event(file); | |
1453 | 311 |
1775 | 312 if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { |
313 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, | |
314 ngx_close_file_n " \"%s\" failed", | |
315 name->data); | |
316 } | |
1453 | 317 |
1775 | 318 goto add_event; |
319 } | |
320 | |
321 ngx_rbtree_delete(&cache->rbtree, &file->node); | |
322 | |
323 cache->current--; | |
324 | |
325 file->close = 1; | |
326 | |
327 goto create; | |
1453 | 328 } |
329 | |
330 /* not found */ | |
331 | |
332 rc = ngx_open_and_stat_file(name->data, of, pool->log); | |
333 | |
334 if (rc != NGX_OK && (of->err == 0 || !of->errors)) { | |
335 goto failed; | |
336 } | |
337 | |
338 create: | |
339 | |
340 if (cache->current >= cache->max) { | |
341 ngx_expire_old_cached_files(cache, 0, pool->log); | |
342 } | |
343 | |
344 file = ngx_alloc(sizeof(ngx_cached_open_file_t), pool->log); | |
345 | |
346 if (file == NULL) { | |
347 goto failed; | |
348 } | |
349 | |
350 file->name = ngx_alloc(name->len + 1, pool->log); | |
351 | |
352 if (file->name == NULL) { | |
353 ngx_free(file); | |
354 file = NULL; | |
355 goto failed; | |
356 } | |
357 | |
358 ngx_cpystrn(file->name, name->data, name->len + 1); | |
359 | |
360 file->node.key = hash; | |
361 | |
362 ngx_rbtree_insert(&cache->rbtree, &file->node); | |
363 | |
364 cache->current++; | |
365 | |
1775 | 366 file->uses = 1; |
1453 | 367 file->count = 0; |
2934
b6d588fa3ee9
initialize use_event field in open file cache
Igor Sysoev <igor@sysoev.ru>
parents:
2783
diff
changeset
|
368 file->use_event = 0; |
1775 | 369 file->event = NULL; |
370 | |
371 add_event: | |
372 | |
373 ngx_open_file_add_event(cache, file, of, pool->log); | |
1453 | 374 |
375 update: | |
376 | |
377 file->fd = of->fd; | |
378 file->err = of->err; | |
379 | |
380 if (of->err == 0) { | |
381 file->uniq = of->uniq; | |
382 file->mtime = of->mtime; | |
383 file->size = of->size; | |
384 | |
385 file->close = 0; | |
386 | |
387 file->is_dir = of->is_dir; | |
388 file->is_file = of->is_file; | |
389 file->is_link = of->is_link; | |
390 file->is_exec = of->is_exec; | |
2246
987831d73bd8
cache directio flag in open file cache
Igor Sysoev <igor@sysoev.ru>
parents:
2231
diff
changeset
|
391 file->is_directio = of->is_directio; |
1453 | 392 |
393 if (!of->is_dir) { | |
394 file->count++; | |
395 } | |
396 } | |
397 | |
398 file->created = now; | |
399 | |
400 found: | |
401 | |
402 file->accessed = now; | |
403 | |
1765 | 404 ngx_queue_insert_head(&cache->expire_queue, &file->queue); |
1453 | 405 |
1772 | 406 ngx_log_debug5(NGX_LOG_DEBUG_CORE, pool->log, 0, |
407 "cached open file: %s, fd:%d, c:%d, e:%d, u:%d", | |
408 file->name, file->fd, file->count, file->err, file->uses); | |
1453 | 409 |
410 if (of->err == 0) { | |
411 | |
412 if (!of->is_dir) { | |
413 cln->handler = ngx_open_file_cleanup; | |
414 ofcln = cln->data; | |
415 | |
416 ofcln->cache = cache; | |
417 ofcln->file = file; | |
1772 | 418 ofcln->min_uses = of->min_uses; |
1453 | 419 ofcln->log = pool->log; |
420 } | |
421 | |
422 return NGX_OK; | |
423 } | |
424 | |
425 return NGX_ERROR; | |
426 | |
427 failed: | |
428 | |
1988
34051c712e41
fix segfault when file is deleted and open_file_cache_errors is off
Igor Sysoev <igor@sysoev.ru>
parents:
1987
diff
changeset
|
429 if (file) { |
1453 | 430 ngx_rbtree_delete(&cache->rbtree, &file->node); |
431 | |
432 cache->current--; | |
433 | |
1988
34051c712e41
fix segfault when file is deleted and open_file_cache_errors is off
Igor Sysoev <igor@sysoev.ru>
parents:
1987
diff
changeset
|
434 if (file->count == 0) { |
34051c712e41
fix segfault when file is deleted and open_file_cache_errors is off
Igor Sysoev <igor@sysoev.ru>
parents:
1987
diff
changeset
|
435 |
2006
b52cb9bf2064
style fix: remove tabs and trailing spaces
Igor Sysoev <igor@sysoev.ru>
parents:
1988
diff
changeset
|
436 if (file->fd != NGX_INVALID_FILE) { |
b52cb9bf2064
style fix: remove tabs and trailing spaces
Igor Sysoev <igor@sysoev.ru>
parents:
1988
diff
changeset
|
437 if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { |
b52cb9bf2064
style fix: remove tabs and trailing spaces
Igor Sysoev <igor@sysoev.ru>
parents:
1988
diff
changeset
|
438 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, |
b52cb9bf2064
style fix: remove tabs and trailing spaces
Igor Sysoev <igor@sysoev.ru>
parents:
1988
diff
changeset
|
439 ngx_close_file_n " \"%s\" failed", |
1988
34051c712e41
fix segfault when file is deleted and open_file_cache_errors is off
Igor Sysoev <igor@sysoev.ru>
parents:
1987
diff
changeset
|
440 file->name); |
2006
b52cb9bf2064
style fix: remove tabs and trailing spaces
Igor Sysoev <igor@sysoev.ru>
parents:
1988
diff
changeset
|
441 } |
b52cb9bf2064
style fix: remove tabs and trailing spaces
Igor Sysoev <igor@sysoev.ru>
parents:
1988
diff
changeset
|
442 } |
1988
34051c712e41
fix segfault when file is deleted and open_file_cache_errors is off
Igor Sysoev <igor@sysoev.ru>
parents:
1987
diff
changeset
|
443 |
34051c712e41
fix segfault when file is deleted and open_file_cache_errors is off
Igor Sysoev <igor@sysoev.ru>
parents:
1987
diff
changeset
|
444 ngx_free(file->name); |
34051c712e41
fix segfault when file is deleted and open_file_cache_errors is off
Igor Sysoev <igor@sysoev.ru>
parents:
1987
diff
changeset
|
445 ngx_free(file); |
34051c712e41
fix segfault when file is deleted and open_file_cache_errors is off
Igor Sysoev <igor@sysoev.ru>
parents:
1987
diff
changeset
|
446 |
34051c712e41
fix segfault when file is deleted and open_file_cache_errors is off
Igor Sysoev <igor@sysoev.ru>
parents:
1987
diff
changeset
|
447 } else { |
34051c712e41
fix segfault when file is deleted and open_file_cache_errors is off
Igor Sysoev <igor@sysoev.ru>
parents:
1987
diff
changeset
|
448 file->close = 1; |
1453 | 449 } |
450 } | |
451 | |
452 if (of->fd != NGX_INVALID_FILE) { | |
453 if (ngx_close_file(of->fd) == NGX_FILE_ERROR) { | |
454 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, | |
455 ngx_close_file_n " \"%s\" failed", name->data); | |
456 } | |
457 } | |
458 | |
459 return NGX_ERROR; | |
460 } | |
461 | |
462 | |
463 static ngx_int_t | |
464 ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, ngx_log_t *log) | |
465 { | |
466 ngx_fd_t fd; | |
467 ngx_file_info_t fi; | |
468 | |
2070 | 469 if (of->fd != NGX_INVALID_FILE) { |
1453 | 470 |
2782
4bd7825fab80
uniform ngx_file_info() interface with ngx_fd_info()
Igor Sysoev <igor@sysoev.ru>
parents:
2756
diff
changeset
|
471 if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) { |
2756
09cab3f8d92e
*) of.test_only to not open file if only stat() is enough
Igor Sysoev <igor@sysoev.ru>
parents:
2629
diff
changeset
|
472 of->failed = ngx_file_info_n; |
2069
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
473 goto failed; |
1453 | 474 } |
475 | |
2070 | 476 if (of->uniq == ngx_file_uniq(&fi)) { |
2069
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
477 goto done; |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
478 } |
1453 | 479 |
2070 | 480 } else if (of->test_dir) { |
481 | |
2782
4bd7825fab80
uniform ngx_file_info() interface with ngx_fd_info()
Igor Sysoev <igor@sysoev.ru>
parents:
2756
diff
changeset
|
482 if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) { |
2756
09cab3f8d92e
*) of.test_only to not open file if only stat() is enough
Igor Sysoev <igor@sysoev.ru>
parents:
2629
diff
changeset
|
483 of->failed = ngx_file_info_n; |
2070 | 484 goto failed; |
485 } | |
486 | |
2460
225fa4abd76f
test ngx_file_info() result, the bug has been introduced in r2070
Igor Sysoev <igor@sysoev.ru>
parents:
2246
diff
changeset
|
487 if (ngx_is_dir(&fi)) { |
2069
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
488 goto done; |
1453 | 489 } |
490 } | |
491 | |
2072 | 492 if (!of->log) { |
3497
ac281bc4c187
use non-blocking open() not to hang on FIFO files, etc.
Igor Sysoev <igor@sysoev.ru>
parents:
3178
diff
changeset
|
493 |
ac281bc4c187
use non-blocking open() not to hang on FIFO files, etc.
Igor Sysoev <igor@sysoev.ru>
parents:
3178
diff
changeset
|
494 /* |
ac281bc4c187
use non-blocking open() not to hang on FIFO files, etc.
Igor Sysoev <igor@sysoev.ru>
parents:
3178
diff
changeset
|
495 * Use non-blocking open() not to hang on FIFO files, etc. |
ac281bc4c187
use non-blocking open() not to hang on FIFO files, etc.
Igor Sysoev <igor@sysoev.ru>
parents:
3178
diff
changeset
|
496 * This flag has no effect on a regular files. |
ac281bc4c187
use non-blocking open() not to hang on FIFO files, etc.
Igor Sysoev <igor@sysoev.ru>
parents:
3178
diff
changeset
|
497 */ |
ac281bc4c187
use non-blocking open() not to hang on FIFO files, etc.
Igor Sysoev <igor@sysoev.ru>
parents:
3178
diff
changeset
|
498 |
ac281bc4c187
use non-blocking open() not to hang on FIFO files, etc.
Igor Sysoev <igor@sysoev.ru>
parents:
3178
diff
changeset
|
499 fd = ngx_open_file(name, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK, |
ac281bc4c187
use non-blocking open() not to hang on FIFO files, etc.
Igor Sysoev <igor@sysoev.ru>
parents:
3178
diff
changeset
|
500 NGX_FILE_OPEN, 0); |
2072 | 501 |
502 } else { | |
2629
367b29612a00
Win32 appends synchronized if only FILE_APPEND_DATA and SYNCHRONIZE are set
Igor Sysoev <igor@sysoev.ru>
parents:
2628
diff
changeset
|
503 fd = ngx_open_file(name, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN, |
367b29612a00
Win32 appends synchronized if only FILE_APPEND_DATA and SYNCHRONIZE are set
Igor Sysoev <igor@sysoev.ru>
parents:
2628
diff
changeset
|
504 NGX_FILE_DEFAULT_ACCESS); |
2072 | 505 } |
1453 | 506 |
507 if (fd == NGX_INVALID_FILE) { | |
2756
09cab3f8d92e
*) of.test_only to not open file if only stat() is enough
Igor Sysoev <igor@sysoev.ru>
parents:
2629
diff
changeset
|
508 of->failed = ngx_open_file_n; |
2069
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
509 goto failed; |
1453 | 510 } |
511 | |
512 if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) { | |
513 ngx_log_error(NGX_LOG_CRIT, log, ngx_errno, | |
514 ngx_fd_info_n " \"%s\" failed", name); | |
515 | |
516 if (ngx_close_file(fd) == NGX_FILE_ERROR) { | |
517 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, | |
518 ngx_close_file_n " \"%s\" failed", name); | |
519 } | |
520 | |
2069
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
521 of->fd = NGX_INVALID_FILE; |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
522 |
1453 | 523 return NGX_ERROR; |
524 } | |
525 | |
526 if (ngx_is_dir(&fi)) { | |
527 if (ngx_close_file(fd) == NGX_FILE_ERROR) { | |
528 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, | |
529 ngx_close_file_n " \"%s\" failed", name); | |
530 } | |
531 | |
2069
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
532 of->fd = NGX_INVALID_FILE; |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
533 |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
534 } else { |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
535 of->fd = fd; |
2129 | 536 |
3178 | 537 if (of->read_ahead && ngx_file_size(&fi) > NGX_MIN_READ_AHEAD) { |
538 if (ngx_read_ahead(fd, of->read_ahead) == NGX_ERROR) { | |
539 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, | |
540 ngx_read_ahead_n " \"%s\" failed", name); | |
541 } | |
542 } | |
543 | |
2129 | 544 if (of->directio <= ngx_file_size(&fi)) { |
3164
b1b1775698d5
uniform ngx_directio_on/off() interface with other file functions
Igor Sysoev <igor@sysoev.ru>
parents:
2934
diff
changeset
|
545 if (ngx_directio_on(fd) == NGX_FILE_ERROR) { |
2129 | 546 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, |
2246
987831d73bd8
cache directio flag in open file cache
Igor Sysoev <igor@sysoev.ru>
parents:
2231
diff
changeset
|
547 ngx_directio_on_n " \"%s\" failed", name); |
2231
8564129d49b6
*) handle unaligned file part for directio
Igor Sysoev <igor@sysoev.ru>
parents:
2129
diff
changeset
|
548 |
8564129d49b6
*) handle unaligned file part for directio
Igor Sysoev <igor@sysoev.ru>
parents:
2129
diff
changeset
|
549 } else { |
8564129d49b6
*) handle unaligned file part for directio
Igor Sysoev <igor@sysoev.ru>
parents:
2129
diff
changeset
|
550 of->is_directio = 1; |
2129 | 551 } |
552 } | |
1453 | 553 } |
554 | |
2069
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
555 done: |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
556 |
1453 | 557 of->uniq = ngx_file_uniq(&fi); |
558 of->mtime = ngx_file_mtime(&fi); | |
559 of->size = ngx_file_size(&fi); | |
3899
e7cd13b7f759
Use more precise stat.st_blocks to account cache size on Unix
Igor Sysoev <igor@sysoev.ru>
parents:
3497
diff
changeset
|
560 of->fs_size = ngx_file_fs_size(&fi); |
1453 | 561 of->is_dir = ngx_is_dir(&fi); |
562 of->is_file = ngx_is_file(&fi); | |
563 of->is_link = ngx_is_link(&fi); | |
564 of->is_exec = ngx_is_exec(&fi); | |
565 | |
566 return NGX_OK; | |
2069
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
567 |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
568 failed: |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
569 |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
570 of->fd = NGX_INVALID_FILE; |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
571 of->err = ngx_errno; |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
572 |
23930ccd2642
use ngx_file_info() and test uniq if file is already open
Igor Sysoev <igor@sysoev.ru>
parents:
2063
diff
changeset
|
573 return NGX_ERROR; |
1453 | 574 } |
575 | |
576 | |
1775 | 577 /* |
578 * we ignore any possible event setting error and | |
579 * fallback to usual periodic file retests | |
580 */ | |
581 | |
582 static void | |
583 ngx_open_file_add_event(ngx_open_file_cache_t *cache, | |
584 ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log) | |
585 { | |
586 ngx_open_file_cache_event_t *fev; | |
587 | |
588 if (!(ngx_event_flags & NGX_USE_VNODE_EVENT) | |
589 || !of->events | |
590 || file->event | |
591 || of->fd == NGX_INVALID_FILE | |
592 || file->uses < of->min_uses) | |
593 { | |
594 return; | |
595 } | |
596 | |
2070 | 597 file->use_event = 0; |
598 | |
1775 | 599 file->event = ngx_calloc(sizeof(ngx_event_t), log); |
600 if (file->event== NULL) { | |
601 return; | |
602 } | |
603 | |
604 fev = ngx_alloc(sizeof(ngx_open_file_cache_event_t), log); | |
605 if (fev == NULL) { | |
606 ngx_free(file->event); | |
607 file->event = NULL; | |
608 return; | |
609 } | |
610 | |
611 fev->fd = of->fd; | |
612 fev->file = file; | |
613 fev->cache = cache; | |
614 | |
615 file->event->handler = ngx_open_file_cache_remove; | |
616 file->event->data = fev; | |
617 | |
618 /* | |
619 * although vnode event may be called while ngx_cycle->poll | |
620 * destruction, however, cleanup procedures are run before any | |
621 * memory freeing and events will be canceled. | |
622 */ | |
623 | |
624 file->event->log = ngx_cycle->log; | |
625 | |
626 if (ngx_add_event(file->event, NGX_VNODE_EVENT, NGX_ONESHOT_EVENT) | |
627 != NGX_OK) | |
628 { | |
629 ngx_free(file->event->data); | |
630 ngx_free(file->event); | |
631 file->event = NULL; | |
632 return; | |
633 } | |
634 | |
635 /* | |
2071 | 636 * we do not set file->use_event here because there may be a race |
637 * condition: a file may be deleted between opening the file and | |
638 * adding event, so we rely upon event notification only after | |
639 * one file revalidation on next file access | |
1775 | 640 */ |
641 | |
642 return; | |
643 } | |
644 | |
645 | |
1453 | 646 static void |
647 ngx_open_file_cleanup(void *data) | |
648 { | |
649 ngx_open_file_cache_cleanup_t *c = data; | |
650 | |
651 c->file->count--; | |
652 | |
1772 | 653 ngx_close_cached_file(c->cache, c->file, c->min_uses, c->log); |
1453 | 654 |
655 /* drop one or two expired open files */ | |
656 ngx_expire_old_cached_files(c->cache, 1, c->log); | |
657 } | |
658 | |
659 | |
660 static void | |
661 ngx_close_cached_file(ngx_open_file_cache_t *cache, | |
1772 | 662 ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log) |
1453 | 663 { |
1772 | 664 ngx_log_debug5(NGX_LOG_DEBUG_CORE, log, 0, |
665 "close cached open file: %s, fd:%d, c:%d, u:%d, %d", | |
666 file->name, file->fd, file->count, file->uses, file->close); | |
1453 | 667 |
668 if (!file->close) { | |
669 | |
670 file->accessed = ngx_time(); | |
671 | |
1765 | 672 ngx_queue_remove(&file->queue); |
1453 | 673 |
1765 | 674 ngx_queue_insert_head(&cache->expire_queue, &file->queue); |
1453 | 675 |
1772 | 676 if (file->uses >= min_uses || file->count) { |
677 return; | |
678 } | |
1453 | 679 } |
680 | |
1775 | 681 ngx_open_file_del_event(file); |
1453 | 682 |
683 if (file->count) { | |
684 return; | |
685 } | |
686 | |
1772 | 687 if (file->fd != NGX_INVALID_FILE) { |
688 | |
689 if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { | |
690 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, | |
691 ngx_close_file_n " \"%s\" failed", file->name); | |
692 } | |
693 | |
694 file->fd = NGX_INVALID_FILE; | |
695 } | |
696 | |
697 if (!file->close) { | |
698 return; | |
1453 | 699 } |
700 | |
701 ngx_free(file->name); | |
702 ngx_free(file); | |
703 } | |
704 | |
705 | |
706 static void | |
1775 | 707 ngx_open_file_del_event(ngx_cached_open_file_t *file) |
708 { | |
709 if (file->event == NULL) { | |
710 return; | |
711 } | |
712 | |
713 (void) ngx_del_event(file->event, NGX_VNODE_EVENT, | |
714 file->count ? NGX_FLUSH_EVENT : NGX_CLOSE_EVENT); | |
715 | |
716 ngx_free(file->event->data); | |
717 ngx_free(file->event); | |
718 file->event = NULL; | |
719 file->use_event = 0; | |
720 } | |
721 | |
722 | |
723 static void | |
1453 | 724 ngx_expire_old_cached_files(ngx_open_file_cache_t *cache, ngx_uint_t n, |
725 ngx_log_t *log) | |
726 { | |
727 time_t now; | |
1765 | 728 ngx_queue_t *q; |
1453 | 729 ngx_cached_open_file_t *file; |
730 | |
731 now = ngx_time(); | |
732 | |
733 /* | |
734 * n == 1 deletes one or two inactive files | |
735 * n == 0 deletes least recently used file by force | |
736 * and one or two inactive files | |
737 */ | |
738 | |
739 while (n < 3) { | |
740 | |
1765 | 741 if (ngx_queue_empty(&cache->expire_queue)) { |
1453 | 742 return; |
743 } | |
744 | |
1765 | 745 q = ngx_queue_last(&cache->expire_queue); |
746 | |
747 file = ngx_queue_data(q, ngx_cached_open_file_t, queue); | |
748 | |
1453 | 749 if (n++ != 0 && now - file->accessed <= cache->inactive) { |
750 return; | |
751 } | |
752 | |
1765 | 753 ngx_queue_remove(q); |
1453 | 754 |
755 ngx_rbtree_delete(&cache->rbtree, &file->node); | |
756 | |
757 cache->current--; | |
758 | |
759 ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, | |
760 "expire cached open file: %s", file->name); | |
761 | |
762 if (!file->err && !file->is_dir) { | |
763 file->close = 1; | |
1772 | 764 ngx_close_cached_file(cache, file, 0, log); |
1453 | 765 |
766 } else { | |
767 ngx_free(file->name); | |
768 ngx_free(file); | |
769 } | |
770 } | |
771 } | |
772 | |
773 | |
774 static void | |
775 ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp, | |
776 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) | |
777 { | |
778 ngx_rbtree_node_t **p; | |
779 ngx_cached_open_file_t *file, *file_temp; | |
780 | |
781 for ( ;; ) { | |
782 | |
783 if (node->key < temp->key) { | |
784 | |
785 p = &temp->left; | |
786 | |
787 } else if (node->key > temp->key) { | |
788 | |
789 p = &temp->right; | |
790 | |
791 } else { /* node->key == temp->key */ | |
792 | |
793 file = (ngx_cached_open_file_t *) node; | |
794 file_temp = (ngx_cached_open_file_t *) temp; | |
795 | |
796 p = (ngx_strcmp(file->name, file_temp->name) < 0) | |
797 ? &temp->left : &temp->right; | |
798 } | |
799 | |
800 if (*p == sentinel) { | |
801 break; | |
802 } | |
803 | |
804 temp = *p; | |
805 } | |
806 | |
807 *p = node; | |
808 node->parent = temp; | |
809 node->left = sentinel; | |
810 node->right = sentinel; | |
811 ngx_rbt_red(node); | |
812 } | |
813 | |
814 | |
1775 | 815 static ngx_cached_open_file_t * |
816 ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name, | |
817 uint32_t hash) | |
818 { | |
819 ngx_int_t rc; | |
820 ngx_rbtree_node_t *node, *sentinel; | |
821 ngx_cached_open_file_t *file; | |
822 | |
823 node = cache->rbtree.root; | |
824 sentinel = cache->rbtree.sentinel; | |
825 | |
826 while (node != sentinel) { | |
827 | |
828 if (hash < node->key) { | |
829 node = node->left; | |
830 continue; | |
831 } | |
832 | |
833 if (hash > node->key) { | |
834 node = node->right; | |
835 continue; | |
836 } | |
837 | |
838 /* hash == node->key */ | |
839 | |
4515 | 840 file = (ngx_cached_open_file_t *) node; |
1775 | 841 |
4515 | 842 rc = ngx_strcmp(name->data, file->name); |
1775 | 843 |
4515 | 844 if (rc == 0) { |
845 return file; | |
846 } | |
1775 | 847 |
4515 | 848 node = (rc < 0) ? node->left : node->right; |
1775 | 849 } |
850 | |
851 return NULL; | |
852 } | |
853 | |
854 | |
1453 | 855 static void |
856 ngx_open_file_cache_remove(ngx_event_t *ev) | |
857 { | |
858 ngx_cached_open_file_t *file; | |
859 ngx_open_file_cache_event_t *fev; | |
860 | |
861 fev = ev->data; | |
862 file = fev->file; | |
863 | |
1765 | 864 ngx_queue_remove(&file->queue); |
1453 | 865 |
866 ngx_rbtree_delete(&fev->cache->rbtree, &file->node); | |
867 | |
868 fev->cache->current--; | |
869 | |
870 /* NGX_ONESHOT_EVENT was already deleted */ | |
871 file->event = NULL; | |
2070 | 872 file->use_event = 0; |
1453 | 873 |
874 file->close = 1; | |
875 | |
1772 | 876 ngx_close_cached_file(fev->cache, file, 0, ev->log); |
1453 | 877 |
878 /* free memory only when fev->cache and fev->file are already not needed */ | |
879 | |
880 ngx_free(ev->data); | |
881 ngx_free(ev); | |
882 } |