File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / lighttpd / src / stat_cache.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Oct 14 10:32:48 2013 UTC (10 years, 8 months ago) by misho
Branches: lighttpd, MAIN
CVS tags: v1_4_33, HEAD
1.4.33

    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>