Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ngx_open_file_cache.c
Go to the documentation of this file.
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 /*
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 
21 #define NGX_MIN_READ_AHEAD (128 * 1024)
22 
23 
24 static void ngx_open_file_cache_cleanup(void *data);
25 #if (NGX_HAVE_OPENAT)
26 static ngx_fd_t ngx_openat_file_owner(ngx_fd_t at_fd, const u_char *name,
27  ngx_int_t mode, ngx_int_t create, ngx_int_t access, ngx_log_t *log);
28 #endif
29 static ngx_fd_t ngx_open_file_wrapper(ngx_str_t *name,
30  ngx_open_file_info_t *of, ngx_int_t mode, ngx_int_t create,
31  ngx_int_t access, ngx_log_t *log);
32 static ngx_int_t ngx_file_info_wrapper(ngx_str_t *name,
34 static ngx_int_t ngx_open_and_stat_file(ngx_str_t *name,
36 static void ngx_open_file_add_event(ngx_open_file_cache_t *cache,
38 static void ngx_open_file_cleanup(void *data);
39 static void ngx_close_cached_file(ngx_open_file_cache_t *cache,
40  ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log);
41 static void ngx_open_file_del_event(ngx_cached_open_file_t *file);
42 static void ngx_expire_old_cached_files(ngx_open_file_cache_t *cache,
43  ngx_uint_t n, ngx_log_t *log);
44 static void ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
47  ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name,
48  uint32_t hash);
49 static void ngx_open_file_cache_remove(ngx_event_t *ev);
50 
51 
53 ngx_open_file_cache_init(ngx_pool_t *pool, ngx_uint_t max, time_t inactive)
54 {
55  ngx_pool_cleanup_t *cln;
56  ngx_open_file_cache_t *cache;
57 
58  cache = ngx_palloc(pool, sizeof(ngx_open_file_cache_t));
59  if (cache == NULL) {
60  return NULL;
61  }
62 
63  ngx_rbtree_init(&cache->rbtree, &cache->sentinel,
64  ngx_open_file_cache_rbtree_insert_value);
65 
67 
68  cache->current = 0;
69  cache->max = max;
70  cache->inactive = inactive;
71 
72  cln = ngx_pool_cleanup_add(pool, 0);
73  if (cln == NULL) {
74  return NULL;
75  }
76 
77  cln->handler = ngx_open_file_cache_cleanup;
78  cln->data = cache;
79 
80  return cache;
81 }
82 
83 
84 static void
85 ngx_open_file_cache_cleanup(void *data)
86 {
87  ngx_open_file_cache_t *cache = data;
88 
89  ngx_queue_t *q;
91 
93  "open file cache cleanup");
94 
95  for ( ;; ) {
96 
97  if (ngx_queue_empty(&cache->expire_queue)) {
98  break;
99  }
100 
101  q = ngx_queue_last(&cache->expire_queue);
102 
103  file = ngx_queue_data(q, ngx_cached_open_file_t, queue);
104 
105  ngx_queue_remove(q);
106 
107  ngx_rbtree_delete(&cache->rbtree, &file->node);
108 
109  cache->current--;
110 
112  "delete cached open file: %s", file->name);
113 
114  if (!file->err && !file->is_dir) {
115  file->close = 1;
116  file->count = 0;
117  ngx_close_cached_file(cache, file, 0, ngx_cycle->log);
118 
119  } else {
120  ngx_free(file->name);
121  ngx_free(file);
122  }
123  }
124 
125  if (cache->current) {
127  "%d items still leave in open file cache",
128  cache->current);
129  }
130 
131  if (cache->rbtree.root != cache->rbtree.sentinel) {
133  "rbtree still is not empty in open file cache");
134 
135  }
136 }
137 
138 
139 ngx_int_t
141  ngx_open_file_info_t *of, ngx_pool_t *pool)
142 {
143  time_t now;
144  uint32_t hash;
145  ngx_int_t rc;
146  ngx_file_info_t fi;
147  ngx_pool_cleanup_t *cln;
151 
152  of->fd = NGX_INVALID_FILE;
153  of->err = 0;
154 
155  if (cache == NULL) {
156 
157  if (of->test_only) {
158 
159  if (ngx_file_info_wrapper(name, of, &fi, pool->log)
160  == NGX_FILE_ERROR)
161  {
162  return NGX_ERROR;
163  }
164 
165  of->uniq = ngx_file_uniq(&fi);
166  of->mtime = ngx_file_mtime(&fi);
167  of->size = ngx_file_size(&fi);
168  of->fs_size = ngx_file_fs_size(&fi);
169  of->is_dir = ngx_is_dir(&fi);
170  of->is_file = ngx_is_file(&fi);
171  of->is_link = ngx_is_link(&fi);
172  of->is_exec = ngx_is_exec(&fi);
173 
174  return NGX_OK;
175  }
176 
177  cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t));
178  if (cln == NULL) {
179  return NGX_ERROR;
180  }
181 
182  rc = ngx_open_and_stat_file(name, of, pool->log);
183 
184  if (rc == NGX_OK && !of->is_dir) {
186  clnf = cln->data;
187 
188  clnf->fd = of->fd;
189  clnf->name = name->data;
190  clnf->log = pool->log;
191  }
192 
193  return rc;
194  }
195 
197  if (cln == NULL) {
198  return NGX_ERROR;
199  }
200 
201  now = ngx_time();
202 
203  hash = ngx_crc32_long(name->data, name->len);
204 
205  file = ngx_open_file_lookup(cache, name, hash);
206 
207  if (file) {
208 
209  file->uses++;
210 
211  ngx_queue_remove(&file->queue);
212 
213  if (file->fd == NGX_INVALID_FILE && file->err == 0 && !file->is_dir) {
214 
215  /* file was not used often enough to keep open */
216 
217  rc = ngx_open_and_stat_file(name, of, pool->log);
218 
219  if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
220  goto failed;
221  }
222 
223  goto add_event;
224  }
225 
226  if (file->use_event
227  || (file->event == NULL
228  && (of->uniq == 0 || of->uniq == file->uniq)
229  && now - file->created < of->valid
230 #if (NGX_HAVE_OPENAT)
231  && of->disable_symlinks == file->disable_symlinks
232  && of->disable_symlinks_from == file->disable_symlinks_from
233 #endif
234  ))
235  {
236  if (file->err == 0) {
237 
238  of->fd = file->fd;
239  of->uniq = file->uniq;
240  of->mtime = file->mtime;
241  of->size = file->size;
242 
243  of->is_dir = file->is_dir;
244  of->is_file = file->is_file;
245  of->is_link = file->is_link;
246  of->is_exec = file->is_exec;
247  of->is_directio = file->is_directio;
248 
249  if (!file->is_dir) {
250  file->count++;
251  ngx_open_file_add_event(cache, file, of, pool->log);
252  }
253 
254  } else {
255  of->err = file->err;
256 #if (NGX_HAVE_OPENAT)
257  of->failed = file->disable_symlinks ? ngx_openat_file_n
258  : ngx_open_file_n;
259 #else
260  of->failed = ngx_open_file_n;
261 #endif
262  }
263 
264  goto found;
265  }
266 
268  "retest open file: %s, fd:%d, c:%d, e:%d",
269  file->name, file->fd, file->count, file->err);
270 
271  if (file->is_dir) {
272 
273  /*
274  * chances that directory became file are very small
275  * so test_dir flag allows to use a single syscall
276  * in ngx_file_info() instead of three syscalls
277  */
278 
279  of->test_dir = 1;
280  }
281 
282  of->fd = file->fd;
283  of->uniq = file->uniq;
284 
285  rc = ngx_open_and_stat_file(name, of, pool->log);
286 
287  if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
288  goto failed;
289  }
290 
291  if (of->is_dir) {
292 
293  if (file->is_dir || file->err) {
294  goto update;
295  }
296 
297  /* file became directory */
298 
299  } else if (of->err == 0) { /* file */
300 
301  if (file->is_dir || file->err) {
302  goto add_event;
303  }
304 
305  if (of->uniq == file->uniq) {
306 
307  if (file->event) {
308  file->use_event = 1;
309  }
310 
311  of->is_directio = file->is_directio;
312 
313  goto update;
314  }
315 
316  /* file was changed */
317 
318  } else { /* error to cache */
319 
320  if (file->err || file->is_dir) {
321  goto update;
322  }
323 
324  /* file was removed, etc. */
325  }
326 
327  if (file->count == 0) {
328 
329  ngx_open_file_del_event(file);
330 
331  if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
333  ngx_close_file_n " \"%V\" failed", name);
334  }
335 
336  goto add_event;
337  }
338 
339  ngx_rbtree_delete(&cache->rbtree, &file->node);
340 
341  cache->current--;
342 
343  file->close = 1;
344 
345  goto create;
346  }
347 
348  /* not found */
349 
350  rc = ngx_open_and_stat_file(name, of, pool->log);
351 
352  if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
353  goto failed;
354  }
355 
356 create:
357 
358  if (cache->current >= cache->max) {
359  ngx_expire_old_cached_files(cache, 0, pool->log);
360  }
361 
362  file = ngx_alloc(sizeof(ngx_cached_open_file_t), pool->log);
363 
364  if (file == NULL) {
365  goto failed;
366  }
367 
368  file->name = ngx_alloc(name->len + 1, pool->log);
369 
370  if (file->name == NULL) {
371  ngx_free(file);
372  file = NULL;
373  goto failed;
374  }
375 
376  ngx_cpystrn(file->name, name->data, name->len + 1);
377 
378  file->node.key = hash;
379 
380  ngx_rbtree_insert(&cache->rbtree, &file->node);
381 
382  cache->current++;
383 
384  file->uses = 1;
385  file->count = 0;
386  file->use_event = 0;
387  file->event = NULL;
388 
389 add_event:
390 
391  ngx_open_file_add_event(cache, file, of, pool->log);
392 
393 update:
394 
395  file->fd = of->fd;
396  file->err = of->err;
397 #if (NGX_HAVE_OPENAT)
398  file->disable_symlinks = of->disable_symlinks;
399  file->disable_symlinks_from = of->disable_symlinks_from;
400 #endif
401 
402  if (of->err == 0) {
403  file->uniq = of->uniq;
404  file->mtime = of->mtime;
405  file->size = of->size;
406 
407  file->close = 0;
408 
409  file->is_dir = of->is_dir;
410  file->is_file = of->is_file;
411  file->is_link = of->is_link;
412  file->is_exec = of->is_exec;
413  file->is_directio = of->is_directio;
414 
415  if (!of->is_dir) {
416  file->count++;
417  }
418  }
419 
420  file->created = now;
421 
422 found:
423 
424  file->accessed = now;
425 
426  ngx_queue_insert_head(&cache->expire_queue, &file->queue);
427 
429  "cached open file: %s, fd:%d, c:%d, e:%d, u:%d",
430  file->name, file->fd, file->count, file->err, file->uses);
431 
432  if (of->err == 0) {
433 
434  if (!of->is_dir) {
435  cln->handler = ngx_open_file_cleanup;
436  ofcln = cln->data;
437 
438  ofcln->cache = cache;
439  ofcln->file = file;
440  ofcln->min_uses = of->min_uses;
441  ofcln->log = pool->log;
442  }
443 
444  return NGX_OK;
445  }
446 
447  return NGX_ERROR;
448 
449 failed:
450 
451  if (file) {
452  ngx_rbtree_delete(&cache->rbtree, &file->node);
453 
454  cache->current--;
455 
456  if (file->count == 0) {
457 
458  if (file->fd != NGX_INVALID_FILE) {
459  if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
461  ngx_close_file_n " \"%s\" failed",
462  file->name);
463  }
464  }
465 
466  ngx_free(file->name);
467  ngx_free(file);
468 
469  } else {
470  file->close = 1;
471  }
472  }
473 
474  if (of->fd != NGX_INVALID_FILE) {
475  if (ngx_close_file(of->fd) == NGX_FILE_ERROR) {
477  ngx_close_file_n " \"%V\" failed", name);
478  }
479  }
480 
481  return NGX_ERROR;
482 }
483 
484 
485 #if (NGX_HAVE_OPENAT)
486 
487 static ngx_fd_t
488 ngx_openat_file_owner(ngx_fd_t at_fd, const u_char *name,
489  ngx_int_t mode, ngx_int_t create, ngx_int_t access, ngx_log_t *log)
490 {
491  ngx_fd_t fd;
492  ngx_err_t err;
493  ngx_file_info_t fi, atfi;
494 
495  /*
496  * To allow symlinks with the same owner, use openat() (followed
497  * by fstat()) and fstatat(AT_SYMLINK_NOFOLLOW), and then compare
498  * uids between fstat() and fstatat().
499  *
500  * As there is a race between openat() and fstatat() we don't
501  * know if openat() in fact opened symlink or not. Therefore,
502  * we have to compare uids even if fstatat() reports the opened
503  * component isn't a symlink (as we don't know whether it was
504  * symlink during openat() or not).
505  */
506 
507  fd = ngx_openat_file(at_fd, name, mode, create, access);
508 
509  if (fd == NGX_INVALID_FILE) {
510  return NGX_INVALID_FILE;
511  }
512 
513  if (ngx_file_at_info(at_fd, name, &atfi, AT_SYMLINK_NOFOLLOW)
514  == NGX_FILE_ERROR)
515  {
516  err = ngx_errno;
517  goto failed;
518  }
519 
520  if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
521  err = ngx_errno;
522  goto failed;
523  }
524 
525  if (fi.st_uid != atfi.st_uid) {
526  err = NGX_ELOOP;
527  goto failed;
528  }
529 
530  return fd;
531 
532 failed:
533 
534  if (ngx_close_file(fd) == NGX_FILE_ERROR) {
536  ngx_close_file_n " \"%V\" failed", name);
537  }
538 
539  ngx_set_errno(err);
540 
541  return NGX_INVALID_FILE;
542 }
543 
544 #endif
545 
546 
547 static ngx_fd_t
548 ngx_open_file_wrapper(ngx_str_t *name, ngx_open_file_info_t *of,
549  ngx_int_t mode, ngx_int_t create, ngx_int_t access, ngx_log_t *log)
550 {
551  ngx_fd_t fd;
552 
553 #if !(NGX_HAVE_OPENAT)
554 
555  fd = ngx_open_file(name->data, mode, create, access);
556 
557  if (fd == NGX_INVALID_FILE) {
558  of->err = ngx_errno;
559  of->failed = ngx_open_file_n;
560  return NGX_INVALID_FILE;
561  }
562 
563  return fd;
564 
565 #else
566 
567  u_char *p, *cp, *end;
568  ngx_fd_t at_fd;
569  ngx_str_t at_name;
570 
571  if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) {
572  fd = ngx_open_file(name->data, mode, create, access);
573 
574  if (fd == NGX_INVALID_FILE) {
575  of->err = ngx_errno;
576  of->failed = ngx_open_file_n;
577  return NGX_INVALID_FILE;
578  }
579 
580  return fd;
581  }
582 
583  p = name->data;
584  end = p + name->len;
585 
586  at_name = *name;
587 
588  if (of->disable_symlinks_from) {
589 
590  cp = p + of->disable_symlinks_from;
591 
592  *cp = '\0';
593 
594  at_fd = ngx_open_file(p, NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,
595  NGX_FILE_OPEN, 0);
596 
597  *cp = '/';
598 
599  if (at_fd == NGX_INVALID_FILE) {
600  of->err = ngx_errno;
601  of->failed = ngx_open_file_n;
602  return NGX_INVALID_FILE;
603  }
604 
605  at_name.len = of->disable_symlinks_from;
606  p = cp + 1;
607 
608  } else if (*p == '/') {
609 
610  at_fd = ngx_open_file("/",
611  NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,
612  NGX_FILE_OPEN, 0);
613 
614  if (at_fd == NGX_INVALID_FILE) {
615  of->err = ngx_errno;
616  of->failed = ngx_openat_file_n;
617  return NGX_INVALID_FILE;
618  }
619 
620  at_name.len = 1;
621  p++;
622 
623  } else {
624  at_fd = NGX_AT_FDCWD;
625  }
626 
627  for ( ;; ) {
628  cp = ngx_strlchr(p, end, '/');
629  if (cp == NULL) {
630  break;
631  }
632 
633  if (cp == p) {
634  p++;
635  continue;
636  }
637 
638  *cp = '\0';
639 
640  if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER) {
641  fd = ngx_openat_file_owner(at_fd, p,
642  NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,
643  NGX_FILE_OPEN, 0, log);
644 
645  } else {
646  fd = ngx_openat_file(at_fd, p,
647  NGX_FILE_SEARCH|NGX_FILE_NONBLOCK|NGX_FILE_NOFOLLOW,
648  NGX_FILE_OPEN, 0);
649  }
650 
651  *cp = '/';
652 
653  if (fd == NGX_INVALID_FILE) {
654  of->err = ngx_errno;
655  of->failed = ngx_openat_file_n;
656  goto failed;
657  }
658 
659  if (at_fd != NGX_AT_FDCWD && ngx_close_file(at_fd) == NGX_FILE_ERROR) {
661  ngx_close_file_n " \"%V\" failed", &at_name);
662  }
663 
664  p = cp + 1;
665  at_fd = fd;
666  at_name.len = cp - at_name.data;
667  }
668 
669  if (p == end) {
670 
671  /*
672  * If pathname ends with a trailing slash, assume the last path
673  * component is a directory and reopen it with requested flags;
674  * if not, fail with ENOTDIR as per POSIX.
675  *
676  * We cannot rely on O_DIRECTORY in the loop above to check
677  * that the last path component is a directory because
678  * O_DIRECTORY doesn't work on FreeBSD 8. Fortunately, by
679  * reopening a directory, we don't depend on it at all.
680  */
681 
682  fd = ngx_openat_file(at_fd, ".", mode, create, access);
683  goto done;
684  }
685 
686  if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER
688  {
689  fd = ngx_openat_file_owner(at_fd, p, mode, create, access, log);
690 
691  } else {
692  fd = ngx_openat_file(at_fd, p, mode|NGX_FILE_NOFOLLOW, create, access);
693  }
694 
695 done:
696 
697  if (fd == NGX_INVALID_FILE) {
698  of->err = ngx_errno;
699  of->failed = ngx_openat_file_n;
700  }
701 
702 failed:
703 
704  if (at_fd != NGX_AT_FDCWD && ngx_close_file(at_fd) == NGX_FILE_ERROR) {
706  ngx_close_file_n " \"%V\" failed", &at_name);
707  }
708 
709  return fd;
710 #endif
711 }
712 
713 
714 static ngx_int_t
715 ngx_file_info_wrapper(ngx_str_t *name, ngx_open_file_info_t *of,
716  ngx_file_info_t *fi, ngx_log_t *log)
717 {
718  ngx_int_t rc;
719 
720 #if !(NGX_HAVE_OPENAT)
721 
722  rc = ngx_file_info(name->data, fi);
723 
724  if (rc == NGX_FILE_ERROR) {
725  of->err = ngx_errno;
726  of->failed = ngx_file_info_n;
727  return NGX_FILE_ERROR;
728  }
729 
730  return rc;
731 
732 #else
733 
734  ngx_fd_t fd;
735 
736  if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) {
737 
738  rc = ngx_file_info(name->data, fi);
739 
740  if (rc == NGX_FILE_ERROR) {
741  of->err = ngx_errno;
742  of->failed = ngx_file_info_n;
743  return NGX_FILE_ERROR;
744  }
745 
746  return rc;
747  }
748 
749  fd = ngx_open_file_wrapper(name, of, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,
750  NGX_FILE_OPEN, 0, log);
751 
752  if (fd == NGX_INVALID_FILE) {
753  return NGX_FILE_ERROR;
754  }
755 
756  rc = ngx_fd_info(fd, fi);
757 
758  if (rc == NGX_FILE_ERROR) {
759  of->err = ngx_errno;
760  of->failed = ngx_fd_info_n;
761  }
762 
763  if (ngx_close_file(fd) == NGX_FILE_ERROR) {
765  ngx_close_file_n " \"%V\" failed", name);
766  }
767 
768  return rc;
769 #endif
770 }
771 
772 
773 static ngx_int_t
774 ngx_open_and_stat_file(ngx_str_t *name, ngx_open_file_info_t *of,
775  ngx_log_t *log)
776 {
777  ngx_fd_t fd;
778  ngx_file_info_t fi;
779 
780  if (of->fd != NGX_INVALID_FILE) {
781 
782  if (ngx_file_info_wrapper(name, of, &fi, log) == NGX_FILE_ERROR) {
783  of->fd = NGX_INVALID_FILE;
784  return NGX_ERROR;
785  }
786 
787  if (of->uniq == ngx_file_uniq(&fi)) {
788  goto done;
789  }
790 
791  } else if (of->test_dir) {
792 
793  if (ngx_file_info_wrapper(name, of, &fi, log) == NGX_FILE_ERROR) {
794  of->fd = NGX_INVALID_FILE;
795  return NGX_ERROR;
796  }
797 
798  if (ngx_is_dir(&fi)) {
799  goto done;
800  }
801  }
802 
803  if (!of->log) {
804 
805  /*
806  * Use non-blocking open() not to hang on FIFO files, etc.
807  * This flag has no effect on a regular files.
808  */
809 
810  fd = ngx_open_file_wrapper(name, of, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,
811  NGX_FILE_OPEN, 0, log);
812 
813  } else {
814  fd = ngx_open_file_wrapper(name, of, NGX_FILE_APPEND,
817  }
818 
819  if (fd == NGX_INVALID_FILE) {
820  of->fd = NGX_INVALID_FILE;
821  return NGX_ERROR;
822  }
823 
824  if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
826  ngx_fd_info_n " \"%V\" failed", name);
827 
828  if (ngx_close_file(fd) == NGX_FILE_ERROR) {
830  ngx_close_file_n " \"%V\" failed", name);
831  }
832 
833  of->fd = NGX_INVALID_FILE;
834 
835  return NGX_ERROR;
836  }
837 
838  if (ngx_is_dir(&fi)) {
839  if (ngx_close_file(fd) == NGX_FILE_ERROR) {
841  ngx_close_file_n " \"%V\" failed", name);
842  }
843 
844  of->fd = NGX_INVALID_FILE;
845 
846  } else {
847  of->fd = fd;
848 
849  if (of->read_ahead && ngx_file_size(&fi) > NGX_MIN_READ_AHEAD) {
850  if (ngx_read_ahead(fd, of->read_ahead) == NGX_ERROR) {
852  ngx_read_ahead_n " \"%V\" failed", name);
853  }
854  }
855 
856  if (of->directio <= ngx_file_size(&fi)) {
857  if (ngx_directio_on(fd) == NGX_FILE_ERROR) {
859  ngx_directio_on_n " \"%V\" failed", name);
860 
861  } else {
862  of->is_directio = 1;
863  }
864  }
865  }
866 
867 done:
868 
869  of->uniq = ngx_file_uniq(&fi);
870  of->mtime = ngx_file_mtime(&fi);
871  of->size = ngx_file_size(&fi);
872  of->fs_size = ngx_file_fs_size(&fi);
873  of->is_dir = ngx_is_dir(&fi);
874  of->is_file = ngx_is_file(&fi);
875  of->is_link = ngx_is_link(&fi);
876  of->is_exec = ngx_is_exec(&fi);
877 
878  return NGX_OK;
879 }
880 
881 
882 /*
883  * we ignore any possible event setting error and
884  * fallback to usual periodic file retests
885  */
886 
887 static void
888 ngx_open_file_add_event(ngx_open_file_cache_t *cache,
890 {
892 
894  || !of->events
895  || file->event
896  || of->fd == NGX_INVALID_FILE
897  || file->uses < of->min_uses)
898  {
899  return;
900  }
901 
902  file->use_event = 0;
903 
904  file->event = ngx_calloc(sizeof(ngx_event_t), log);
905  if (file->event== NULL) {
906  return;
907  }
908 
909  fev = ngx_alloc(sizeof(ngx_open_file_cache_event_t), log);
910  if (fev == NULL) {
911  ngx_free(file->event);
912  file->event = NULL;
913  return;
914  }
915 
916  fev->fd = of->fd;
917  fev->file = file;
918  fev->cache = cache;
919 
920  file->event->handler = ngx_open_file_cache_remove;
921  file->event->data = fev;
922 
923  /*
924  * although vnode event may be called while ngx_cycle->poll
925  * destruction, however, cleanup procedures are run before any
926  * memory freeing and events will be canceled.
927  */
928 
929  file->event->log = ngx_cycle->log;
930 
932  != NGX_OK)
933  {
934  ngx_free(file->event->data);
935  ngx_free(file->event);
936  file->event = NULL;
937  return;
938  }
939 
940  /*
941  * we do not set file->use_event here because there may be a race
942  * condition: a file may be deleted between opening the file and
943  * adding event, so we rely upon event notification only after
944  * one file revalidation on next file access
945  */
946 
947  return;
948 }
949 
950 
951 static void
952 ngx_open_file_cleanup(void *data)
953 {
955 
956  c->file->count--;
957 
958  ngx_close_cached_file(c->cache, c->file, c->min_uses, c->log);
959 
960  /* drop one or two expired open files */
961  ngx_expire_old_cached_files(c->cache, 1, c->log);
962 }
963 
964 
965 static void
966 ngx_close_cached_file(ngx_open_file_cache_t *cache,
967  ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log)
968 {
970  "close cached open file: %s, fd:%d, c:%d, u:%d, %d",
971  file->name, file->fd, file->count, file->uses, file->close);
972 
973  if (!file->close) {
974 
975  file->accessed = ngx_time();
976 
977  ngx_queue_remove(&file->queue);
978 
979  ngx_queue_insert_head(&cache->expire_queue, &file->queue);
980 
981  if (file->uses >= min_uses || file->count) {
982  return;
983  }
984  }
985 
986  ngx_open_file_del_event(file);
987 
988  if (file->count) {
989  return;
990  }
991 
992  if (file->fd != NGX_INVALID_FILE) {
993 
994  if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
996  ngx_close_file_n " \"%s\" failed", file->name);
997  }
998 
999  file->fd = NGX_INVALID_FILE;
1000  }
1001 
1002  if (!file->close) {
1003  return;
1004  }
1005 
1006  ngx_free(file->name);
1007  ngx_free(file);
1008 }
1009 
1010 
1011 static void
1012 ngx_open_file_del_event(ngx_cached_open_file_t *file)
1013 {
1014  if (file->event == NULL) {
1015  return;
1016  }
1017 
1018  (void) ngx_del_event(file->event, NGX_VNODE_EVENT,
1020 
1021  ngx_free(file->event->data);
1022  ngx_free(file->event);
1023  file->event = NULL;
1024  file->use_event = 0;
1025 }
1026 
1027 
1028 static void
1029 ngx_expire_old_cached_files(ngx_open_file_cache_t *cache, ngx_uint_t n,
1030  ngx_log_t *log)
1031 {
1032  time_t now;
1033  ngx_queue_t *q;
1034  ngx_cached_open_file_t *file;
1035 
1036  now = ngx_time();
1037 
1038  /*
1039  * n == 1 deletes one or two inactive files
1040  * n == 0 deletes least recently used file by force
1041  * and one or two inactive files
1042  */
1043 
1044  while (n < 3) {
1045 
1046  if (ngx_queue_empty(&cache->expire_queue)) {
1047  return;
1048  }
1049 
1050  q = ngx_queue_last(&cache->expire_queue);
1051 
1052  file = ngx_queue_data(q, ngx_cached_open_file_t, queue);
1053 
1054  if (n++ != 0 && now - file->accessed <= cache->inactive) {
1055  return;
1056  }
1057 
1058  ngx_queue_remove(q);
1059 
1060  ngx_rbtree_delete(&cache->rbtree, &file->node);
1061 
1062  cache->current--;
1063 
1065  "expire cached open file: %s", file->name);
1066 
1067  if (!file->err && !file->is_dir) {
1068  file->close = 1;
1069  ngx_close_cached_file(cache, file, 0, log);
1070 
1071  } else {
1072  ngx_free(file->name);
1073  ngx_free(file);
1074  }
1075  }
1076 }
1077 
1078 
1079 static void
1080 ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
1082 {
1083  ngx_rbtree_node_t **p;
1084  ngx_cached_open_file_t *file, *file_temp;
1085 
1086  for ( ;; ) {
1087 
1088  if (node->key < temp->key) {
1089 
1090  p = &temp->left;
1091 
1092  } else if (node->key > temp->key) {
1093 
1094  p = &temp->right;
1095 
1096  } else { /* node->key == temp->key */
1097 
1098  file = (ngx_cached_open_file_t *) node;
1099  file_temp = (ngx_cached_open_file_t *) temp;
1100 
1101  p = (ngx_strcmp(file->name, file_temp->name) < 0)
1102  ? &temp->left : &temp->right;
1103  }
1104 
1105  if (*p == sentinel) {
1106  break;
1107  }
1108 
1109  temp = *p;
1110  }
1111 
1112  *p = node;
1113  node->parent = temp;
1114  node->left = sentinel;
1115  node->right = sentinel;
1116  ngx_rbt_red(node);
1117 }
1118 
1119 
1120 static ngx_cached_open_file_t *
1121 ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name,
1122  uint32_t hash)
1123 {
1124  ngx_int_t rc;
1125  ngx_rbtree_node_t *node, *sentinel;
1126  ngx_cached_open_file_t *file;
1127 
1128  node = cache->rbtree.root;
1129  sentinel = cache->rbtree.sentinel;
1130 
1131  while (node != sentinel) {
1132 
1133  if (hash < node->key) {
1134  node = node->left;
1135  continue;
1136  }
1137 
1138  if (hash > node->key) {
1139  node = node->right;
1140  continue;
1141  }
1142 
1143  /* hash == node->key */
1144 
1145  file = (ngx_cached_open_file_t *) node;
1146 
1147  rc = ngx_strcmp(name->data, file->name);
1148 
1149  if (rc == 0) {
1150  return file;
1151  }
1152 
1153  node = (rc < 0) ? node->left : node->right;
1154  }
1155 
1156  return NULL;
1157 }
1158 
1159 
1160 static void
1161 ngx_open_file_cache_remove(ngx_event_t *ev)
1162 {
1163  ngx_cached_open_file_t *file;
1165 
1166  fev = ev->data;
1167  file = fev->file;
1168 
1169  ngx_queue_remove(&file->queue);
1170 
1171  ngx_rbtree_delete(&fev->cache->rbtree, &file->node);
1172 
1173  fev->cache->current--;
1174 
1175  /* NGX_ONESHOT_EVENT was already deleted */
1176  file->event = NULL;
1177  file->use_event = 0;
1178 
1179  file->close = 1;
1180 
1181  ngx_close_cached_file(fev->cache, file, 0, ev->log);
1182 
1183  /* free memory only when fev->cache and fev->file are already not needed */
1184 
1185  ngx_free(ev->data);
1186  ngx_free(ev);
1187 }