src/core/ngx_open_file_cache.c - nginx-1.7.10

Functions defined

Macros defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_event.h>


  8. /*
  9. * open file cache caches
  10. *    open file handles with stat() info;
  11. *    directories stat() info;
  12. *    files and directories errors: not found, access denied, etc.
  13. */


  14. #define NGX_MIN_READ_AHEAD  (128 * 1024)


  15. static void ngx_open_file_cache_cleanup(void *data);
  16. #if (NGX_HAVE_OPENAT)
  17. static ngx_fd_t ngx_openat_file_owner(ngx_fd_t at_fd, const u_char *name,
  18.     ngx_int_t mode, ngx_int_t create, ngx_int_t access, ngx_log_t *log);
  19. #if (NGX_HAVE_O_PATH)
  20. static ngx_int_t ngx_file_o_path_info(ngx_fd_t fd, ngx_file_info_t *fi,
  21.     ngx_log_t *log);
  22. #endif
  23. #endif
  24. static ngx_fd_t ngx_open_file_wrapper(ngx_str_t *name,
  25.     ngx_open_file_info_t *of, ngx_int_t mode, ngx_int_t create,
  26.     ngx_int_t access, ngx_log_t *log);
  27. static ngx_int_t ngx_file_info_wrapper(ngx_str_t *name,
  28.     ngx_open_file_info_t *of, ngx_file_info_t *fi, ngx_log_t *log);
  29. static ngx_int_t ngx_open_and_stat_file(ngx_str_t *name,
  30.     ngx_open_file_info_t *of, ngx_log_t *log);
  31. static void ngx_open_file_add_event(ngx_open_file_cache_t *cache,
  32.     ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log);
  33. static void ngx_open_file_cleanup(void *data);
  34. static void ngx_close_cached_file(ngx_open_file_cache_t *cache,
  35.     ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log);
  36. static void ngx_open_file_del_event(ngx_cached_open_file_t *file);
  37. static void ngx_expire_old_cached_files(ngx_open_file_cache_t *cache,
  38.     ngx_uint_t n, ngx_log_t *log);
  39. static void ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
  40.     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
  41. static ngx_cached_open_file_t *
  42.     ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name,
  43.     uint32_t hash);
  44. static void ngx_open_file_cache_remove(ngx_event_t *ev);


  45. ngx_open_file_cache_t *
  46. ngx_open_file_cache_init(ngx_pool_t *pool, ngx_uint_t max, time_t inactive)
  47. {
  48.     ngx_pool_cleanup_t     *cln;
  49.     ngx_open_file_cache_t  *cache;

  50.     cache = ngx_palloc(pool, sizeof(ngx_open_file_cache_t));
  51.     if (cache == NULL) {
  52.         return NULL;
  53.     }

  54.     ngx_rbtree_init(&cache->rbtree, &cache->sentinel,
  55.                     ngx_open_file_cache_rbtree_insert_value);

  56.     ngx_queue_init(&cache->expire_queue);

  57.     cache->current = 0;
  58.     cache->max = max;
  59.     cache->inactive = inactive;

  60.     cln = ngx_pool_cleanup_add(pool, 0);
  61.     if (cln == NULL) {
  62.         return NULL;
  63.     }

  64.     cln->handler = ngx_open_file_cache_cleanup;
  65.     cln->data = cache;

  66.     return cache;
  67. }


  68. static void
  69. ngx_open_file_cache_cleanup(void *data)
  70. {
  71.     ngx_open_file_cache_t  *cache = data;

  72.     ngx_queue_t             *q;
  73.     ngx_cached_open_file_t  *file;

  74.     ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
  75.                    "open file cache cleanup");

  76.     for ( ;; ) {

  77.         if (ngx_queue_empty(&cache->expire_queue)) {
  78.             break;
  79.         }

  80.         q = ngx_queue_last(&cache->expire_queue);

  81.         file = ngx_queue_data(q, ngx_cached_open_file_t, queue);

  82.         ngx_queue_remove(q);

  83.         ngx_rbtree_delete(&cache->rbtree, &file->node);

  84.         cache->current--;

  85.         ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
  86.                        "delete cached open file: %s", file->name);

  87.         if (!file->err && !file->is_dir) {
  88.             file->close = 1;
  89.             file->count = 0;
  90.             ngx_close_cached_file(cache, file, 0, ngx_cycle->log);

  91.         } else {
  92.             ngx_free(file->name);
  93.             ngx_free(file);
  94.         }
  95.     }

  96.     if (cache->current) {
  97.         ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
  98.                       "%ui items still leave in open file cache",
  99.                       cache->current);
  100.     }

  101.     if (cache->rbtree.root != cache->rbtree.sentinel) {
  102.         ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
  103.                       "rbtree still is not empty in open file cache");

  104.     }
  105. }


  106. ngx_int_t
  107. ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name,
  108.     ngx_open_file_info_t *of, ngx_pool_t *pool)
  109. {
  110.     time_t                          now;
  111.     uint32_t                        hash;
  112.     ngx_int_t                       rc;
  113.     ngx_file_info_t                 fi;
  114.     ngx_pool_cleanup_t             *cln;
  115.     ngx_cached_open_file_t         *file;
  116.     ngx_pool_cleanup_file_t        *clnf;
  117.     ngx_open_file_cache_cleanup_t  *ofcln;

  118.     of->fd = NGX_INVALID_FILE;
  119.     of->err = 0;

  120.     if (cache == NULL) {

  121.         if (of->test_only) {

  122.             if (ngx_file_info_wrapper(name, of, &fi, pool->log)
  123.                 == NGX_FILE_ERROR)
  124.             {
  125.                 return NGX_ERROR;
  126.             }

  127.             of->uniq = ngx_file_uniq(&fi);
  128.             of->mtime = ngx_file_mtime(&fi);
  129.             of->size = ngx_file_size(&fi);
  130.             of->fs_size = ngx_file_fs_size(&fi);
  131.             of->is_dir = ngx_is_dir(&fi);
  132.             of->is_file = ngx_is_file(&fi);
  133.             of->is_link = ngx_is_link(&fi);
  134.             of->is_exec = ngx_is_exec(&fi);

  135.             return NGX_OK;
  136.         }

  137.         cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t));
  138.         if (cln == NULL) {
  139.             return NGX_ERROR;
  140.         }

  141.         rc = ngx_open_and_stat_file(name, of, pool->log);

  142.         if (rc == NGX_OK && !of->is_dir) {
  143.             cln->handler = ngx_pool_cleanup_file;
  144.             clnf = cln->data;

  145.             clnf->fd = of->fd;
  146.             clnf->name = name->data;
  147.             clnf->log = pool->log;
  148.         }

  149.         return rc;
  150.     }

  151.     cln = ngx_pool_cleanup_add(pool, sizeof(ngx_open_file_cache_cleanup_t));
  152.     if (cln == NULL) {
  153.         return NGX_ERROR;
  154.     }

  155.     now = ngx_time();

  156.     hash = ngx_crc32_long(name->data, name->len);

  157.     file = ngx_open_file_lookup(cache, name, hash);

  158.     if (file) {

  159.         file->uses++;

  160.         ngx_queue_remove(&file->queue);

  161.         if (file->fd == NGX_INVALID_FILE && file->err == 0 && !file->is_dir) {

  162.             /* file was not used often enough to keep open */

  163.             rc = ngx_open_and_stat_file(name, of, pool->log);

  164.             if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
  165.                 goto failed;
  166.             }

  167.             goto add_event;
  168.         }

  169.         if (file->use_event
  170.             || (file->event == NULL
  171.                 && (of->uniq == 0 || of->uniq == file->uniq)
  172.                 && now - file->created < of->valid
  173. #if (NGX_HAVE_OPENAT)
  174.                 && of->disable_symlinks == file->disable_symlinks
  175.                 && of->disable_symlinks_from == file->disable_symlinks_from
  176. #endif
  177.             ))
  178.         {
  179.             if (file->err == 0) {

  180.                 of->fd = file->fd;
  181.                 of->uniq = file->uniq;
  182.                 of->mtime = file->mtime;
  183.                 of->size = file->size;

  184.                 of->is_dir = file->is_dir;
  185.                 of->is_file = file->is_file;
  186.                 of->is_link = file->is_link;
  187.                 of->is_exec = file->is_exec;
  188.                 of->is_directio = file->is_directio;

  189.                 if (!file->is_dir) {
  190.                     file->count++;
  191.                     ngx_open_file_add_event(cache, file, of, pool->log);
  192.                 }

  193.             } else {
  194.                 of->err = file->err;
  195. #if (NGX_HAVE_OPENAT)
  196.                 of->failed = file->disable_symlinks ? ngx_openat_file_n
  197.                                                     : ngx_open_file_n;
  198. #else
  199.                 of->failed = ngx_open_file_n;
  200. #endif
  201.             }

  202.             goto found;
  203.         }

  204.         ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0,
  205.                        "retest open file: %s, fd:%d, c:%d, e:%d",
  206.                        file->name, file->fd, file->count, file->err);

  207.         if (file->is_dir) {

  208.             /*
  209.              * chances that directory became file are very small
  210.              * so test_dir flag allows to use a single syscall
  211.              * in ngx_file_info() instead of three syscalls
  212.              */

  213.             of->test_dir = 1;
  214.         }

  215.         of->fd = file->fd;
  216.         of->uniq = file->uniq;

  217.         rc = ngx_open_and_stat_file(name, of, pool->log);

  218.         if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
  219.             goto failed;
  220.         }

  221.         if (of->is_dir) {

  222.             if (file->is_dir || file->err) {
  223.                 goto update;
  224.             }

  225.             /* file became directory */

  226.         } else if (of->err == 0) {  /* file */

  227.             if (file->is_dir || file->err) {
  228.                 goto add_event;
  229.             }

  230.             if (of->uniq == file->uniq) {

  231.                 if (file->event) {
  232.                     file->use_event = 1;
  233.                 }

  234.                 of->is_directio = file->is_directio;

  235.                 goto update;
  236.             }

  237.             /* file was changed */

  238.         } else { /* error to cache */

  239.             if (file->err || file->is_dir) {
  240.                 goto update;
  241.             }

  242.             /* file was removed, etc. */
  243.         }

  244.         if (file->count == 0) {

  245.             ngx_open_file_del_event(file);

  246.             if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
  247.                 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
  248.                               ngx_close_file_n " \"%V\" failed", name);
  249.             }

  250.             goto add_event;
  251.         }

  252.         ngx_rbtree_delete(&cache->rbtree, &file->node);

  253.         cache->current--;

  254.         file->close = 1;

  255.         goto create;
  256.     }

  257.     /* not found */

  258.     rc = ngx_open_and_stat_file(name, of, pool->log);

  259.     if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
  260.         goto failed;
  261.     }

  262. create:

  263.     if (cache->current >= cache->max) {
  264.         ngx_expire_old_cached_files(cache, 0, pool->log);
  265.     }

  266.     file = ngx_alloc(sizeof(ngx_cached_open_file_t), pool->log);

  267.     if (file == NULL) {
  268.         goto failed;
  269.     }

  270.     file->name = ngx_alloc(name->len + 1, pool->log);

  271.     if (file->name == NULL) {
  272.         ngx_free(file);
  273.         file = NULL;
  274.         goto failed;
  275.     }

  276.     ngx_cpystrn(file->name, name->data, name->len + 1);

  277.     file->node.key = hash;

  278.     ngx_rbtree_insert(&cache->rbtree, &file->node);

  279.     cache->current++;

  280.     file->uses = 1;
  281.     file->count = 0;
  282.     file->use_event = 0;
  283.     file->event = NULL;

  284. add_event:

  285.     ngx_open_file_add_event(cache, file, of, pool->log);

  286. update:

  287.     file->fd = of->fd;
  288.     file->err = of->err;
  289. #if (NGX_HAVE_OPENAT)
  290.     file->disable_symlinks = of->disable_symlinks;
  291.     file->disable_symlinks_from = of->disable_symlinks_from;
  292. #endif

  293.     if (of->err == 0) {
  294.         file->uniq = of->uniq;
  295.         file->mtime = of->mtime;
  296.         file->size = of->size;

  297.         file->close = 0;

  298.         file->is_dir = of->is_dir;
  299.         file->is_file = of->is_file;
  300.         file->is_link = of->is_link;
  301.         file->is_exec = of->is_exec;
  302.         file->is_directio = of->is_directio;

  303.         if (!of->is_dir) {
  304.             file->count++;
  305.         }
  306.     }

  307.     file->created = now;

  308. found:

  309.     file->accessed = now;

  310.     ngx_queue_insert_head(&cache->expire_queue, &file->queue);

  311.     ngx_log_debug5(NGX_LOG_DEBUG_CORE, pool->log, 0,
  312.                    "cached open file: %s, fd:%d, c:%d, e:%d, u:%d",
  313.                    file->name, file->fd, file->count, file->err, file->uses);

  314.     if (of->err == 0) {

  315.         if (!of->is_dir) {
  316.             cln->handler = ngx_open_file_cleanup;
  317.             ofcln = cln->data;

  318.             ofcln->cache = cache;
  319.             ofcln->file = file;
  320.             ofcln->min_uses = of->min_uses;
  321.             ofcln->log = pool->log;
  322.         }

  323.         return NGX_OK;
  324.     }

  325.     return NGX_ERROR;

  326. failed:

  327.     if (file) {
  328.         ngx_rbtree_delete(&cache->rbtree, &file->node);

  329.         cache->current--;

  330.         if (file->count == 0) {

  331.             if (file->fd != NGX_INVALID_FILE) {
  332.                 if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
  333.                     ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
  334.                                   ngx_close_file_n " \"%s\" failed",
  335.                                   file->name);
  336.                 }
  337.             }

  338.             ngx_free(file->name);
  339.             ngx_free(file);

  340.         } else {
  341.             file->close = 1;
  342.         }
  343.     }

  344.     if (of->fd != NGX_INVALID_FILE) {
  345.         if (ngx_close_file(of->fd) == NGX_FILE_ERROR) {
  346.             ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
  347.                           ngx_close_file_n " \"%V\" failed", name);
  348.         }
  349.     }

  350.     return NGX_ERROR;
  351. }


  352. #if (NGX_HAVE_OPENAT)

  353. static ngx_fd_t
  354. ngx_openat_file_owner(ngx_fd_t at_fd, const u_char *name,
  355.     ngx_int_t mode, ngx_int_t create, ngx_int_t access, ngx_log_t *log)
  356. {
  357.     ngx_fd_t         fd;
  358.     ngx_err_t        err;
  359.     ngx_file_info_t  fi, atfi;

  360.     /*
  361.      * To allow symlinks with the same owner, use openat() (followed
  362.      * by fstat()) and fstatat(AT_SYMLINK_NOFOLLOW), and then compare
  363.      * uids between fstat() and fstatat().
  364.      *
  365.      * As there is a race between openat() and fstatat() we don't
  366.      * know if openat() in fact opened symlink or not.  Therefore,
  367.      * we have to compare uids even if fstatat() reports the opened
  368.      * component isn't a symlink (as we don't know whether it was
  369.      * symlink during openat() or not).
  370.      */

  371.     fd = ngx_openat_file(at_fd, name, mode, create, access);

  372.     if (fd == NGX_INVALID_FILE) {
  373.         return NGX_INVALID_FILE;
  374.     }

  375.     if (ngx_file_at_info(at_fd, name, &atfi, AT_SYMLINK_NOFOLLOW)
  376.         == NGX_FILE_ERROR)
  377.     {
  378.         err = ngx_errno;
  379.         goto failed;
  380.     }

  381. #if (NGX_HAVE_O_PATH)
  382.     if (ngx_file_o_path_info(fd, &fi, log) == NGX_ERROR) {
  383.         err = ngx_errno;
  384.         goto failed;
  385.     }
  386. #else
  387.     if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
  388.         err = ngx_errno;
  389.         goto failed;
  390.     }
  391. #endif

  392.     if (fi.st_uid != atfi.st_uid) {
  393.         err = NGX_ELOOP;
  394.         goto failed;
  395.     }

  396.     return fd;

  397. failed:

  398.     if (ngx_close_file(fd) == NGX_FILE_ERROR) {
  399.         ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
  400.                       ngx_close_file_n " \"%V\" failed", name);
  401.     }

  402.     ngx_set_errno(err);

  403.     return NGX_INVALID_FILE;
  404. }


  405. #if (NGX_HAVE_O_PATH)

  406. static ngx_int_t
  407. ngx_file_o_path_info(ngx_fd_t fd, ngx_file_info_t *fi, ngx_log_t *log)
  408. {
  409.     static ngx_uint_t  use_fstat = 1;

  410.     /*
  411.      * In Linux 2.6.39 the O_PATH flag was introduced that allows to obtain
  412.      * a descriptor without actually opening file or directory.  It requires
  413.      * less permissions for path components, but till Linux 3.6 fstat() returns
  414.      * EBADF on such descriptors, and fstatat() with the AT_EMPTY_PATH flag
  415.      * should be used instead.
  416.      *
  417.      * Three scenarios are handled in this function:
  418.      *
  419.      * 1) The kernel is newer than 3.6 or fstat() with O_PATH support was
  420.      *    backported by vendor.  Then fstat() is used.
  421.      *
  422.      * 2) The kernel is newer than 2.6.39 but older than 3.6.  In this case
  423.      *    the first call of fstat() returns EBADF and we fallback to fstatat()
  424.      *    with AT_EMPTY_PATH which was introduced at the same time as O_PATH.
  425.      *
  426.      * 3) The kernel is older than 2.6.39 but nginx was build with O_PATH
  427.      *    support.  Since descriptors are opened with O_PATH|O_RDONLY flags
  428.      *    and O_PATH is ignored by the kernel then the O_RDONLY flag is
  429.      *    actually used.  In this case fstat() just works.
  430.      */

  431.     if (use_fstat) {
  432.         if (ngx_fd_info(fd, fi) != NGX_FILE_ERROR) {
  433.             return NGX_OK;
  434.         }

  435.         if (ngx_errno != NGX_EBADF) {
  436.             return NGX_ERROR;
  437.         }

  438.         ngx_log_error(NGX_LOG_NOTICE, log, 0,
  439.                       "fstat(O_PATH) failed with EBADF, "
  440.                       "switching to fstatat(AT_EMPTY_PATH)");

  441.         use_fstat = 0;
  442.     }

  443.     if (ngx_file_at_info(fd, "", fi, AT_EMPTY_PATH) != NGX_FILE_ERROR) {
  444.         return NGX_OK;
  445.     }

  446.     return NGX_ERROR;
  447. }

  448. #endif

  449. #endif /* NGX_HAVE_OPENAT */


  450. static ngx_fd_t
  451. ngx_open_file_wrapper(ngx_str_t *name, ngx_open_file_info_t *of,
  452.     ngx_int_t mode, ngx_int_t create, ngx_int_t access, ngx_log_t *log)
  453. {
  454.     ngx_fd_t  fd;

  455. #if !(NGX_HAVE_OPENAT)

  456.     fd = ngx_open_file(name->data, mode, create, access);

  457.     if (fd == NGX_INVALID_FILE) {
  458.         of->err = ngx_errno;
  459.         of->failed = ngx_open_file_n;
  460.         return NGX_INVALID_FILE;
  461.     }

  462.     return fd;

  463. #else

  464.     u_char           *p, *cp, *end;
  465.     ngx_fd_t          at_fd;
  466.     ngx_str_t         at_name;

  467.     if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) {
  468.         fd = ngx_open_file(name->data, mode, create, access);

  469.         if (fd == NGX_INVALID_FILE) {
  470.             of->err = ngx_errno;
  471.             of->failed = ngx_open_file_n;
  472.             return NGX_INVALID_FILE;
  473.         }

  474.         return fd;
  475.     }

  476.     p = name->data;
  477.     end = p + name->len;

  478.     at_name = *name;

  479.     if (of->disable_symlinks_from) {

  480.         cp = p + of->disable_symlinks_from;

  481.         *cp = '\0';

  482.         at_fd = ngx_open_file(p, NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,
  483.                               NGX_FILE_OPEN, 0);

  484.         *cp = '/';

  485.         if (at_fd == NGX_INVALID_FILE) {
  486.             of->err = ngx_errno;
  487.             of->failed = ngx_open_file_n;
  488.             return NGX_INVALID_FILE;
  489.         }

  490.         at_name.len = of->disable_symlinks_from;
  491.         p = cp + 1;

  492.     } else if (*p == '/') {

  493.         at_fd = ngx_open_file("/",
  494.                               NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,
  495.                               NGX_FILE_OPEN, 0);

  496.         if (at_fd == NGX_INVALID_FILE) {
  497.             of->err = ngx_errno;
  498.             of->failed = ngx_openat_file_n;
  499.             return NGX_INVALID_FILE;
  500.         }

  501.         at_name.len = 1;
  502.         p++;

  503.     } else {
  504.         at_fd = NGX_AT_FDCWD;
  505.     }

  506.     for ( ;; ) {
  507.         cp = ngx_strlchr(p, end, '/');
  508.         if (cp == NULL) {
  509.             break;
  510.         }

  511.         if (cp == p) {
  512.             p++;
  513.             continue;
  514.         }

  515.         *cp = '\0';

  516.         if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER) {
  517.             fd = ngx_openat_file_owner(at_fd, p,
  518.                                        NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,
  519.                                        NGX_FILE_OPEN, 0, log);

  520.         } else {
  521.             fd = ngx_openat_file(at_fd, p,
  522.                            NGX_FILE_SEARCH|NGX_FILE_NONBLOCK|NGX_FILE_NOFOLLOW,
  523.                            NGX_FILE_OPEN, 0);
  524.         }

  525.         *cp = '/';

  526.         if (fd == NGX_INVALID_FILE) {
  527.             of->err = ngx_errno;
  528.             of->failed = ngx_openat_file_n;
  529.             goto failed;
  530.         }

  531.         if (at_fd != NGX_AT_FDCWD && ngx_close_file(at_fd) == NGX_FILE_ERROR) {
  532.             ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
  533.                           ngx_close_file_n " \"%V\" failed", &at_name);
  534.         }

  535.         p = cp + 1;
  536.         at_fd = fd;
  537.         at_name.len = cp - at_name.data;
  538.     }

  539.     if (p == end) {

  540.         /*
  541.          * If pathname ends with a trailing slash, assume the last path
  542.          * component is a directory and reopen it with requested flags;
  543.          * if not, fail with ENOTDIR as per POSIX.
  544.          *
  545.          * We cannot rely on O_DIRECTORY in the loop above to check
  546.          * that the last path component is a directory because
  547.          * O_DIRECTORY doesn't work on FreeBSD 8.  Fortunately, by
  548.          * reopening a directory, we don't depend on it at all.
  549.          */

  550.         fd = ngx_openat_file(at_fd, ".", mode, create, access);
  551.         goto done;
  552.     }

  553.     if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER
  554.         && !(create & (NGX_FILE_CREATE_OR_OPEN|NGX_FILE_TRUNCATE)))
  555.     {
  556.         fd = ngx_openat_file_owner(at_fd, p, mode, create, access, log);

  557.     } else {
  558.         fd = ngx_openat_file(at_fd, p, mode|NGX_FILE_NOFOLLOW, create, access);
  559.     }

  560. done:

  561.     if (fd == NGX_INVALID_FILE) {
  562.         of->err = ngx_errno;
  563.         of->failed = ngx_openat_file_n;
  564.     }

  565. failed:

  566.     if (at_fd != NGX_AT_FDCWD && ngx_close_file(at_fd) == NGX_FILE_ERROR) {
  567.         ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
  568.                       ngx_close_file_n " \"%V\" failed", &at_name);
  569.     }

  570.     return fd;
  571. #endif
  572. }


  573. static ngx_int_t
  574. ngx_file_info_wrapper(ngx_str_t *name, ngx_open_file_info_t *of,
  575.     ngx_file_info_t *fi, ngx_log_t *log)
  576. {
  577.     ngx_int_t  rc;

  578. #if !(NGX_HAVE_OPENAT)

  579.     rc = ngx_file_info(name->data, fi);

  580.     if (rc == NGX_FILE_ERROR) {
  581.         of->err = ngx_errno;
  582.         of->failed = ngx_file_info_n;
  583.         return NGX_FILE_ERROR;
  584.     }

  585.     return rc;

  586. #else

  587.     ngx_fd_t  fd;

  588.     if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) {

  589.         rc = ngx_file_info(name->data, fi);

  590.         if (rc == NGX_FILE_ERROR) {
  591.             of->err = ngx_errno;
  592.             of->failed = ngx_file_info_n;
  593.             return NGX_FILE_ERROR;
  594.         }

  595.         return rc;
  596.     }

  597.     fd = ngx_open_file_wrapper(name, of, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,
  598.                                NGX_FILE_OPEN, 0, log);

  599.     if (fd == NGX_INVALID_FILE) {
  600.         return NGX_FILE_ERROR;
  601.     }

  602.     rc = ngx_fd_info(fd, fi);

  603.     if (rc == NGX_FILE_ERROR) {
  604.         of->err = ngx_errno;
  605.         of->failed = ngx_fd_info_n;
  606.     }

  607.     if (ngx_close_file(fd) == NGX_FILE_ERROR) {
  608.         ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
  609.                       ngx_close_file_n " \"%V\" failed", name);
  610.     }

  611.     return rc;
  612. #endif
  613. }


  614. static ngx_int_t
  615. ngx_open_and_stat_file(ngx_str_t *name, ngx_open_file_info_t *of,
  616.     ngx_log_t *log)
  617. {
  618.     ngx_fd_t         fd;
  619.     ngx_file_info_t  fi;

  620.     if (of->fd != NGX_INVALID_FILE) {

  621.         if (ngx_file_info_wrapper(name, of, &fi, log) == NGX_FILE_ERROR) {
  622.             of->fd = NGX_INVALID_FILE;
  623.             return NGX_ERROR;
  624.         }

  625.         if (of->uniq == ngx_file_uniq(&fi)) {
  626.             goto done;
  627.         }

  628.     } else if (of->test_dir) {

  629.         if (ngx_file_info_wrapper(name, of, &fi, log) == NGX_FILE_ERROR) {
  630.             of->fd = NGX_INVALID_FILE;
  631.             return NGX_ERROR;
  632.         }

  633.         if (ngx_is_dir(&fi)) {
  634.             goto done;
  635.         }
  636.     }

  637.     if (!of->log) {

  638.         /*
  639.          * Use non-blocking open() not to hang on FIFO files, etc.
  640.          * This flag has no effect on a regular files.
  641.          */

  642.         fd = ngx_open_file_wrapper(name, of, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,
  643.                                    NGX_FILE_OPEN, 0, log);

  644.     } else {
  645.         fd = ngx_open_file_wrapper(name, of, NGX_FILE_APPEND,
  646.                                    NGX_FILE_CREATE_OR_OPEN,
  647.                                    NGX_FILE_DEFAULT_ACCESS, log);
  648.     }

  649.     if (fd == NGX_INVALID_FILE) {
  650.         of->fd = NGX_INVALID_FILE;
  651.         return NGX_ERROR;
  652.     }

  653.     if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
  654.         ngx_log_error(NGX_LOG_CRIT, log, ngx_errno,
  655.                       ngx_fd_info_n " \"%V\" failed", name);

  656.         if (ngx_close_file(fd) == NGX_FILE_ERROR) {
  657.             ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
  658.                           ngx_close_file_n " \"%V\" failed", name);
  659.         }

  660.         of->fd = NGX_INVALID_FILE;

  661.         return NGX_ERROR;
  662.     }

  663.     if (ngx_is_dir(&fi)) {
  664.         if (ngx_close_file(fd) == NGX_FILE_ERROR) {
  665.             ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
  666.                           ngx_close_file_n " \"%V\" failed", name);
  667.         }

  668.         of->fd = NGX_INVALID_FILE;

  669.     } else {
  670.         of->fd = fd;

  671.         if (of->read_ahead && ngx_file_size(&fi) > NGX_MIN_READ_AHEAD) {
  672.             if (ngx_read_ahead(fd, of->read_ahead) == NGX_ERROR) {
  673.                 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
  674.                               ngx_read_ahead_n " \"%V\" failed", name);
  675.             }
  676.         }

  677.         if (of->directio <= ngx_file_size(&fi)) {
  678.             if (ngx_directio_on(fd) == NGX_FILE_ERROR) {
  679.                 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
  680.                               ngx_directio_on_n " \"%V\" failed", name);

  681.             } else {
  682.                 of->is_directio = 1;
  683.             }
  684.         }
  685.     }

  686. done:

  687.     of->uniq = ngx_file_uniq(&fi);
  688.     of->mtime = ngx_file_mtime(&fi);
  689.     of->size = ngx_file_size(&fi);
  690.     of->fs_size = ngx_file_fs_size(&fi);
  691.     of->is_dir = ngx_is_dir(&fi);
  692.     of->is_file = ngx_is_file(&fi);
  693.     of->is_link = ngx_is_link(&fi);
  694.     of->is_exec = ngx_is_exec(&fi);

  695.     return NGX_OK;
  696. }


  697. /*
  698. * we ignore any possible event setting error and
  699. * fallback to usual periodic file retests
  700. */

  701. static void
  702. ngx_open_file_add_event(ngx_open_file_cache_t *cache,
  703.     ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log)
  704. {
  705.     ngx_open_file_cache_event_t  *fev;

  706.     if (!(ngx_event_flags & NGX_USE_VNODE_EVENT)
  707.         || !of->events
  708.         || file->event
  709.         || of->fd == NGX_INVALID_FILE
  710.         || file->uses < of->min_uses)
  711.     {
  712.         return;
  713.     }

  714.     file->use_event = 0;

  715.     file->event = ngx_calloc(sizeof(ngx_event_t), log);
  716.     if (file->event== NULL) {
  717.         return;
  718.     }

  719.     fev = ngx_alloc(sizeof(ngx_open_file_cache_event_t), log);
  720.     if (fev == NULL) {
  721.         ngx_free(file->event);
  722.         file->event = NULL;
  723.         return;
  724.     }

  725.     fev->fd = of->fd;
  726.     fev->file = file;
  727.     fev->cache = cache;

  728.     file->event->handler = ngx_open_file_cache_remove;
  729.     file->event->data = fev;

  730.     /*
  731.      * although vnode event may be called while ngx_cycle->poll
  732.      * destruction, however, cleanup procedures are run before any
  733.      * memory freeing and events will be canceled.
  734.      */

  735.     file->event->log = ngx_cycle->log;

  736.     if (ngx_add_event(file->event, NGX_VNODE_EVENT, NGX_ONESHOT_EVENT)
  737.         != NGX_OK)
  738.     {
  739.         ngx_free(file->event->data);
  740.         ngx_free(file->event);
  741.         file->event = NULL;
  742.         return;
  743.     }

  744.     /*
  745.      * we do not set file->use_event here because there may be a race
  746.      * condition: a file may be deleted between opening the file and
  747.      * adding event, so we rely upon event notification only after
  748.      * one file revalidation on next file access
  749.      */

  750.     return;
  751. }


  752. static void
  753. ngx_open_file_cleanup(void *data)
  754. {
  755.     ngx_open_file_cache_cleanup_t  *c = data;

  756.     c->file->count--;

  757.     ngx_close_cached_file(c->cache, c->file, c->min_uses, c->log);

  758.     /* drop one or two expired open files */
  759.     ngx_expire_old_cached_files(c->cache, 1, c->log);
  760. }


  761. static void
  762. ngx_close_cached_file(ngx_open_file_cache_t *cache,
  763.     ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log)
  764. {
  765.     ngx_log_debug5(NGX_LOG_DEBUG_CORE, log, 0,
  766.                    "close cached open file: %s, fd:%d, c:%d, u:%d, %d",
  767.                    file->name, file->fd, file->count, file->uses, file->close);

  768.     if (!file->close) {

  769.         file->accessed = ngx_time();

  770.         ngx_queue_remove(&file->queue);

  771.         ngx_queue_insert_head(&cache->expire_queue, &file->queue);

  772.         if (file->uses >= min_uses || file->count) {
  773.             return;
  774.         }
  775.     }

  776.     ngx_open_file_del_event(file);

  777.     if (file->count) {
  778.         return;
  779.     }

  780.     if (file->fd != NGX_INVALID_FILE) {

  781.         if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
  782.             ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
  783.                           ngx_close_file_n " \"%s\" failed", file->name);
  784.         }

  785.         file->fd = NGX_INVALID_FILE;
  786.     }

  787.     if (!file->close) {
  788.         return;
  789.     }

  790.     ngx_free(file->name);
  791.     ngx_free(file);
  792. }


  793. static void
  794. ngx_open_file_del_event(ngx_cached_open_file_t *file)
  795. {
  796.     if (file->event == NULL) {
  797.         return;
  798.     }

  799.     (void) ngx_del_event(file->event, NGX_VNODE_EVENT,
  800.                          file->count ? NGX_FLUSH_EVENT : NGX_CLOSE_EVENT);

  801.     ngx_free(file->event->data);
  802.     ngx_free(file->event);
  803.     file->event = NULL;
  804.     file->use_event = 0;
  805. }


  806. static void
  807. ngx_expire_old_cached_files(ngx_open_file_cache_t *cache, ngx_uint_t n,
  808.     ngx_log_t *log)
  809. {
  810.     time_t                   now;
  811.     ngx_queue_t             *q;
  812.     ngx_cached_open_file_t  *file;

  813.     now = ngx_time();

  814.     /*
  815.      * n == 1 deletes one or two inactive files
  816.      * n == 0 deletes least recently used file by force
  817.      *        and one or two inactive files
  818.      */

  819.     while (n < 3) {

  820.         if (ngx_queue_empty(&cache->expire_queue)) {
  821.             return;
  822.         }

  823.         q = ngx_queue_last(&cache->expire_queue);

  824.         file = ngx_queue_data(q, ngx_cached_open_file_t, queue);

  825.         if (n++ != 0 && now - file->accessed <= cache->inactive) {
  826.             return;
  827.         }

  828.         ngx_queue_remove(q);

  829.         ngx_rbtree_delete(&cache->rbtree, &file->node);

  830.         cache->current--;

  831.         ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0,
  832.                        "expire cached open file: %s", file->name);

  833.         if (!file->err && !file->is_dir) {
  834.             file->close = 1;
  835.             ngx_close_cached_file(cache, file, 0, log);

  836.         } else {
  837.             ngx_free(file->name);
  838.             ngx_free(file);
  839.         }
  840.     }
  841. }


  842. static void
  843. ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
  844.     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
  845. {
  846.     ngx_rbtree_node_t       **p;
  847.     ngx_cached_open_file_t    *file, *file_temp;

  848.     for ( ;; ) {

  849.         if (node->key < temp->key) {

  850.             p = &temp->left;

  851.         } else if (node->key > temp->key) {

  852.             p = &temp->right;

  853.         } else { /* node->key == temp->key */

  854.             file = (ngx_cached_open_file_t *) node;
  855.             file_temp = (ngx_cached_open_file_t *) temp;

  856.             p = (ngx_strcmp(file->name, file_temp->name) < 0)
  857.                     ? &temp->left : &temp->right;
  858.         }

  859.         if (*p == sentinel) {
  860.             break;
  861.         }

  862.         temp = *p;
  863.     }

  864.     *p = node;
  865.     node->parent = temp;
  866.     node->left = sentinel;
  867.     node->right = sentinel;
  868.     ngx_rbt_red(node);
  869. }


  870. static ngx_cached_open_file_t *
  871. ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name,
  872.     uint32_t hash)
  873. {
  874.     ngx_int_t                rc;
  875.     ngx_rbtree_node_t       *node, *sentinel;
  876.     ngx_cached_open_file_t  *file;

  877.     node = cache->rbtree.root;
  878.     sentinel = cache->rbtree.sentinel;

  879.     while (node != sentinel) {

  880.         if (hash < node->key) {
  881.             node = node->left;
  882.             continue;
  883.         }

  884.         if (hash > node->key) {
  885.             node = node->right;
  886.             continue;
  887.         }

  888.         /* hash == node->key */

  889.         file = (ngx_cached_open_file_t *) node;

  890.         rc = ngx_strcmp(name->data, file->name);

  891.         if (rc == 0) {
  892.             return file;
  893.         }

  894.         node = (rc < 0) ? node->left : node->right;
  895.     }

  896.     return NULL;
  897. }


  898. static void
  899. ngx_open_file_cache_remove(ngx_event_t *ev)
  900. {
  901.     ngx_cached_open_file_t       *file;
  902.     ngx_open_file_cache_event_t  *fev;

  903.     fev = ev->data;
  904.     file = fev->file;

  905.     ngx_queue_remove(&file->queue);

  906.     ngx_rbtree_delete(&fev->cache->rbtree, &file->node);

  907.     fev->cache->current--;

  908.     /* NGX_ONESHOT_EVENT was already deleted */
  909.     file->event = NULL;
  910.     file->use_event = 0;

  911.     file->close = 1;

  912.     ngx_close_cached_file(fev->cache, file, 0, ev->log);

  913.     /* free memory only when fev->cache and fev->file are already not needed */

  914.     ngx_free(ev->data);
  915.     ngx_free(ev);
  916. }