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>