Annotation of embedaddon/lighttpd/src/stat_cache.c, revision 1.1
1.1 ! misho 1: #include "log.h"
! 2: #include "stat_cache.h"
! 3: #include "fdevent.h"
! 4: #include "etag.h"
! 5:
! 6: #include <sys/types.h>
! 7: #include <sys/stat.h>
! 8:
! 9: #include <stdlib.h>
! 10: #include <string.h>
! 11: #include <errno.h>
! 12: #include <unistd.h>
! 13: #include <stdio.h>
! 14: #include <fcntl.h>
! 15: #include <assert.h>
! 16:
! 17: #ifdef HAVE_ATTR_ATTRIBUTES_H
! 18: # include <attr/attributes.h>
! 19: #endif
! 20:
! 21: #ifdef HAVE_FAM_H
! 22: # include <fam.h>
! 23: #endif
! 24:
! 25: #include "sys-mmap.h"
! 26:
! 27: /* NetBSD 1.3.x needs it */
! 28: #ifndef MAP_FAILED
! 29: # define MAP_FAILED -1
! 30: #endif
! 31:
! 32: #ifndef O_LARGEFILE
! 33: # define O_LARGEFILE 0
! 34: #endif
! 35:
! 36: #ifndef HAVE_LSTAT
! 37: # define lstat stat
! 38: #endif
! 39:
! 40: #if 0
! 41: /* enables debug code for testing if all nodes in the stat-cache as accessable */
! 42: #define DEBUG_STAT_CACHE
! 43: #endif
! 44:
! 45: /*
! 46: * stat-cache
! 47: *
! 48: * we cache the stat() calls in our own storage
! 49: * the directories are cached in FAM
! 50: *
! 51: * if we get a change-event from FAM, we increment the version in the FAM->dir mapping
! 52: *
! 53: * if the stat()-cache is queried we check if the version id for the directory is the
! 54: * same and return immediatly.
! 55: *
! 56: *
! 57: * What we need:
! 58: *
! 59: * - for each stat-cache entry we need a fast indirect lookup on the directory name
! 60: * - for each FAMRequest we have to find the version in the directory cache (index as userdata)
! 61: *
! 62: * stat <<-> directory <-> FAMRequest
! 63: *
! 64: * if file is deleted, directory is dirty, file is rechecked ...
! 65: * if directory is deleted, directory mapping is removed
! 66: *
! 67: * */
! 68:
! 69: #ifdef HAVE_FAM_H
! 70: typedef struct {
! 71: FAMRequest *req;
! 72: FAMConnection *fc;
! 73:
! 74: buffer *name;
! 75:
! 76: int version;
! 77: } fam_dir_entry;
! 78: #endif
! 79:
! 80: /* the directory name is too long to always compare on it
! 81: * - we need a hash
! 82: * - the hash-key is used as sorting criteria for a tree
! 83: * - a splay-tree is used as we can use the caching effect of it
! 84: */
! 85:
! 86: /* we want to cleanup the stat-cache every few seconds, let's say 10
! 87: *
! 88: * - remove entries which are outdated since 30s
! 89: * - remove entries which are fresh but havn't been used since 60s
! 90: * - if we don't have a stat-cache entry for a directory, release it from the monitor
! 91: */
! 92:
! 93: #ifdef DEBUG_STAT_CACHE
! 94: typedef struct {
! 95: int *ptr;
! 96:
! 97: size_t used;
! 98: size_t size;
! 99: } fake_keys;
! 100:
! 101: static fake_keys ctrl;
! 102: #endif
! 103:
! 104: stat_cache *stat_cache_init(void) {
! 105: stat_cache *fc = NULL;
! 106:
! 107: fc = calloc(1, sizeof(*fc));
! 108:
! 109: fc->dir_name = buffer_init();
! 110: fc->hash_key = buffer_init();
! 111: #ifdef HAVE_FAM_H
! 112: fc->fam = calloc(1, sizeof(*fc->fam));
! 113: #endif
! 114:
! 115: #ifdef DEBUG_STAT_CACHE
! 116: ctrl.size = 0;
! 117: #endif
! 118:
! 119: return fc;
! 120: }
! 121:
! 122: static stat_cache_entry * stat_cache_entry_init(void) {
! 123: stat_cache_entry *sce = NULL;
! 124:
! 125: sce = calloc(1, sizeof(*sce));
! 126:
! 127: sce->name = buffer_init();
! 128: sce->etag = buffer_init();
! 129: sce->content_type = buffer_init();
! 130:
! 131: return sce;
! 132: }
! 133:
! 134: static void stat_cache_entry_free(void *data) {
! 135: stat_cache_entry *sce = data;
! 136: if (!sce) return;
! 137:
! 138: buffer_free(sce->etag);
! 139: buffer_free(sce->name);
! 140: buffer_free(sce->content_type);
! 141:
! 142: free(sce);
! 143: }
! 144:
! 145: #ifdef HAVE_FAM_H
! 146: static fam_dir_entry * fam_dir_entry_init(void) {
! 147: fam_dir_entry *fam_dir = NULL;
! 148:
! 149: fam_dir = calloc(1, sizeof(*fam_dir));
! 150:
! 151: fam_dir->name = buffer_init();
! 152:
! 153: return fam_dir;
! 154: }
! 155:
! 156: static void fam_dir_entry_free(void *data) {
! 157: fam_dir_entry *fam_dir = data;
! 158:
! 159: if (!fam_dir) return;
! 160:
! 161: FAMCancelMonitor(fam_dir->fc, fam_dir->req);
! 162:
! 163: buffer_free(fam_dir->name);
! 164: free(fam_dir->req);
! 165:
! 166: free(fam_dir);
! 167: }
! 168: #endif
! 169:
! 170: void stat_cache_free(stat_cache *sc) {
! 171: while (sc->files) {
! 172: int osize;
! 173: splay_tree *node = sc->files;
! 174:
! 175: osize = sc->files->size;
! 176:
! 177: stat_cache_entry_free(node->data);
! 178: sc->files = splaytree_delete(sc->files, node->key);
! 179:
! 180: assert(osize - 1 == splaytree_size(sc->files));
! 181: }
! 182:
! 183: buffer_free(sc->dir_name);
! 184: buffer_free(sc->hash_key);
! 185:
! 186: #ifdef HAVE_FAM_H
! 187: while (sc->dirs) {
! 188: int osize;
! 189: splay_tree *node = sc->dirs;
! 190:
! 191: osize = sc->dirs->size;
! 192:
! 193: fam_dir_entry_free(node->data);
! 194: sc->dirs = splaytree_delete(sc->dirs, node->key);
! 195:
! 196: if (osize == 1) {
! 197: assert(NULL == sc->dirs);
! 198: } else {
! 199: assert(osize == (sc->dirs->size + 1));
! 200: }
! 201: }
! 202:
! 203: if (sc->fam) {
! 204: FAMClose(sc->fam);
! 205: free(sc->fam);
! 206: }
! 207: #endif
! 208: free(sc);
! 209: }
! 210:
! 211: #ifdef HAVE_XATTR
! 212: static int stat_cache_attr_get(buffer *buf, char *name) {
! 213: int attrlen;
! 214: int ret;
! 215:
! 216: attrlen = 1024;
! 217: buffer_prepare_copy(buf, attrlen);
! 218: attrlen--;
! 219: if(0 == (ret = attr_get(name, "Content-Type", buf->ptr, &attrlen, 0))) {
! 220: buf->used = attrlen + 1;
! 221: buf->ptr[attrlen] = '\0';
! 222: }
! 223: return ret;
! 224: }
! 225: #endif
! 226:
! 227: /* the famous DJB hash function for strings */
! 228: static uint32_t hashme(buffer *str) {
! 229: uint32_t hash = 5381;
! 230: const char *s;
! 231: for (s = str->ptr; *s; s++) {
! 232: hash = ((hash << 5) + hash) + *s;
! 233: }
! 234:
! 235: hash &= ~(1 << 31); /* strip the highest bit */
! 236:
! 237: return hash;
! 238: }
! 239:
! 240: #ifdef HAVE_FAM_H
! 241: handler_t stat_cache_handle_fdevent(server *srv, void *_fce, int revent) {
! 242: size_t i;
! 243: stat_cache *sc = srv->stat_cache;
! 244: size_t events;
! 245:
! 246: UNUSED(_fce);
! 247: /* */
! 248:
! 249: if ((revent & FDEVENT_IN) &&
! 250: sc->fam) {
! 251:
! 252: events = FAMPending(sc->fam);
! 253:
! 254: for (i = 0; i < events; i++) {
! 255: FAMEvent fe;
! 256: fam_dir_entry *fam_dir;
! 257: splay_tree *node;
! 258: int ndx, j;
! 259:
! 260: FAMNextEvent(sc->fam, &fe);
! 261:
! 262: /* handle event */
! 263:
! 264: switch(fe.code) {
! 265: case FAMChanged:
! 266: case FAMDeleted:
! 267: case FAMMoved:
! 268: /* if the filename is a directory remove the entry */
! 269:
! 270: fam_dir = fe.userdata;
! 271: fam_dir->version++;
! 272:
! 273: /* file/dir is still here */
! 274: if (fe.code == FAMChanged) break;
! 275:
! 276: /* we have 2 versions, follow and no-follow-symlink */
! 277:
! 278: for (j = 0; j < 2; j++) {
! 279: buffer_copy_string(sc->hash_key, fe.filename);
! 280: buffer_append_long(sc->hash_key, j);
! 281:
! 282: ndx = hashme(sc->hash_key);
! 283:
! 284: sc->dirs = splaytree_splay(sc->dirs, ndx);
! 285: node = sc->dirs;
! 286:
! 287: if (node && (node->key == ndx)) {
! 288: int osize = splaytree_size(sc->dirs);
! 289:
! 290: fam_dir_entry_free(node->data);
! 291: sc->dirs = splaytree_delete(sc->dirs, ndx);
! 292:
! 293: assert(osize - 1 == splaytree_size(sc->dirs));
! 294: }
! 295: }
! 296: break;
! 297: default:
! 298: break;
! 299: }
! 300: }
! 301: }
! 302:
! 303: if (revent & FDEVENT_HUP) {
! 304: /* fam closed the connection */
! 305: srv->stat_cache->fam_fcce_ndx = -1;
! 306:
! 307: fdevent_event_del(srv->ev, &(sc->fam_fcce_ndx), FAMCONNECTION_GETFD(sc->fam));
! 308: fdevent_unregister(srv->ev, FAMCONNECTION_GETFD(sc->fam));
! 309:
! 310: FAMClose(sc->fam);
! 311: free(sc->fam);
! 312:
! 313: sc->fam = NULL;
! 314: }
! 315:
! 316: return HANDLER_GO_ON;
! 317: }
! 318:
! 319: static int buffer_copy_dirname(buffer *dst, buffer *file) {
! 320: size_t i;
! 321:
! 322: if (buffer_is_empty(file)) return -1;
! 323:
! 324: for (i = file->used - 1; i+1 > 0; i--) {
! 325: if (file->ptr[i] == '/') {
! 326: buffer_copy_string_len(dst, file->ptr, i);
! 327: return 0;
! 328: }
! 329: }
! 330:
! 331: return -1;
! 332: }
! 333: #endif
! 334:
! 335: #ifdef HAVE_LSTAT
! 336: static int stat_cache_lstat(server *srv, buffer *dname, struct stat *lst) {
! 337: if (lstat(dname->ptr, lst) == 0) {
! 338: return S_ISLNK(lst->st_mode) ? 0 : 1;
! 339: }
! 340: else {
! 341: log_error_write(srv, __FILE__, __LINE__, "sbs",
! 342: "lstat failed for:",
! 343: dname, strerror(errno));
! 344: };
! 345: return -1;
! 346: }
! 347: #endif
! 348:
! 349: /***
! 350: *
! 351: *
! 352: *
! 353: * returns:
! 354: * - HANDLER_FINISHED on cache-miss (don't forget to reopen the file)
! 355: * - HANDLER_ERROR on stat() failed -> see errno for problem
! 356: */
! 357:
! 358: handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_cache_entry **ret_sce) {
! 359: #ifdef HAVE_FAM_H
! 360: fam_dir_entry *fam_dir = NULL;
! 361: int dir_ndx = -1;
! 362: splay_tree *dir_node = NULL;
! 363: #endif
! 364: stat_cache_entry *sce = NULL;
! 365: stat_cache *sc;
! 366: struct stat st;
! 367: size_t k;
! 368: int fd;
! 369: struct stat lst;
! 370: #ifdef DEBUG_STAT_CACHE
! 371: size_t i;
! 372: #endif
! 373:
! 374: int file_ndx;
! 375: splay_tree *file_node = NULL;
! 376:
! 377: *ret_sce = NULL;
! 378:
! 379: /*
! 380: * check if the directory for this file has changed
! 381: */
! 382:
! 383: sc = srv->stat_cache;
! 384:
! 385: buffer_copy_string_buffer(sc->hash_key, name);
! 386: buffer_append_long(sc->hash_key, con->conf.follow_symlink);
! 387:
! 388: file_ndx = hashme(sc->hash_key);
! 389: sc->files = splaytree_splay(sc->files, file_ndx);
! 390:
! 391: #ifdef DEBUG_STAT_CACHE
! 392: for (i = 0; i < ctrl.used; i++) {
! 393: if (ctrl.ptr[i] == file_ndx) break;
! 394: }
! 395: #endif
! 396:
! 397: if (sc->files && (sc->files->key == file_ndx)) {
! 398: #ifdef DEBUG_STAT_CACHE
! 399: /* it was in the cache */
! 400: assert(i < ctrl.used);
! 401: #endif
! 402:
! 403: /* we have seen this file already and
! 404: * don't stat() it again in the same second */
! 405:
! 406: file_node = sc->files;
! 407:
! 408: sce = file_node->data;
! 409:
! 410: /* check if the name is the same, we might have a collision */
! 411:
! 412: if (buffer_is_equal(name, sce->name)) {
! 413: if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_SIMPLE) {
! 414: if (sce->stat_ts == srv->cur_ts) {
! 415: *ret_sce = sce;
! 416: return HANDLER_GO_ON;
! 417: }
! 418: }
! 419: } else {
! 420: /* oops, a collision,
! 421: *
! 422: * file_node is used by the FAM check below to see if we know this file
! 423: * and if we can save a stat().
! 424: *
! 425: * BUT, the sce is not reset here as the entry into the cache is ok, we
! 426: * it is just not pointing to our requested file.
! 427: *
! 428: * */
! 429:
! 430: file_node = NULL;
! 431: }
! 432: } else {
! 433: #ifdef DEBUG_STAT_CACHE
! 434: if (i != ctrl.used) {
! 435: log_error_write(srv, __FILE__, __LINE__, "xSB",
! 436: file_ndx, "was already inserted but not found in cache, ", name);
! 437: }
! 438: assert(i == ctrl.used);
! 439: #endif
! 440: }
! 441:
! 442: #ifdef HAVE_FAM_H
! 443: /* dir-check */
! 444: if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) {
! 445: if (0 != buffer_copy_dirname(sc->dir_name, name)) {
! 446: log_error_write(srv, __FILE__, __LINE__, "sb",
! 447: "no '/' found in filename:", name);
! 448: return HANDLER_ERROR;
! 449: }
! 450:
! 451: buffer_copy_string_buffer(sc->hash_key, sc->dir_name);
! 452: buffer_append_long(sc->hash_key, con->conf.follow_symlink);
! 453:
! 454: dir_ndx = hashme(sc->hash_key);
! 455:
! 456: sc->dirs = splaytree_splay(sc->dirs, dir_ndx);
! 457:
! 458: if (sc->dirs && (sc->dirs->key == dir_ndx)) {
! 459: dir_node = sc->dirs;
! 460: }
! 461:
! 462: if (dir_node && file_node) {
! 463: /* we found a file */
! 464:
! 465: sce = file_node->data;
! 466: fam_dir = dir_node->data;
! 467:
! 468: if (fam_dir->version == sce->dir_version) {
! 469: /* the stat()-cache entry is still ok */
! 470:
! 471: *ret_sce = sce;
! 472: return HANDLER_GO_ON;
! 473: }
! 474: }
! 475: }
! 476: #endif
! 477:
! 478: /*
! 479: * *lol*
! 480: * - open() + fstat() on a named-pipe results in a (intended) hang.
! 481: * - stat() if regular file + open() to see if we can read from it is better
! 482: *
! 483: * */
! 484: if (-1 == stat(name->ptr, &st)) {
! 485: return HANDLER_ERROR;
! 486: }
! 487:
! 488:
! 489: if (S_ISREG(st.st_mode)) {
! 490: /* fix broken stat/open for symlinks to reg files with appended slash on freebsd,osx */
! 491: if (name->ptr[name->used-2] == '/') {
! 492: errno = ENOTDIR;
! 493: return HANDLER_ERROR;
! 494: }
! 495:
! 496: /* try to open the file to check if we can read it */
! 497: if (-1 == (fd = open(name->ptr, O_RDONLY))) {
! 498: return HANDLER_ERROR;
! 499: }
! 500: close(fd);
! 501: }
! 502:
! 503: if (NULL == sce) {
! 504: #ifdef DEBUG_STAT_CACHE
! 505: int osize = splaytree_size(sc->files);
! 506: #endif
! 507:
! 508: sce = stat_cache_entry_init();
! 509: buffer_copy_string_buffer(sce->name, name);
! 510:
! 511: sc->files = splaytree_insert(sc->files, file_ndx, sce);
! 512: #ifdef DEBUG_STAT_CACHE
! 513: if (ctrl.size == 0) {
! 514: ctrl.size = 16;
! 515: ctrl.used = 0;
! 516: ctrl.ptr = malloc(ctrl.size * sizeof(*ctrl.ptr));
! 517: } else if (ctrl.size == ctrl.used) {
! 518: ctrl.size += 16;
! 519: ctrl.ptr = realloc(ctrl.ptr, ctrl.size * sizeof(*ctrl.ptr));
! 520: }
! 521:
! 522: ctrl.ptr[ctrl.used++] = file_ndx;
! 523:
! 524: assert(sc->files);
! 525: assert(sc->files->data == sce);
! 526: assert(osize + 1 == splaytree_size(sc->files));
! 527: #endif
! 528: }
! 529:
! 530: sce->st = st;
! 531: sce->stat_ts = srv->cur_ts;
! 532:
! 533: /* catch the obvious symlinks
! 534: *
! 535: * this is not a secure check as we still have a race-condition between
! 536: * the stat() and the open. We can only solve this by
! 537: * 1. open() the file
! 538: * 2. fstat() the fd
! 539: *
! 540: * and keeping the file open for the rest of the time. But this can
! 541: * only be done at network level.
! 542: *
! 543: * per default it is not a symlink
! 544: * */
! 545: #ifdef HAVE_LSTAT
! 546: sce->is_symlink = 0;
! 547:
! 548: /* we want to only check for symlinks if we should block symlinks.
! 549: */
! 550: if (!con->conf.follow_symlink) {
! 551: if (stat_cache_lstat(srv, name, &lst) == 0) {
! 552: #ifdef DEBUG_STAT_CACHE
! 553: log_error_write(srv, __FILE__, __LINE__, "sb",
! 554: "found symlink", name);
! 555: #endif
! 556: sce->is_symlink = 1;
! 557: }
! 558:
! 559: /*
! 560: * we assume "/" can not be symlink, so
! 561: * skip the symlink stuff if our path is /
! 562: **/
! 563: else if ((name->used > 2)) {
! 564: buffer *dname;
! 565: char *s_cur;
! 566:
! 567: dname = buffer_init();
! 568: buffer_copy_string_buffer(dname, name);
! 569:
! 570: while ((s_cur = strrchr(dname->ptr,'/'))) {
! 571: *s_cur = '\0';
! 572: dname->used = s_cur - dname->ptr + 1;
! 573: if (dname->ptr == s_cur) {
! 574: #ifdef DEBUG_STAT_CACHE
! 575: log_error_write(srv, __FILE__, __LINE__, "s", "reached /");
! 576: #endif
! 577: break;
! 578: }
! 579: #ifdef DEBUG_STAT_CACHE
! 580: log_error_write(srv, __FILE__, __LINE__, "sbs",
! 581: "checking if", dname, "is a symlink");
! 582: #endif
! 583: if (stat_cache_lstat(srv, dname, &lst) == 0) {
! 584: sce->is_symlink = 1;
! 585: #ifdef DEBUG_STAT_CACHE
! 586: log_error_write(srv, __FILE__, __LINE__, "sb",
! 587: "found symlink", dname);
! 588: #endif
! 589: break;
! 590: };
! 591: };
! 592: buffer_free(dname);
! 593: };
! 594: };
! 595: #endif
! 596:
! 597: if (S_ISREG(st.st_mode)) {
! 598: /* determine mimetype */
! 599: buffer_reset(sce->content_type);
! 600: #ifdef HAVE_XATTR
! 601: if (con->conf.use_xattr) {
! 602: stat_cache_attr_get(sce->content_type, name->ptr);
! 603: }
! 604: #endif
! 605: /* xattr did not set a content-type. ask the config */
! 606: if (buffer_is_empty(sce->content_type)) {
! 607: for (k = 0; k < con->conf.mimetypes->used; k++) {
! 608: data_string *ds = (data_string *)con->conf.mimetypes->data[k];
! 609: buffer *type = ds->key;
! 610:
! 611: if (type->used == 0) continue;
! 612:
! 613: /* check if the right side is the same */
! 614: if (type->used > name->used) continue;
! 615:
! 616: if (0 == strncasecmp(name->ptr + name->used - type->used, type->ptr, type->used - 1)) {
! 617: buffer_copy_string_buffer(sce->content_type, ds->value);
! 618: break;
! 619: }
! 620: }
! 621: }
! 622: etag_create(sce->etag, &(sce->st), con->etag_flags);
! 623: } else if (S_ISDIR(st.st_mode)) {
! 624: etag_create(sce->etag, &(sce->st), con->etag_flags);
! 625: }
! 626:
! 627: #ifdef HAVE_FAM_H
! 628: if (sc->fam &&
! 629: (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM)) {
! 630: /* is this directory already registered ? */
! 631: if (!dir_node) {
! 632: fam_dir = fam_dir_entry_init();
! 633: fam_dir->fc = sc->fam;
! 634:
! 635: buffer_copy_string_buffer(fam_dir->name, sc->dir_name);
! 636:
! 637: fam_dir->version = 1;
! 638:
! 639: fam_dir->req = calloc(1, sizeof(FAMRequest));
! 640:
! 641: if (0 != FAMMonitorDirectory(sc->fam, fam_dir->name->ptr,
! 642: fam_dir->req, fam_dir)) {
! 643:
! 644: log_error_write(srv, __FILE__, __LINE__, "sbsbs",
! 645: "monitoring dir failed:",
! 646: fam_dir->name,
! 647: "file:", name,
! 648: FamErrlist[FAMErrno]);
! 649:
! 650: fam_dir_entry_free(fam_dir);
! 651: } else {
! 652: int osize = 0;
! 653:
! 654: if (sc->dirs) {
! 655: osize = sc->dirs->size;
! 656: }
! 657:
! 658: sc->dirs = splaytree_insert(sc->dirs, dir_ndx, fam_dir);
! 659: assert(sc->dirs);
! 660: assert(sc->dirs->data == fam_dir);
! 661: assert(osize == (sc->dirs->size - 1));
! 662: }
! 663: } else {
! 664: fam_dir = dir_node->data;
! 665: }
! 666:
! 667: /* bind the fam_fc to the stat() cache entry */
! 668:
! 669: if (fam_dir) {
! 670: sce->dir_version = fam_dir->version;
! 671: sce->dir_ndx = dir_ndx;
! 672: }
! 673: }
! 674: #endif
! 675:
! 676: *ret_sce = sce;
! 677:
! 678: return HANDLER_GO_ON;
! 679: }
! 680:
! 681: /**
! 682: * remove stat() from cache which havn't been stat()ed for
! 683: * more than 10 seconds
! 684: *
! 685: *
! 686: * walk though the stat-cache, collect the ids which are too old
! 687: * and remove them in a second loop
! 688: */
! 689:
! 690: static int stat_cache_tag_old_entries(server *srv, splay_tree *t, int *keys, size_t *ndx) {
! 691: stat_cache_entry *sce;
! 692:
! 693: if (!t) return 0;
! 694:
! 695: stat_cache_tag_old_entries(srv, t->left, keys, ndx);
! 696: stat_cache_tag_old_entries(srv, t->right, keys, ndx);
! 697:
! 698: sce = t->data;
! 699:
! 700: if (srv->cur_ts - sce->stat_ts > 2) {
! 701: keys[(*ndx)++] = t->key;
! 702: }
! 703:
! 704: return 0;
! 705: }
! 706:
! 707: int stat_cache_trigger_cleanup(server *srv) {
! 708: stat_cache *sc;
! 709: size_t max_ndx = 0, i;
! 710: int *keys;
! 711:
! 712: sc = srv->stat_cache;
! 713:
! 714: if (!sc->files) return 0;
! 715:
! 716: keys = calloc(1, sizeof(size_t) * sc->files->size);
! 717:
! 718: stat_cache_tag_old_entries(srv, sc->files, keys, &max_ndx);
! 719:
! 720: for (i = 0; i < max_ndx; i++) {
! 721: int ndx = keys[i];
! 722: splay_tree *node;
! 723:
! 724: sc->files = splaytree_splay(sc->files, ndx);
! 725:
! 726: node = sc->files;
! 727:
! 728: if (node && (node->key == ndx)) {
! 729: #ifdef DEBUG_STAT_CACHE
! 730: size_t j;
! 731: int osize = splaytree_size(sc->files);
! 732: stat_cache_entry *sce = node->data;
! 733: #endif
! 734: stat_cache_entry_free(node->data);
! 735: sc->files = splaytree_delete(sc->files, ndx);
! 736:
! 737: #ifdef DEBUG_STAT_CACHE
! 738: for (j = 0; j < ctrl.used; j++) {
! 739: if (ctrl.ptr[j] == ndx) {
! 740: ctrl.ptr[j] = ctrl.ptr[--ctrl.used];
! 741: break;
! 742: }
! 743: }
! 744:
! 745: assert(osize - 1 == splaytree_size(sc->files));
! 746: #endif
! 747: }
! 748: }
! 749:
! 750: free(keys);
! 751:
! 752: return 0;
! 753: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>