Annotation of embedaddon/nginx/src/core/ngx_open_file_cache.c, revision 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>