3 * Copyright (C) Igor Sysoev
7 #include <ngx_config.h>
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.
20 static void ngx_open_file_cache_cleanup(void *data);
21 static ngx_int_t ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of,
23 static 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);
25 static void ngx_open_file_cleanup(void *data);
26 static void ngx_close_cached_file(ngx_open_file_cache_t *cache,
27 ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log);
28 static void ngx_open_file_del_event(ngx_cached_open_file_t *file);
29 static void ngx_expire_old_cached_files(ngx_open_file_cache_t *cache,
30 ngx_uint_t n, ngx_log_t *log);
31 static void ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
32 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
33 static ngx_cached_open_file_t *
34 ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name,
36 static void ngx_open_file_cache_remove(ngx_event_t *ev);
39 ngx_open_file_cache_t *
40 ngx_open_file_cache_init(ngx_pool_t *pool, ngx_uint_t max, time_t inactive)
42 ngx_pool_cleanup_t *cln;
43 ngx_open_file_cache_t *cache;
45 cache = ngx_palloc(pool, sizeof(ngx_open_file_cache_t));
50 ngx_rbtree_init(&cache->rbtree, &cache->sentinel,
51 ngx_open_file_cache_rbtree_insert_value);
53 ngx_queue_init(&cache->expire_queue);
57 cache->inactive = inactive;
59 cln = ngx_pool_cleanup_add(pool, 0);
64 cln->handler = ngx_open_file_cache_cleanup;
72 ngx_open_file_cache_cleanup(void *data)
74 ngx_open_file_cache_t *cache = data;
77 ngx_cached_open_file_t *file;
79 ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
80 "open file cache cleanup");
84 if (ngx_queue_empty(&cache->expire_queue)) {
88 q = ngx_queue_last(&cache->expire_queue);
90 file = ngx_queue_data(q, ngx_cached_open_file_t, queue);
94 ngx_rbtree_delete(&cache->rbtree, &file->node);
98 ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
99 "delete cached open file: %s", file->name);
101 if (!file->err && !file->is_dir) {
104 ngx_close_cached_file(cache, file, 0, ngx_cycle->log);
107 ngx_free(file->name);
112 if (cache->current) {
113 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
114 "%d items still leave in open file cache",
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");
127 ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name,
128 ngx_open_file_info_t *of, ngx_pool_t *pool)
133 ngx_pool_cleanup_t *cln;
134 ngx_cached_open_file_t *file;
135 ngx_pool_cleanup_file_t *clnf;
136 ngx_open_file_cache_cleanup_t *ofcln;
138 of->fd = NGX_INVALID_FILE;
143 cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t));
148 rc = ngx_open_and_stat_file(name->data, of, pool->log);
150 if (rc == NGX_OK && !of->is_dir) {
151 cln->handler = ngx_pool_cleanup_file;
155 clnf->name = name->data;
156 clnf->log = pool->log;
162 cln = ngx_pool_cleanup_add(pool, sizeof(ngx_open_file_cache_cleanup_t));
169 hash = ngx_crc32_long(name->data, name->len);
171 file = ngx_open_file_lookup(cache, name, hash);
177 ngx_queue_remove(&file->queue);
179 if (file->fd == NGX_INVALID_FILE && file->err == 0 && !file->is_dir) {
181 /* file was not used often enough to keep open */
183 rc = ngx_open_and_stat_file(name->data, of, pool->log);
185 if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
193 || (file->event == NULL
194 && (of->uniq == 0 || of->uniq == file->uniq)
195 && now - file->created < of->valid))
197 if (file->err == 0) {
200 of->uniq = file->uniq;
201 of->mtime = file->mtime;
202 of->size = file->size;
204 of->is_dir = file->is_dir;
205 of->is_file = file->is_file;
206 of->is_link = file->is_link;
207 of->is_exec = file->is_exec;
208 of->is_directio = file->is_directio;
212 ngx_open_file_add_event(cache, file, of, pool->log);
222 ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0,
223 "retest open file: %s, fd:%d, c:%d, e:%d",
224 file->name, file->fd, file->count, file->err);
229 * chances that directory became file are very small
230 * so test_dir flag allows to use a single syscall
231 * in ngx_file_info() instead of three syscalls
238 of->uniq = file->uniq;
240 rc = ngx_open_and_stat_file(name->data, of, pool->log);
242 if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
248 if (file->is_dir || file->err) {
252 /* file became directory */
254 } else if (of->err == 0) { /* file */
256 if (file->is_dir || file->err) {
260 if (of->uniq == file->uniq) {
271 /* file was changed */
273 } else { /* error to cache */
275 if (file->err || file->is_dir) {
279 /* file was removed, etc. */
282 if (file->count == 0) {
284 ngx_open_file_del_event(file);
286 if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
287 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
288 ngx_close_file_n " \"%s\" failed",
295 ngx_rbtree_delete(&cache->rbtree, &file->node);
306 rc = ngx_open_and_stat_file(name->data, of, pool->log);
308 if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
314 if (cache->current >= cache->max) {
315 ngx_expire_old_cached_files(cache, 0, pool->log);
318 file = ngx_alloc(sizeof(ngx_cached_open_file_t), pool->log);
324 file->name = ngx_alloc(name->len + 1, pool->log);
326 if (file->name == NULL) {
332 ngx_cpystrn(file->name, name->data, name->len + 1);
334 file->node.key = hash;
336 ngx_rbtree_insert(&cache->rbtree, &file->node);
346 ngx_open_file_add_event(cache, file, of, pool->log);
354 file->uniq = of->uniq;
355 file->mtime = of->mtime;
356 file->size = of->size;
360 file->is_dir = of->is_dir;
361 file->is_file = of->is_file;
362 file->is_link = of->is_link;
363 file->is_exec = of->is_exec;
364 file->is_directio = of->is_directio;
377 file->accessed = now;
379 ngx_queue_insert_head(&cache->expire_queue, &file->queue);
381 ngx_log_debug5(NGX_LOG_DEBUG_CORE, pool->log, 0,
382 "cached open file: %s, fd:%d, c:%d, e:%d, u:%d",
383 file->name, file->fd, file->count, file->err, file->uses);
388 cln->handler = ngx_open_file_cleanup;
391 ofcln->cache = cache;
393 ofcln->min_uses = of->min_uses;
394 ofcln->log = pool->log;
405 ngx_rbtree_delete(&cache->rbtree, &file->node);
409 if (file->count == 0) {
411 if (file->fd != NGX_INVALID_FILE) {
412 if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
413 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
414 ngx_close_file_n " \"%s\" failed",
419 ngx_free(file->name);
427 if (of->fd != NGX_INVALID_FILE) {
428 if (ngx_close_file(of->fd) == NGX_FILE_ERROR) {
429 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
430 ngx_close_file_n " \"%s\" failed", name->data);
439 ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, ngx_log_t *log)
444 if (of->fd != NGX_INVALID_FILE) {
446 if (ngx_file_info(name, &fi) == -1) {
450 if (of->uniq == ngx_file_uniq(&fi)) {
454 } else if (of->test_dir) {
456 if (ngx_file_info(name, &fi) == -1) {
460 if (ngx_is_dir(&fi)) {
466 fd = ngx_open_file(name, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
469 fd = ngx_open_file(name, NGX_FILE_RDWR,
470 NGX_FILE_CREATE_OR_OPEN|NGX_FILE_APPEND,
471 NGX_FILE_DEFAULT_ACCESS);
474 if (fd == NGX_INVALID_FILE) {
478 if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
479 ngx_log_error(NGX_LOG_CRIT, log, ngx_errno,
480 ngx_fd_info_n " \"%s\" failed", name);
482 if (ngx_close_file(fd) == NGX_FILE_ERROR) {
483 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
484 ngx_close_file_n " \"%s\" failed", name);
487 of->fd = NGX_INVALID_FILE;
492 if (ngx_is_dir(&fi)) {
493 if (ngx_close_file(fd) == NGX_FILE_ERROR) {
494 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
495 ngx_close_file_n " \"%s\" failed", name);
498 of->fd = NGX_INVALID_FILE;
503 if (of->directio <= ngx_file_size(&fi)) {
504 if (ngx_directio_on(fd) == -1) {
505 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
506 ngx_directio_on_n " \"%s\" failed", name);
516 of->uniq = ngx_file_uniq(&fi);
517 of->mtime = ngx_file_mtime(&fi);
518 of->size = ngx_file_size(&fi);
519 of->is_dir = ngx_is_dir(&fi);
520 of->is_file = ngx_is_file(&fi);
521 of->is_link = ngx_is_link(&fi);
522 of->is_exec = ngx_is_exec(&fi);
528 of->fd = NGX_INVALID_FILE;
536 * we ignore any possible event setting error and
537 * fallback to usual periodic file retests
541 ngx_open_file_add_event(ngx_open_file_cache_t *cache,
542 ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log)
544 ngx_open_file_cache_event_t *fev;
546 if (!(ngx_event_flags & NGX_USE_VNODE_EVENT)
549 || of->fd == NGX_INVALID_FILE
550 || file->uses < of->min_uses)
557 file->event = ngx_calloc(sizeof(ngx_event_t), log);
558 if (file->event== NULL) {
562 fev = ngx_alloc(sizeof(ngx_open_file_cache_event_t), log);
564 ngx_free(file->event);
573 file->event->handler = ngx_open_file_cache_remove;
574 file->event->data = fev;
577 * although vnode event may be called while ngx_cycle->poll
578 * destruction, however, cleanup procedures are run before any
579 * memory freeing and events will be canceled.
582 file->event->log = ngx_cycle->log;
584 if (ngx_add_event(file->event, NGX_VNODE_EVENT, NGX_ONESHOT_EVENT)
587 ngx_free(file->event->data);
588 ngx_free(file->event);
594 * we do not set file->use_event here because there may be a race
595 * condition: a file may be deleted between opening the file and
596 * adding event, so we rely upon event notification only after
597 * one file revalidation on next file access
605 ngx_open_file_cleanup(void *data)
607 ngx_open_file_cache_cleanup_t *c = data;
611 ngx_close_cached_file(c->cache, c->file, c->min_uses, c->log);
613 /* drop one or two expired open files */
614 ngx_expire_old_cached_files(c->cache, 1, c->log);
619 ngx_close_cached_file(ngx_open_file_cache_t *cache,
620 ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log)
622 ngx_log_debug5(NGX_LOG_DEBUG_CORE, log, 0,
623 "close cached open file: %s, fd:%d, c:%d, u:%d, %d",
624 file->name, file->fd, file->count, file->uses, file->close);
628 file->accessed = ngx_time();
630 ngx_queue_remove(&file->queue);
632 ngx_queue_insert_head(&cache->expire_queue, &file->queue);
634 if (file->uses >= min_uses || file->count) {
639 ngx_open_file_del_event(file);
645 if (file->fd != NGX_INVALID_FILE) {
647 if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
648 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
649 ngx_close_file_n " \"%s\" failed", file->name);
652 file->fd = NGX_INVALID_FILE;
659 ngx_free(file->name);
665 ngx_open_file_del_event(ngx_cached_open_file_t *file)
667 if (file->event == NULL) {
671 (void) ngx_del_event(file->event, NGX_VNODE_EVENT,
672 file->count ? NGX_FLUSH_EVENT : NGX_CLOSE_EVENT);
674 ngx_free(file->event->data);
675 ngx_free(file->event);
682 ngx_expire_old_cached_files(ngx_open_file_cache_t *cache, ngx_uint_t n,
687 ngx_cached_open_file_t *file;
692 * n == 1 deletes one or two inactive files
693 * n == 0 deletes least recently used file by force
694 * and one or two inactive files
699 if (ngx_queue_empty(&cache->expire_queue)) {
703 q = ngx_queue_last(&cache->expire_queue);
705 file = ngx_queue_data(q, ngx_cached_open_file_t, queue);
707 if (n++ != 0 && now - file->accessed <= cache->inactive) {
713 ngx_rbtree_delete(&cache->rbtree, &file->node);
717 ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0,
718 "expire cached open file: %s", file->name);
720 if (!file->err && !file->is_dir) {
722 ngx_close_cached_file(cache, file, 0, log);
725 ngx_free(file->name);
733 ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
734 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
736 ngx_rbtree_node_t **p;
737 ngx_cached_open_file_t *file, *file_temp;
741 if (node->key < temp->key) {
745 } else if (node->key > temp->key) {
749 } else { /* node->key == temp->key */
751 file = (ngx_cached_open_file_t *) node;
752 file_temp = (ngx_cached_open_file_t *) temp;
754 p = (ngx_strcmp(file->name, file_temp->name) < 0)
755 ? &temp->left : &temp->right;
758 if (*p == sentinel) {
767 node->left = sentinel;
768 node->right = sentinel;
773 static ngx_cached_open_file_t *
774 ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name,
778 ngx_rbtree_node_t *node, *sentinel;
779 ngx_cached_open_file_t *file;
781 node = cache->rbtree.root;
782 sentinel = cache->rbtree.sentinel;
784 while (node != sentinel) {
786 if (hash < node->key) {
791 if (hash > node->key) {
796 /* hash == node->key */
799 file = (ngx_cached_open_file_t *) node;
801 rc = ngx_strcmp(name->data, file->name);
807 node = (rc < 0) ? node->left : node->right;
809 } while (node != sentinel && hash == node->key);
819 ngx_open_file_cache_remove(ngx_event_t *ev)
821 ngx_cached_open_file_t *file;
822 ngx_open_file_cache_event_t *fev;
827 ngx_queue_remove(&file->queue);
829 ngx_rbtree_delete(&fev->cache->rbtree, &file->node);
831 fev->cache->current--;
833 /* NGX_ONESHOT_EVENT was already deleted */
839 ngx_close_cached_file(fev->cache, file, 0, ev->log);
841 /* free memory only when fev->cache and fev->file are already not needed */