Annotation of embedaddon/nginx/src/core/ngx_open_file_cache.c, revision 1.1.1.1

1.1       misho       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,
                     33:     ngx_open_file_info_t *of, ngx_file_info_t *fi, ngx_log_t *log);
                     34: static ngx_int_t ngx_open_and_stat_file(ngx_str_t *name,
                     35:     ngx_open_file_info_t *of, ngx_log_t *log);
                     36: static void ngx_open_file_add_event(ngx_open_file_cache_t *cache,
                     37:     ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log);
                     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,
                     45:     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
                     46: static ngx_cached_open_file_t *
                     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: 
                     52: ngx_open_file_cache_t *
                     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: 
                     66:     ngx_queue_init(&cache->expire_queue);
                     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;
                     90:     ngx_cached_open_file_t  *file;
                     91: 
                     92:     ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
                     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: 
                    111:         ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
                    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) {
                    126:         ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
                    127:                       "%d items still leave in open file cache",
                    128:                       cache->current);
                    129:     }
                    130: 
                    131:     if (cache->rbtree.root != cache->rbtree.sentinel) {
                    132:         ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
                    133:                       "rbtree still is not empty in open file cache");
                    134: 
                    135:     }
                    136: }
                    137: 
                    138: 
                    139: ngx_int_t
                    140: ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name,
                    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;
                    148:     ngx_cached_open_file_t         *file;
                    149:     ngx_pool_cleanup_file_t        *clnf;
                    150:     ngx_open_file_cache_cleanup_t  *ofcln;
                    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) {
                    185:             cln->handler = ngx_pool_cleanup_file;
                    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: 
                    196:     cln = ngx_pool_cleanup_add(pool, sizeof(ngx_open_file_cache_cleanup_t));
                    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: 
                    267:         ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0,
                    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) {
                    332:                 ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
                    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: 
                    428:     ngx_log_debug5(NGX_LOG_DEBUG_CORE, pool->log, 0,
                    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) {
                    460:                     ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
                    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) {
                    476:             ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
                    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) {
                    535:         ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                    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) {
                    660:             ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                    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
                    687:         && !(create & (NGX_FILE_CREATE_OR_OPEN|NGX_FILE_TRUNCATE)))
                    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) {
                    705:         ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                    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) {
                    764:         ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                    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,
                    815:                                    NGX_FILE_CREATE_OR_OPEN,
                    816:                                    NGX_FILE_DEFAULT_ACCESS, log);
                    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) {
                    825:         ngx_log_error(NGX_LOG_CRIT, log, ngx_errno,
                    826:                       ngx_fd_info_n " \"%V\" failed", name);
                    827: 
                    828:         if (ngx_close_file(fd) == NGX_FILE_ERROR) {
                    829:             ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                    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) {
                    840:             ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                    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) {
                    851:                 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                    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) {
                    858:                 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                    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,
                    889:     ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log)
                    890: {
                    891:     ngx_open_file_cache_event_t  *fev;
                    892: 
                    893:     if (!(ngx_event_flags & NGX_USE_VNODE_EVENT)
                    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: 
                    931:     if (ngx_add_event(file->event, NGX_VNODE_EVENT, NGX_ONESHOT_EVENT)
                    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: {
                    954:     ngx_open_file_cache_cleanup_t  *c = data;
                    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: {
                    969:     ngx_log_debug5(NGX_LOG_DEBUG_CORE, log, 0,
                    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) {
                    995:             ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                    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,
                   1019:                          file->count ? NGX_FLUSH_EVENT : NGX_CLOSE_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: 
                   1064:         ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0,
                   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,
                   1081:     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
                   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;
                   1164:     ngx_open_file_cache_event_t  *fev;
                   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: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>