blob: 173980fa8bd547371796e8734ed9b2f25720d70c [file] [log] [blame]
Igor Sysoevd92bee52007-09-01 12:11:21 +00001
2/*
3 * Copyright (C) Igor Sysoev
4 */
5
6
7#include <ngx_config.h>
8#include <ngx_core.h>
9#include <ngx_event.h>
10
11
12/*
13 * open file cache caches
14 * open file handles with stat() info;
15 * directories stat() info;
16 * files and directories errors: not found, access denied, etc.
17 */
18
19
20static void ngx_open_file_cache_cleanup(void *data);
Igor Sysoev421a3b82007-12-25 10:46:40 +000021static ngx_int_t ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of,
22 ngx_log_t *log);
23static void ngx_open_file_add_event(ngx_open_file_cache_t *cache,
24 ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log);
Igor Sysoevd92bee52007-09-01 12:11:21 +000025static void ngx_open_file_cleanup(void *data);
26static void ngx_close_cached_file(ngx_open_file_cache_t *cache,
Igor Sysoevf3b0e492007-12-22 13:19:39 +000027 ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log);
Igor Sysoev421a3b82007-12-25 10:46:40 +000028static void ngx_open_file_del_event(ngx_cached_open_file_t *file);
Igor Sysoevd92bee52007-09-01 12:11:21 +000029static void ngx_expire_old_cached_files(ngx_open_file_cache_t *cache,
30 ngx_uint_t n, ngx_log_t *log);
31static void ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
32 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
Igor Sysoev421a3b82007-12-25 10:46:40 +000033static ngx_cached_open_file_t *
34 ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name,
35 uint32_t hash);
Igor Sysoevd92bee52007-09-01 12:11:21 +000036static void ngx_open_file_cache_remove(ngx_event_t *ev);
37
38
39ngx_open_file_cache_t *
40ngx_open_file_cache_init(ngx_pool_t *pool, ngx_uint_t max, time_t inactive)
41{
Igor Sysoevd92bee52007-09-01 12:11:21 +000042 ngx_pool_cleanup_t *cln;
43 ngx_open_file_cache_t *cache;
44
45 cache = ngx_palloc(pool, sizeof(ngx_open_file_cache_t));
46 if (cache == NULL) {
47 return NULL;
48 }
49
Igor Sysoevddc8cbd2007-12-20 21:29:52 +000050 ngx_rbtree_init(&cache->rbtree, &cache->sentinel,
Igor Sysoev7912e4b2007-12-17 08:52:00 +000051 ngx_open_file_cache_rbtree_insert_value);
Igor Sysoevd92bee52007-09-01 12:11:21 +000052
Igor Sysoevff71b942007-12-21 15:33:15 +000053 ngx_queue_init(&cache->expire_queue);
54
Igor Sysoevd92bee52007-09-01 12:11:21 +000055 cache->current = 0;
56 cache->max = max;
57 cache->inactive = inactive;
58
59 cln = ngx_pool_cleanup_add(pool, 0);
60 if (cln == NULL) {
61 return NULL;
62 }
63
64 cln->handler = ngx_open_file_cache_cleanup;
65 cln->data = cache;
66
67 return cache;
68}
69
70
71static void
72ngx_open_file_cache_cleanup(void *data)
73{
74 ngx_open_file_cache_t *cache = data;
75
Igor Sysoevff71b942007-12-21 15:33:15 +000076 ngx_queue_t *q;
Igor Sysoevd92bee52007-09-01 12:11:21 +000077 ngx_cached_open_file_t *file;
78
79 ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
80 "open file cache cleanup");
81
82 for ( ;; ) {
83
Igor Sysoevff71b942007-12-21 15:33:15 +000084 if (ngx_queue_empty(&cache->expire_queue)) {
Igor Sysoevf2d60af2007-12-21 16:19:14 +000085 break;
Igor Sysoevd92bee52007-09-01 12:11:21 +000086 }
87
Igor Sysoevff71b942007-12-21 15:33:15 +000088 q = ngx_queue_last(&cache->expire_queue);
89
90 file = ngx_queue_data(q, ngx_cached_open_file_t, queue);
91
92 ngx_queue_remove(q);
Igor Sysoevd92bee52007-09-01 12:11:21 +000093
94 ngx_rbtree_delete(&cache->rbtree, &file->node);
95
96 cache->current--;
97
98 ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
99 "delete cached open file: %s", file->name);
100
101 if (!file->err && !file->is_dir) {
102 file->close = 1;
103 file->count = 0;
Igor Sysoevf3b0e492007-12-22 13:19:39 +0000104 ngx_close_cached_file(cache, file, 0, ngx_cycle->log);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000105
106 } else {
107 ngx_free(file->name);
108 ngx_free(file);
109 }
110 }
111
112 if (cache->current) {
113 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
114 "%d items still leave in open file cache",
115 cache->current);
116 }
117
118 if (cache->rbtree.root != cache->rbtree.sentinel) {
119 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
120 "rbtree still is not empty in open file cache");
121
122 }
123}
124
125
126ngx_int_t
127ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name,
128 ngx_open_file_info_t *of, ngx_pool_t *pool)
129{
130 time_t now;
131 uint32_t hash;
132 ngx_int_t rc;
Igor Sysoevf1cc4572009-04-27 09:55:53 +0000133 ngx_file_info_t fi;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000134 ngx_pool_cleanup_t *cln;
135 ngx_cached_open_file_t *file;
136 ngx_pool_cleanup_file_t *clnf;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000137 ngx_open_file_cache_cleanup_t *ofcln;
138
Igor Sysoevd6711d32008-06-26 16:10:13 +0000139 of->fd = NGX_INVALID_FILE;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000140 of->err = 0;
141
142 if (cache == NULL) {
143
Igor Sysoevf1cc4572009-04-27 09:55:53 +0000144 if (of->test_only) {
145
Igor Sysoevef919752009-04-29 19:28:52 +0000146 if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) {
Igor Sysoevf1cc4572009-04-27 09:55:53 +0000147 of->err = ngx_errno;
148 of->failed = ngx_file_info_n;
149 return NGX_ERROR;
150 }
151
152 of->uniq = ngx_file_uniq(&fi);
153 of->mtime = ngx_file_mtime(&fi);
154 of->size = ngx_file_size(&fi);
155 of->is_dir = ngx_is_dir(&fi);
156 of->is_file = ngx_is_file(&fi);
157 of->is_link = ngx_is_link(&fi);
158 of->is_exec = ngx_is_exec(&fi);
159
160 return NGX_OK;
161 }
162
Igor Sysoevd92bee52007-09-01 12:11:21 +0000163 cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t));
164 if (cln == NULL) {
165 return NGX_ERROR;
166 }
167
168 rc = ngx_open_and_stat_file(name->data, of, pool->log);
169
170 if (rc == NGX_OK && !of->is_dir) {
171 cln->handler = ngx_pool_cleanup_file;
172 clnf = cln->data;
173
174 clnf->fd = of->fd;
175 clnf->name = name->data;
176 clnf->log = pool->log;
177 }
178
179 return rc;
180 }
181
182 cln = ngx_pool_cleanup_add(pool, sizeof(ngx_open_file_cache_cleanup_t));
183 if (cln == NULL) {
184 return NGX_ERROR;
185 }
186
Igor Sysoevd92bee52007-09-01 12:11:21 +0000187 now = ngx_time();
188
Igor Sysoev421a3b82007-12-25 10:46:40 +0000189 hash = ngx_crc32_long(name->data, name->len);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000190
Igor Sysoev421a3b82007-12-25 10:46:40 +0000191 file = ngx_open_file_lookup(cache, name, hash);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000192
Igor Sysoev421a3b82007-12-25 10:46:40 +0000193 if (file) {
Igor Sysoevd92bee52007-09-01 12:11:21 +0000194
Igor Sysoev421a3b82007-12-25 10:46:40 +0000195 file->uses++;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000196
Igor Sysoevada91902008-04-29 18:14:45 +0000197 ngx_queue_remove(&file->queue);
198
Igor Sysoev421a3b82007-12-25 10:46:40 +0000199 if (file->fd == NGX_INVALID_FILE && file->err == 0 && !file->is_dir) {
Igor Sysoevd92bee52007-09-01 12:11:21 +0000200
Igor Sysoev421a3b82007-12-25 10:46:40 +0000201 /* file was not used often enough to keep open */
Igor Sysoevd92bee52007-09-01 12:11:21 +0000202
Igor Sysoev421a3b82007-12-25 10:46:40 +0000203 rc = ngx_open_and_stat_file(name->data, of, pool->log);
Igor Sysoevf3b0e492007-12-22 13:19:39 +0000204
Igor Sysoev421a3b82007-12-25 10:46:40 +0000205 if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
206 goto failed;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000207 }
208
Igor Sysoev421a3b82007-12-25 10:46:40 +0000209 goto add_event;
210 }
Igor Sysoevd92bee52007-09-01 12:11:21 +0000211
Igor Sysoevbf934762008-06-30 12:11:47 +0000212 if (file->use_event
Igor Sysoev3e6f74d2008-06-23 13:35:34 +0000213 || (file->event == NULL
214 && (of->uniq == 0 || of->uniq == file->uniq)
215 && now - file->created < of->valid))
Igor Sysoev421a3b82007-12-25 10:46:40 +0000216 {
217 if (file->err == 0) {
Igor Sysoevd92bee52007-09-01 12:11:21 +0000218
Igor Sysoev421a3b82007-12-25 10:46:40 +0000219 of->fd = file->fd;
220 of->uniq = file->uniq;
221 of->mtime = file->mtime;
222 of->size = file->size;
223
224 of->is_dir = file->is_dir;
225 of->is_file = file->is_file;
226 of->is_link = file->is_link;
227 of->is_exec = file->is_exec;
Igor Sysoev77cdae12008-09-12 13:39:51 +0000228 of->is_directio = file->is_directio;
Igor Sysoev421a3b82007-12-25 10:46:40 +0000229
230 if (!file->is_dir) {
231 file->count++;
232 ngx_open_file_add_event(cache, file, of, pool->log);
233 }
234
235 } else {
236 of->err = file->err;
Igor Sysoev5461f392009-04-30 08:01:50 +0000237 of->failed = ngx_open_file_n;
Igor Sysoev421a3b82007-12-25 10:46:40 +0000238 }
239
240 goto found;
241 }
242
243 ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0,
244 "retest open file: %s, fd:%d, c:%d, e:%d",
245 file->name, file->fd, file->count, file->err);
246
247 if (file->is_dir) {
248
249 /*
250 * chances that directory became file are very small
251 * so test_dir flag allows to use a single syscall
252 * in ngx_file_info() instead of three syscalls
253 */
254
255 of->test_dir = 1;
256 }
257
Igor Sysoevd6711d32008-06-26 16:10:13 +0000258 of->fd = file->fd;
259 of->uniq = file->uniq;
260
Igor Sysoev421a3b82007-12-25 10:46:40 +0000261 rc = ngx_open_and_stat_file(name->data, of, pool->log);
262
263 if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
264 goto failed;
265 }
266
267 if (of->is_dir) {
268
269 if (file->is_dir || file->err) {
270 goto update;
271 }
272
273 /* file became directory */
274
275 } else if (of->err == 0) { /* file */
276
277 if (file->is_dir || file->err) {
278 goto add_event;
279 }
280
Igor Sysoevbf934762008-06-30 12:11:47 +0000281 if (of->uniq == file->uniq) {
Igor Sysoev421a3b82007-12-25 10:46:40 +0000282
Igor Sysoev421a3b82007-12-25 10:46:40 +0000283 file->count++;
284
285 if (file->event) {
286 file->use_event = 1;
Igor Sysoev421a3b82007-12-25 10:46:40 +0000287 }
288
Igor Sysoev421a3b82007-12-25 10:46:40 +0000289 goto renew;
290 }
291
292 /* file was changed */
293
294 } else { /* error to cache */
295
296 if (file->err || file->is_dir) {
297 goto update;
298 }
299
300 /* file was removed, etc. */
301 }
302
303 if (file->count == 0) {
304
305 ngx_open_file_del_event(file);
306
307 if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
308 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
309 ngx_close_file_n " \"%s\" failed",
310 name->data);
311 }
312
313 goto add_event;
314 }
315
316 ngx_rbtree_delete(&cache->rbtree, &file->node);
317
318 cache->current--;
319
320 file->close = 1;
321
322 goto create;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000323 }
324
325 /* not found */
326
Igor Sysoevd92bee52007-09-01 12:11:21 +0000327 rc = ngx_open_and_stat_file(name->data, of, pool->log);
328
329 if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
330 goto failed;
331 }
332
333create:
334
335 if (cache->current >= cache->max) {
336 ngx_expire_old_cached_files(cache, 0, pool->log);
337 }
338
339 file = ngx_alloc(sizeof(ngx_cached_open_file_t), pool->log);
340
341 if (file == NULL) {
342 goto failed;
343 }
344
345 file->name = ngx_alloc(name->len + 1, pool->log);
346
347 if (file->name == NULL) {
348 ngx_free(file);
349 file = NULL;
350 goto failed;
351 }
352
353 ngx_cpystrn(file->name, name->data, name->len + 1);
354
355 file->node.key = hash;
356
357 ngx_rbtree_insert(&cache->rbtree, &file->node);
358
359 cache->current++;
360
Igor Sysoevf3b0e492007-12-22 13:19:39 +0000361 file->uses = 1;
Igor Sysoev421a3b82007-12-25 10:46:40 +0000362 file->count = 0;
Igor Sysoev421a3b82007-12-25 10:46:40 +0000363 file->event = NULL;
364
365add_event:
366
367 ngx_open_file_add_event(cache, file, of, pool->log);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000368
369update:
370
Igor Sysoevd92bee52007-09-01 12:11:21 +0000371 file->fd = of->fd;
372 file->err = of->err;
373
374 if (of->err == 0) {
375 file->uniq = of->uniq;
376 file->mtime = of->mtime;
377 file->size = of->size;
378
379 file->close = 0;
380
381 file->is_dir = of->is_dir;
382 file->is_file = of->is_file;
383 file->is_link = of->is_link;
384 file->is_exec = of->is_exec;
Igor Sysoev77cdae12008-09-12 13:39:51 +0000385 file->is_directio = of->is_directio;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000386
387 if (!of->is_dir) {
388 file->count++;
389 }
390 }
391
392renew:
393
394 file->created = now;
395
396found:
397
398 file->accessed = now;
399
Igor Sysoevff71b942007-12-21 15:33:15 +0000400 ngx_queue_insert_head(&cache->expire_queue, &file->queue);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000401
Igor Sysoevf3b0e492007-12-22 13:19:39 +0000402 ngx_log_debug5(NGX_LOG_DEBUG_CORE, pool->log, 0,
403 "cached open file: %s, fd:%d, c:%d, e:%d, u:%d",
404 file->name, file->fd, file->count, file->err, file->uses);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000405
406 if (of->err == 0) {
407
408 if (!of->is_dir) {
409 cln->handler = ngx_open_file_cleanup;
410 ofcln = cln->data;
411
412 ofcln->cache = cache;
413 ofcln->file = file;
Igor Sysoevf3b0e492007-12-22 13:19:39 +0000414 ofcln->min_uses = of->min_uses;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000415 ofcln->log = pool->log;
416 }
417
418 return NGX_OK;
419 }
420
421 return NGX_ERROR;
422
423failed:
424
Igor Sysoeva3278412008-04-29 18:15:23 +0000425 if (file) {
Igor Sysoevd92bee52007-09-01 12:11:21 +0000426 ngx_rbtree_delete(&cache->rbtree, &file->node);
427
428 cache->current--;
429
Igor Sysoeva3278412008-04-29 18:15:23 +0000430 if (file->count == 0) {
Igor Sysoevd92bee52007-09-01 12:11:21 +0000431
Igor Sysoev433608c2008-05-14 07:54:52 +0000432 if (file->fd != NGX_INVALID_FILE) {
433 if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
434 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
435 ngx_close_file_n " \"%s\" failed",
Igor Sysoeva3278412008-04-29 18:15:23 +0000436 file->name);
Igor Sysoev433608c2008-05-14 07:54:52 +0000437 }
438 }
Igor Sysoeva3278412008-04-29 18:15:23 +0000439
440 ngx_free(file->name);
441 ngx_free(file);
442
443 } else {
444 file->close = 1;
445 }
Igor Sysoevd92bee52007-09-01 12:11:21 +0000446 }
447
448 if (of->fd != NGX_INVALID_FILE) {
449 if (ngx_close_file(of->fd) == NGX_FILE_ERROR) {
450 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
451 ngx_close_file_n " \"%s\" failed", name->data);
452 }
453 }
454
455 return NGX_ERROR;
456}
457
458
459static ngx_int_t
460ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, ngx_log_t *log)
461{
462 ngx_fd_t fd;
463 ngx_file_info_t fi;
464
Igor Sysoevbf934762008-06-30 12:11:47 +0000465 if (of->fd != NGX_INVALID_FILE) {
Igor Sysoevd92bee52007-09-01 12:11:21 +0000466
Igor Sysoevef919752009-04-29 19:28:52 +0000467 if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) {
Igor Sysoevf1cc4572009-04-27 09:55:53 +0000468 of->failed = ngx_file_info_n;
Igor Sysoevd6711d32008-06-26 16:10:13 +0000469 goto failed;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000470 }
471
Igor Sysoevbf934762008-06-30 12:11:47 +0000472 if (of->uniq == ngx_file_uniq(&fi)) {
Igor Sysoevd6711d32008-06-26 16:10:13 +0000473 goto done;
474 }
Igor Sysoevd92bee52007-09-01 12:11:21 +0000475
Igor Sysoevbf934762008-06-30 12:11:47 +0000476 } else if (of->test_dir) {
477
Igor Sysoevef919752009-04-29 19:28:52 +0000478 if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) {
Igor Sysoevf1cc4572009-04-27 09:55:53 +0000479 of->failed = ngx_file_info_n;
Igor Sysoevbf934762008-06-30 12:11:47 +0000480 goto failed;
481 }
482
Igor Sysoev467f4372009-01-21 15:50:52 +0000483 if (ngx_is_dir(&fi)) {
Igor Sysoevd6711d32008-06-26 16:10:13 +0000484 goto done;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000485 }
486 }
487
Igor Sysoevb8821542008-06-30 12:27:24 +0000488 if (!of->log) {
489 fd = ngx_open_file(name, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
490
491 } else {
Igor Sysoev24c27872009-03-31 13:52:01 +0000492 fd = ngx_open_file(name, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN,
493 NGX_FILE_DEFAULT_ACCESS);
Igor Sysoevb8821542008-06-30 12:27:24 +0000494 }
Igor Sysoevd92bee52007-09-01 12:11:21 +0000495
496 if (fd == NGX_INVALID_FILE) {
Igor Sysoevf1cc4572009-04-27 09:55:53 +0000497 of->failed = ngx_open_file_n;
Igor Sysoevd6711d32008-06-26 16:10:13 +0000498 goto failed;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000499 }
500
501 if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
502 ngx_log_error(NGX_LOG_CRIT, log, ngx_errno,
503 ngx_fd_info_n " \"%s\" failed", name);
504
505 if (ngx_close_file(fd) == NGX_FILE_ERROR) {
506 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
507 ngx_close_file_n " \"%s\" failed", name);
508 }
509
Igor Sysoevd6711d32008-06-26 16:10:13 +0000510 of->fd = NGX_INVALID_FILE;
511
Igor Sysoevd92bee52007-09-01 12:11:21 +0000512 return NGX_ERROR;
513 }
514
515 if (ngx_is_dir(&fi)) {
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
Igor Sysoevd6711d32008-06-26 16:10:13 +0000521 of->fd = NGX_INVALID_FILE;
522
523 } else {
524 of->fd = fd;
Igor Sysoev385af282008-07-30 12:34:04 +0000525
526 if (of->directio <= ngx_file_size(&fi)) {
Igor Sysoev77cdae12008-09-12 13:39:51 +0000527 if (ngx_directio_on(fd) == -1) {
Igor Sysoev385af282008-07-30 12:34:04 +0000528 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
Igor Sysoev77cdae12008-09-12 13:39:51 +0000529 ngx_directio_on_n " \"%s\" failed", name);
Igor Sysoev8633e1f2008-09-05 14:48:47 +0000530
531 } else {
532 of->is_directio = 1;
Igor Sysoev385af282008-07-30 12:34:04 +0000533 }
534 }
Igor Sysoevd92bee52007-09-01 12:11:21 +0000535 }
536
Igor Sysoevd6711d32008-06-26 16:10:13 +0000537done:
538
Igor Sysoevd92bee52007-09-01 12:11:21 +0000539 of->uniq = ngx_file_uniq(&fi);
540 of->mtime = ngx_file_mtime(&fi);
541 of->size = ngx_file_size(&fi);
542 of->is_dir = ngx_is_dir(&fi);
543 of->is_file = ngx_is_file(&fi);
544 of->is_link = ngx_is_link(&fi);
545 of->is_exec = ngx_is_exec(&fi);
546
547 return NGX_OK;
Igor Sysoevd6711d32008-06-26 16:10:13 +0000548
549failed:
550
551 of->fd = NGX_INVALID_FILE;
552 of->err = ngx_errno;
553
554 return NGX_ERROR;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000555}
556
557
Igor Sysoev421a3b82007-12-25 10:46:40 +0000558/*
559 * we ignore any possible event setting error and
560 * fallback to usual periodic file retests
561 */
562
563static void
564ngx_open_file_add_event(ngx_open_file_cache_t *cache,
565 ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log)
566{
567 ngx_open_file_cache_event_t *fev;
568
569 if (!(ngx_event_flags & NGX_USE_VNODE_EVENT)
570 || !of->events
571 || file->event
572 || of->fd == NGX_INVALID_FILE
573 || file->uses < of->min_uses)
574 {
575 return;
576 }
577
Igor Sysoevbf934762008-06-30 12:11:47 +0000578 file->use_event = 0;
579
Igor Sysoev421a3b82007-12-25 10:46:40 +0000580 file->event = ngx_calloc(sizeof(ngx_event_t), log);
581 if (file->event== NULL) {
582 return;
583 }
584
585 fev = ngx_alloc(sizeof(ngx_open_file_cache_event_t), log);
586 if (fev == NULL) {
587 ngx_free(file->event);
588 file->event = NULL;
589 return;
590 }
591
592 fev->fd = of->fd;
593 fev->file = file;
594 fev->cache = cache;
595
596 file->event->handler = ngx_open_file_cache_remove;
597 file->event->data = fev;
598
599 /*
600 * although vnode event may be called while ngx_cycle->poll
601 * destruction, however, cleanup procedures are run before any
602 * memory freeing and events will be canceled.
603 */
604
605 file->event->log = ngx_cycle->log;
606
607 if (ngx_add_event(file->event, NGX_VNODE_EVENT, NGX_ONESHOT_EVENT)
608 != NGX_OK)
609 {
610 ngx_free(file->event->data);
611 ngx_free(file->event);
612 file->event = NULL;
613 return;
614 }
615
616 /*
Igor Sysoevc3584fe2008-06-30 12:12:16 +0000617 * we do not set file->use_event here because there may be a race
618 * condition: a file may be deleted between opening the file and
619 * adding event, so we rely upon event notification only after
620 * one file revalidation on next file access
Igor Sysoev421a3b82007-12-25 10:46:40 +0000621 */
622
623 return;
624}
625
626
Igor Sysoevd92bee52007-09-01 12:11:21 +0000627static void
628ngx_open_file_cleanup(void *data)
629{
630 ngx_open_file_cache_cleanup_t *c = data;
631
632 c->file->count--;
633
Igor Sysoevf3b0e492007-12-22 13:19:39 +0000634 ngx_close_cached_file(c->cache, c->file, c->min_uses, c->log);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000635
636 /* drop one or two expired open files */
637 ngx_expire_old_cached_files(c->cache, 1, c->log);
638}
639
640
641static void
642ngx_close_cached_file(ngx_open_file_cache_t *cache,
Igor Sysoevf3b0e492007-12-22 13:19:39 +0000643 ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log)
Igor Sysoevd92bee52007-09-01 12:11:21 +0000644{
Igor Sysoevf3b0e492007-12-22 13:19:39 +0000645 ngx_log_debug5(NGX_LOG_DEBUG_CORE, log, 0,
646 "close cached open file: %s, fd:%d, c:%d, u:%d, %d",
647 file->name, file->fd, file->count, file->uses, file->close);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000648
649 if (!file->close) {
650
651 file->accessed = ngx_time();
652
Igor Sysoevff71b942007-12-21 15:33:15 +0000653 ngx_queue_remove(&file->queue);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000654
Igor Sysoevff71b942007-12-21 15:33:15 +0000655 ngx_queue_insert_head(&cache->expire_queue, &file->queue);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000656
Igor Sysoevf3b0e492007-12-22 13:19:39 +0000657 if (file->uses >= min_uses || file->count) {
658 return;
659 }
Igor Sysoevd92bee52007-09-01 12:11:21 +0000660 }
661
Igor Sysoev421a3b82007-12-25 10:46:40 +0000662 ngx_open_file_del_event(file);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000663
664 if (file->count) {
665 return;
666 }
667
Igor Sysoevf3b0e492007-12-22 13:19:39 +0000668 if (file->fd != NGX_INVALID_FILE) {
669
670 if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
671 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
672 ngx_close_file_n " \"%s\" failed", file->name);
673 }
674
675 file->fd = NGX_INVALID_FILE;
676 }
677
678 if (!file->close) {
679 return;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000680 }
681
682 ngx_free(file->name);
683 ngx_free(file);
684}
685
686
687static void
Igor Sysoev421a3b82007-12-25 10:46:40 +0000688ngx_open_file_del_event(ngx_cached_open_file_t *file)
689{
690 if (file->event == NULL) {
691 return;
692 }
693
694 (void) ngx_del_event(file->event, NGX_VNODE_EVENT,
695 file->count ? NGX_FLUSH_EVENT : NGX_CLOSE_EVENT);
696
697 ngx_free(file->event->data);
698 ngx_free(file->event);
699 file->event = NULL;
700 file->use_event = 0;
701}
702
703
704static void
Igor Sysoevd92bee52007-09-01 12:11:21 +0000705ngx_expire_old_cached_files(ngx_open_file_cache_t *cache, ngx_uint_t n,
706 ngx_log_t *log)
707{
708 time_t now;
Igor Sysoevff71b942007-12-21 15:33:15 +0000709 ngx_queue_t *q;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000710 ngx_cached_open_file_t *file;
711
712 now = ngx_time();
713
714 /*
715 * n == 1 deletes one or two inactive files
716 * n == 0 deletes least recently used file by force
717 * and one or two inactive files
718 */
719
720 while (n < 3) {
721
Igor Sysoevff71b942007-12-21 15:33:15 +0000722 if (ngx_queue_empty(&cache->expire_queue)) {
Igor Sysoevd92bee52007-09-01 12:11:21 +0000723 return;
724 }
725
Igor Sysoevff71b942007-12-21 15:33:15 +0000726 q = ngx_queue_last(&cache->expire_queue);
727
728 file = ngx_queue_data(q, ngx_cached_open_file_t, queue);
729
Igor Sysoevd92bee52007-09-01 12:11:21 +0000730 if (n++ != 0 && now - file->accessed <= cache->inactive) {
731 return;
732 }
733
Igor Sysoevff71b942007-12-21 15:33:15 +0000734 ngx_queue_remove(q);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000735
736 ngx_rbtree_delete(&cache->rbtree, &file->node);
737
738 cache->current--;
739
740 ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0,
741 "expire cached open file: %s", file->name);
742
743 if (!file->err && !file->is_dir) {
744 file->close = 1;
Igor Sysoevf3b0e492007-12-22 13:19:39 +0000745 ngx_close_cached_file(cache, file, 0, log);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000746
747 } else {
748 ngx_free(file->name);
749 ngx_free(file);
750 }
751 }
752}
753
754
755static void
756ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
757 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
758{
759 ngx_rbtree_node_t **p;
760 ngx_cached_open_file_t *file, *file_temp;
761
762 for ( ;; ) {
763
764 if (node->key < temp->key) {
765
766 p = &temp->left;
767
768 } else if (node->key > temp->key) {
769
770 p = &temp->right;
771
772 } else { /* node->key == temp->key */
773
774 file = (ngx_cached_open_file_t *) node;
775 file_temp = (ngx_cached_open_file_t *) temp;
776
777 p = (ngx_strcmp(file->name, file_temp->name) < 0)
778 ? &temp->left : &temp->right;
779 }
780
781 if (*p == sentinel) {
782 break;
783 }
784
785 temp = *p;
786 }
787
788 *p = node;
789 node->parent = temp;
790 node->left = sentinel;
791 node->right = sentinel;
792 ngx_rbt_red(node);
793}
794
795
Igor Sysoev421a3b82007-12-25 10:46:40 +0000796static ngx_cached_open_file_t *
797ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name,
798 uint32_t hash)
799{
800 ngx_int_t rc;
801 ngx_rbtree_node_t *node, *sentinel;
802 ngx_cached_open_file_t *file;
803
804 node = cache->rbtree.root;
805 sentinel = cache->rbtree.sentinel;
806
807 while (node != sentinel) {
808
809 if (hash < node->key) {
810 node = node->left;
811 continue;
812 }
813
814 if (hash > node->key) {
815 node = node->right;
816 continue;
817 }
818
819 /* hash == node->key */
820
821 do {
822 file = (ngx_cached_open_file_t *) node;
823
824 rc = ngx_strcmp(name->data, file->name);
825
826 if (rc == 0) {
827 return file;
828 }
829
830 node = (rc < 0) ? node->left : node->right;
831
832 } while (node != sentinel && hash == node->key);
833
834 break;
835 }
836
837 return NULL;
838}
839
840
Igor Sysoevd92bee52007-09-01 12:11:21 +0000841static void
842ngx_open_file_cache_remove(ngx_event_t *ev)
843{
844 ngx_cached_open_file_t *file;
845 ngx_open_file_cache_event_t *fev;
846
847 fev = ev->data;
848 file = fev->file;
849
Igor Sysoevff71b942007-12-21 15:33:15 +0000850 ngx_queue_remove(&file->queue);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000851
852 ngx_rbtree_delete(&fev->cache->rbtree, &file->node);
853
854 fev->cache->current--;
855
856 /* NGX_ONESHOT_EVENT was already deleted */
857 file->event = NULL;
Igor Sysoevbf934762008-06-30 12:11:47 +0000858 file->use_event = 0;
Igor Sysoevd92bee52007-09-01 12:11:21 +0000859
860 file->close = 1;
861
Igor Sysoevf3b0e492007-12-22 13:19:39 +0000862 ngx_close_cached_file(fev->cache, file, 0, ev->log);
Igor Sysoevd92bee52007-09-01 12:11:21 +0000863
864 /* free memory only when fev->cache and fev->file are already not needed */
865
866 ngx_free(ev->data);
867 ngx_free(ev);
868}