File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / lighttpd / src / stat_cache.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 20:20:06 2014 UTC (10 years ago) by misho
Branches: lighttpd, MAIN
CVS tags: v1_4_35p0, v1_4_35, HEAD
lighttpd 1.4.35

    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: 
   73: 	buffer *name;
   74: 
   75: 	int version;
   76: } fam_dir_entry;
   77: #endif
   78: 
   79: /* the directory name is too long to always compare on it
   80:  * - we need a hash
   81:  * - the hash-key is used as sorting criteria for a tree
   82:  * - a splay-tree is used as we can use the caching effect of it
   83:  */
   84: 
   85: /* we want to cleanup the stat-cache every few seconds, let's say 10
   86:  *
   87:  * - remove entries which are outdated since 30s
   88:  * - remove entries which are fresh but havn't been used since 60s
   89:  * - if we don't have a stat-cache entry for a directory, release it from the monitor
   90:  */
   91: 
   92: #ifdef DEBUG_STAT_CACHE
   93: typedef struct {
   94: 	int *ptr;
   95: 
   96: 	size_t used;
   97: 	size_t size;
   98: } fake_keys;
   99: 
  100: static fake_keys ctrl;
  101: #endif
  102: 
  103: stat_cache *stat_cache_init(void) {
  104: 	stat_cache *sc = NULL;
  105: 
  106: 	sc = calloc(1, sizeof(*sc));
  107: 
  108: 	sc->dir_name = buffer_init();
  109: 	sc->hash_key = buffer_init();
  110: 
  111: #ifdef HAVE_FAM_H
  112: 	sc->fam_fcce_ndx = -1;
  113: #endif
  114: 
  115: #ifdef DEBUG_STAT_CACHE
  116: 	ctrl.size = 0;
  117: #endif
  118: 
  119: 	return sc;
  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(FAMConnection *fc, void *data) {
  157: 	fam_dir_entry *fam_dir = data;
  158: 
  159: 	if (!fam_dir) return;
  160: 
  161: 	FAMCancelMonitor(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: 		force_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(&sc->fam, node->data);
  194: 		sc->dirs = splaytree_delete(sc->dirs, node->key);
  195: 
  196: 		if (osize == 1) {
  197: 			force_assert(NULL == sc->dirs);
  198: 		} else {
  199: 			force_assert(osize == (sc->dirs->size + 1));
  200: 		}
  201: 	}
  202: 
  203: 	if (-1 != sc->fam_fcce_ndx) {
  204: 		/* fd events already gone */
  205: 		sc->fam_fcce_ndx = -1;
  206: 
  207: 		FAMClose(&sc->fam);
  208: 	}
  209: #endif
  210: 	free(sc);
  211: }
  212: 
  213: #ifdef HAVE_XATTR
  214: static int stat_cache_attr_get(buffer *buf, char *name) {
  215: 	int attrlen;
  216: 	int ret;
  217: 
  218: 	attrlen = 1024;
  219: 	buffer_prepare_copy(buf, attrlen);
  220: 	attrlen--;
  221: 	if(0 == (ret = attr_get(name, "Content-Type", buf->ptr, &attrlen, 0))) {
  222: 		buf->used = attrlen + 1;
  223: 		buf->ptr[attrlen] = '\0';
  224: 	}
  225: 	return ret;
  226: }
  227: #endif
  228: 
  229: /* the famous DJB hash function for strings */
  230: static uint32_t hashme(buffer *str) {
  231: 	uint32_t hash = 5381;
  232: 	const char *s;
  233: 	for (s = str->ptr; *s; s++) {
  234: 		hash = ((hash << 5) + hash) + *s;
  235: 	}
  236: 
  237: 	hash &= ~(1 << 31); /* strip the highest bit */
  238: 
  239: 	return hash;
  240: }
  241: 
  242: #ifdef HAVE_FAM_H
  243: handler_t stat_cache_handle_fdevent(server *srv, void *_fce, int revent) {
  244: 	size_t i;
  245: 	stat_cache *sc = srv->stat_cache;
  246: 	size_t events;
  247: 
  248: 	UNUSED(_fce);
  249: 	/* */
  250: 
  251: 	if (revent & FDEVENT_IN) {
  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(&sc->fam, node->data);
  291: 						sc->dirs = splaytree_delete(sc->dirs, ndx);
  292: 
  293: 						force_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: 		fdevent_event_del(srv->ev, &(sc->fam_fcce_ndx), FAMCONNECTION_GETFD(&sc->fam));
  306: 		fdevent_unregister(srv->ev, FAMCONNECTION_GETFD(&sc->fam));
  307: 
  308: 		FAMClose(&sc->fam);
  309: 	}
  310: 
  311: 	return HANDLER_GO_ON;
  312: }
  313: 
  314: static int buffer_copy_dirname(buffer *dst, buffer *file) {
  315: 	size_t i;
  316: 
  317: 	if (buffer_is_empty(file)) return -1;
  318: 
  319: 	for (i = file->used - 1; i+1 > 0; i--) {
  320: 		if (file->ptr[i] == '/') {
  321: 			buffer_copy_string_len(dst, file->ptr, i);
  322: 			return 0;
  323: 		}
  324: 	}
  325: 
  326: 	return -1;
  327: }
  328: #endif
  329: 
  330: #ifdef HAVE_LSTAT
  331: static int stat_cache_lstat(server *srv, buffer *dname, struct stat *lst) {
  332: 	if (lstat(dname->ptr, lst) == 0) {
  333: 		return S_ISLNK(lst->st_mode) ? 0 : 1;
  334: 	}
  335: 	else {
  336: 		log_error_write(srv, __FILE__, __LINE__, "sbs",
  337: 				"lstat failed for:",
  338: 				dname, strerror(errno));
  339: 	};
  340: 	return -1;
  341: }
  342: #endif
  343: 
  344: /***
  345:  *
  346:  *
  347:  *
  348:  * returns:
  349:  *  - HANDLER_FINISHED on cache-miss (don't forget to reopen the file)
  350:  *  - HANDLER_ERROR on stat() failed -> see errno for problem
  351:  */
  352: 
  353: handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_cache_entry **ret_sce) {
  354: #ifdef HAVE_FAM_H
  355: 	fam_dir_entry *fam_dir = NULL;
  356: 	int dir_ndx = -1;
  357: 	splay_tree *dir_node = NULL;
  358: #endif
  359: 	stat_cache_entry *sce = NULL;
  360: 	stat_cache *sc;
  361: 	struct stat st;
  362: 	size_t k;
  363: 	int fd;
  364: 	struct stat lst;
  365: #ifdef DEBUG_STAT_CACHE
  366: 	size_t i;
  367: #endif
  368: 
  369: 	int file_ndx;
  370: 	splay_tree *file_node = NULL;
  371: 
  372: 	*ret_sce = NULL;
  373: 
  374: 	/*
  375: 	 * check if the directory for this file has changed
  376: 	 */
  377: 
  378: 	sc = srv->stat_cache;
  379: 
  380: 	buffer_copy_string_buffer(sc->hash_key, name);
  381: 	buffer_append_long(sc->hash_key, con->conf.follow_symlink);
  382: 
  383: 	file_ndx = hashme(sc->hash_key);
  384: 	sc->files = splaytree_splay(sc->files, file_ndx);
  385: 
  386: #ifdef DEBUG_STAT_CACHE
  387: 	for (i = 0; i < ctrl.used; i++) {
  388: 		if (ctrl.ptr[i] == file_ndx) break;
  389: 	}
  390: #endif
  391: 
  392: 	if (sc->files && (sc->files->key == file_ndx)) {
  393: #ifdef DEBUG_STAT_CACHE
  394: 		/* it was in the cache */
  395: 		force_assert(i < ctrl.used);
  396: #endif
  397: 
  398: 		/* we have seen this file already and
  399: 		 * don't stat() it again in the same second */
  400: 
  401: 		file_node = sc->files;
  402: 
  403: 		sce = file_node->data;
  404: 
  405: 		/* check if the name is the same, we might have a collision */
  406: 
  407: 		if (buffer_is_equal(name, sce->name)) {
  408: 			if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_SIMPLE) {
  409: 				if (sce->stat_ts == srv->cur_ts) {
  410: 					*ret_sce = sce;
  411: 					return HANDLER_GO_ON;
  412: 				}
  413: 			}
  414: 		} else {
  415: 			/* oops, a collision,
  416: 			 *
  417: 			 * file_node is used by the FAM check below to see if we know this file
  418: 			 * and if we can save a stat().
  419: 			 *
  420: 			 * BUT, the sce is not reset here as the entry into the cache is ok, we
  421: 			 * it is just not pointing to our requested file.
  422: 			 *
  423: 			 *  */
  424: 
  425: 			file_node = NULL;
  426: 		}
  427: 	} else {
  428: #ifdef DEBUG_STAT_CACHE
  429: 		if (i != ctrl.used) {
  430: 			log_error_write(srv, __FILE__, __LINE__, "xSB",
  431: 				file_ndx, "was already inserted but not found in cache, ", name);
  432: 		}
  433: 		force_assert(i == ctrl.used);
  434: #endif
  435: 	}
  436: 
  437: #ifdef HAVE_FAM_H
  438: 	/* dir-check */
  439: 	if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) {
  440: 		if (0 != buffer_copy_dirname(sc->dir_name, name)) {
  441: 			log_error_write(srv, __FILE__, __LINE__, "sb",
  442: 				"no '/' found in filename:", name);
  443: 			return HANDLER_ERROR;
  444: 		}
  445: 
  446: 		buffer_copy_string_buffer(sc->hash_key, sc->dir_name);
  447: 		buffer_append_long(sc->hash_key, con->conf.follow_symlink);
  448: 
  449: 		dir_ndx = hashme(sc->hash_key);
  450: 
  451: 		sc->dirs = splaytree_splay(sc->dirs, dir_ndx);
  452: 
  453: 		if (sc->dirs && (sc->dirs->key == dir_ndx)) {
  454: 			dir_node = sc->dirs;
  455: 		}
  456: 
  457: 		if (dir_node && file_node) {
  458: 			/* we found a file */
  459: 
  460: 			sce = file_node->data;
  461: 			fam_dir = dir_node->data;
  462: 
  463: 			if (fam_dir->version == sce->dir_version) {
  464: 				/* the stat()-cache entry is still ok */
  465: 
  466: 				*ret_sce = sce;
  467: 				return HANDLER_GO_ON;
  468: 			}
  469: 		}
  470: 	}
  471: #endif
  472: 
  473: 	/*
  474: 	 * *lol*
  475: 	 * - open() + fstat() on a named-pipe results in a (intended) hang.
  476: 	 * - stat() if regular file + open() to see if we can read from it is better
  477: 	 *
  478: 	 * */
  479: 	if (-1 == stat(name->ptr, &st)) {
  480: 		return HANDLER_ERROR;
  481: 	}
  482: 
  483: 
  484: 	if (S_ISREG(st.st_mode)) {
  485: 		/* fix broken stat/open for symlinks to reg files with appended slash on freebsd,osx */
  486: 		if (name->ptr[name->used-2] == '/') {
  487: 			errno = ENOTDIR;
  488: 			return HANDLER_ERROR;
  489: 		}
  490: 
  491: 		/* try to open the file to check if we can read it */
  492: 		if (-1 == (fd = open(name->ptr, O_RDONLY))) {
  493: 			return HANDLER_ERROR;
  494: 		}
  495: 		close(fd);
  496: 	}
  497: 
  498: 	if (NULL == sce) {
  499: #ifdef DEBUG_STAT_CACHE
  500: 		int osize = splaytree_size(sc->files);
  501: #endif
  502: 
  503: 		sce = stat_cache_entry_init();
  504: 		buffer_copy_string_buffer(sce->name, name);
  505: 
  506: 		sc->files = splaytree_insert(sc->files, file_ndx, sce);
  507: #ifdef DEBUG_STAT_CACHE
  508: 		if (ctrl.size == 0) {
  509: 			ctrl.size = 16;
  510: 			ctrl.used = 0;
  511: 			ctrl.ptr = malloc(ctrl.size * sizeof(*ctrl.ptr));
  512: 		} else if (ctrl.size == ctrl.used) {
  513: 			ctrl.size += 16;
  514: 			ctrl.ptr = realloc(ctrl.ptr, ctrl.size * sizeof(*ctrl.ptr));
  515: 		}
  516: 
  517: 		ctrl.ptr[ctrl.used++] = file_ndx;
  518: 
  519: 		force_assert(sc->files);
  520: 		force_assert(sc->files->data == sce);
  521: 		force_assert(osize + 1 == splaytree_size(sc->files));
  522: #endif
  523: 	}
  524: 
  525: 	sce->st = st;
  526: 	sce->stat_ts = srv->cur_ts;
  527: 
  528: 	/* catch the obvious symlinks
  529: 	 *
  530: 	 * this is not a secure check as we still have a race-condition between
  531: 	 * the stat() and the open. We can only solve this by
  532: 	 * 1. open() the file
  533: 	 * 2. fstat() the fd
  534: 	 *
  535: 	 * and keeping the file open for the rest of the time. But this can
  536: 	 * only be done at network level.
  537: 	 *
  538: 	 * per default it is not a symlink
  539: 	 * */
  540: #ifdef HAVE_LSTAT
  541: 	sce->is_symlink = 0;
  542: 
  543: 	/* we want to only check for symlinks if we should block symlinks.
  544: 	 */
  545: 	if (!con->conf.follow_symlink) {
  546: 		if (stat_cache_lstat(srv, name, &lst)  == 0) {
  547: #ifdef DEBUG_STAT_CACHE
  548: 				log_error_write(srv, __FILE__, __LINE__, "sb",
  549: 						"found symlink", name);
  550: #endif
  551: 				sce->is_symlink = 1;
  552: 		}
  553: 
  554: 		/*
  555: 		 * we assume "/" can not be symlink, so
  556: 		 * skip the symlink stuff if our path is /
  557: 		 **/
  558: 		else if ((name->used > 2)) {
  559: 			buffer *dname;
  560: 			char *s_cur;
  561: 
  562: 			dname = buffer_init();
  563: 			buffer_copy_string_buffer(dname, name);
  564: 
  565: 			while ((s_cur = strrchr(dname->ptr,'/'))) {
  566: 				*s_cur = '\0';
  567: 				dname->used = s_cur - dname->ptr + 1;
  568: 				if (dname->ptr == s_cur) {
  569: #ifdef DEBUG_STAT_CACHE
  570: 					log_error_write(srv, __FILE__, __LINE__, "s", "reached /");
  571: #endif
  572: 					break;
  573: 				}
  574: #ifdef DEBUG_STAT_CACHE
  575: 				log_error_write(srv, __FILE__, __LINE__, "sbs",
  576: 						"checking if", dname, "is a symlink");
  577: #endif
  578: 				if (stat_cache_lstat(srv, dname, &lst)  == 0) {
  579: 					sce->is_symlink = 1;
  580: #ifdef DEBUG_STAT_CACHE
  581: 					log_error_write(srv, __FILE__, __LINE__, "sb",
  582: 							"found symlink", dname);
  583: #endif
  584: 					break;
  585: 				};
  586: 			};
  587: 			buffer_free(dname);
  588: 		};
  589: 	};
  590: #endif
  591: 
  592: 	if (S_ISREG(st.st_mode)) {
  593: 		/* determine mimetype */
  594: 		buffer_reset(sce->content_type);
  595: #ifdef HAVE_XATTR
  596: 		if (con->conf.use_xattr) {
  597: 			stat_cache_attr_get(sce->content_type, name->ptr);
  598: 		}
  599: #endif
  600: 		/* xattr did not set a content-type. ask the config */
  601: 		if (buffer_is_empty(sce->content_type)) {
  602: 			for (k = 0; k < con->conf.mimetypes->used; k++) {
  603: 				data_string *ds = (data_string *)con->conf.mimetypes->data[k];
  604: 				buffer *type = ds->key;
  605: 
  606: 				if (type->used == 0) continue;
  607: 
  608: 				/* check if the right side is the same */
  609: 				if (type->used > name->used) continue;
  610: 
  611: 				if (0 == strncasecmp(name->ptr + name->used - type->used, type->ptr, type->used - 1)) {
  612: 					buffer_copy_string_buffer(sce->content_type, ds->value);
  613: 					break;
  614: 				}
  615: 			}
  616: 		}
  617: 		etag_create(sce->etag, &(sce->st), con->etag_flags);
  618: 	} else if (S_ISDIR(st.st_mode)) {
  619: 		etag_create(sce->etag, &(sce->st), con->etag_flags);
  620: 	}
  621: 
  622: #ifdef HAVE_FAM_H
  623: 	if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) {
  624: 		/* is this directory already registered ? */
  625: 		if (!dir_node) {
  626: 			fam_dir = fam_dir_entry_init();
  627: 
  628: 			buffer_copy_string_buffer(fam_dir->name, sc->dir_name);
  629: 
  630: 			fam_dir->version = 1;
  631: 
  632: 			fam_dir->req = calloc(1, sizeof(FAMRequest));
  633: 
  634: 			if (0 != FAMMonitorDirectory(&sc->fam, fam_dir->name->ptr,
  635: 						     fam_dir->req, fam_dir)) {
  636: 
  637: 				log_error_write(srv, __FILE__, __LINE__, "sbsbs",
  638: 						"monitoring dir failed:",
  639: 						fam_dir->name, 
  640: 						"file:", name,
  641: 						FamErrlist[FAMErrno]);
  642: 
  643: 				fam_dir_entry_free(&sc->fam, fam_dir);
  644: 				fam_dir = NULL;
  645: 			} else {
  646: 				int osize = 0;
  647: 
  648: 				if (sc->dirs) {
  649: 					osize = sc->dirs->size;
  650: 				}
  651: 
  652: 				sc->dirs = splaytree_insert(sc->dirs, dir_ndx, fam_dir);
  653: 				force_assert(sc->dirs);
  654: 				force_assert(sc->dirs->data == fam_dir);
  655: 				force_assert(osize == (sc->dirs->size - 1));
  656: 			}
  657: 		} else {
  658: 			fam_dir = dir_node->data;
  659: 		}
  660: 
  661: 		/* bind the fam_fc to the stat() cache entry */
  662: 
  663: 		if (fam_dir) {
  664: 			sce->dir_version = fam_dir->version;
  665: 		}
  666: 	}
  667: #endif
  668: 
  669: 	*ret_sce = sce;
  670: 
  671: 	return HANDLER_GO_ON;
  672: }
  673: 
  674: /**
  675:  * remove stat() from cache which havn't been stat()ed for
  676:  * more than 10 seconds
  677:  *
  678:  *
  679:  * walk though the stat-cache, collect the ids which are too old
  680:  * and remove them in a second loop
  681:  */
  682: 
  683: static int stat_cache_tag_old_entries(server *srv, splay_tree *t, int *keys, size_t *ndx) {
  684: 	stat_cache_entry *sce;
  685: 
  686: 	if (!t) return 0;
  687: 
  688: 	stat_cache_tag_old_entries(srv, t->left, keys, ndx);
  689: 	stat_cache_tag_old_entries(srv, t->right, keys, ndx);
  690: 
  691: 	sce = t->data;
  692: 
  693: 	if (srv->cur_ts - sce->stat_ts > 2) {
  694: 		keys[(*ndx)++] = t->key;
  695: 	}
  696: 
  697: 	return 0;
  698: }
  699: 
  700: int stat_cache_trigger_cleanup(server *srv) {
  701: 	stat_cache *sc;
  702: 	size_t max_ndx = 0, i;
  703: 	int *keys;
  704: 
  705: 	sc = srv->stat_cache;
  706: 
  707: 	if (!sc->files) return 0;
  708: 
  709: 	keys = calloc(1, sizeof(int) * sc->files->size);
  710: 
  711: 	stat_cache_tag_old_entries(srv, sc->files, keys, &max_ndx);
  712: 
  713: 	for (i = 0; i < max_ndx; i++) {
  714: 		int ndx = keys[i];
  715: 		splay_tree *node;
  716: 
  717: 		sc->files = splaytree_splay(sc->files, ndx);
  718: 
  719: 		node = sc->files;
  720: 
  721: 		if (node && (node->key == ndx)) {
  722: #ifdef DEBUG_STAT_CACHE
  723: 			size_t j;
  724: 			int osize = splaytree_size(sc->files);
  725: 			stat_cache_entry *sce = node->data;
  726: #endif
  727: 			stat_cache_entry_free(node->data);
  728: 			sc->files = splaytree_delete(sc->files, ndx);
  729: 
  730: #ifdef DEBUG_STAT_CACHE
  731: 			for (j = 0; j < ctrl.used; j++) {
  732: 				if (ctrl.ptr[j] == ndx) {
  733: 					ctrl.ptr[j] = ctrl.ptr[--ctrl.used];
  734: 					break;
  735: 				}
  736: 			}
  737: 
  738: 			force_assert(osize - 1 == splaytree_size(sc->files));
  739: #endif
  740: 		}
  741: 	}
  742: 
  743: 	free(keys);
  744: 
  745: 	return 0;
  746: }

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