18abd06a7SGlenn Strauss #include "first.h" 28abd06a7SGlenn Strauss 322e8b456SStefan Bühler #include "log.h" 422e8b456SStefan Bühler #include "stat_cache.h" 522e8b456SStefan Bühler #include "fdevent.h" 622e8b456SStefan Bühler #include "etag.h" 7e1164797SGlenn Strauss #include "splaytree.h" 85e134da0SJan Kneschke 95e134da0SJan Kneschke #include <sys/types.h> 105e134da0SJan Kneschke #include <sys/stat.h> 115e134da0SJan Kneschke 125e134da0SJan Kneschke #include <stdlib.h> 135e134da0SJan Kneschke #include <string.h> 145e134da0SJan Kneschke #include <errno.h> 155e134da0SJan Kneschke #include <unistd.h> 165e134da0SJan Kneschke #include <fcntl.h> 175e134da0SJan Kneschke 185e134da0SJan Kneschke #ifdef HAVE_ATTR_ATTRIBUTES_H 195e134da0SJan Kneschke # include <attr/attributes.h> 205e134da0SJan Kneschke #endif 215e134da0SJan Kneschke 224d55d4adSMoritz Wilhelmy #ifdef HAVE_SYS_EXTATTR_H 234d55d4adSMoritz Wilhelmy # include <sys/extattr.h> 244d55d4adSMoritz Wilhelmy #endif 254d55d4adSMoritz Wilhelmy 265e134da0SJan Kneschke #ifdef HAVE_FAM_H 275e134da0SJan Kneschke # include <fam.h> 285e134da0SJan Kneschke #endif 295e134da0SJan Kneschke 305e134da0SJan Kneschke #ifndef HAVE_LSTAT 315e134da0SJan Kneschke # define lstat stat 325e134da0SJan Kneschke #endif 335e134da0SJan Kneschke 340f190e16SMarcus Rückert #if 0 35b795fd36SJan Kneschke /* enables debug code for testing if all nodes in the stat-cache as accessable */ 36b795fd36SJan Kneschke #define DEBUG_STAT_CACHE 37b795fd36SJan Kneschke #endif 38b795fd36SJan Kneschke 395e134da0SJan Kneschke /* 405e134da0SJan Kneschke * stat-cache 415e134da0SJan Kneschke * 425e134da0SJan Kneschke * we cache the stat() calls in our own storage 435e134da0SJan Kneschke * the directories are cached in FAM 445e134da0SJan Kneschke * 455e134da0SJan Kneschke * if we get a change-event from FAM, we increment the version in the FAM->dir mapping 465e134da0SJan Kneschke * 475e134da0SJan Kneschke * if the stat()-cache is queried we check if the version id for the directory is the 485e134da0SJan Kneschke * same and return immediatly. 495e134da0SJan Kneschke * 505e134da0SJan Kneschke * 515e134da0SJan Kneschke * What we need: 525e134da0SJan Kneschke * 535e134da0SJan Kneschke * - for each stat-cache entry we need a fast indirect lookup on the directory name 545e134da0SJan Kneschke * - for each FAMRequest we have to find the version in the directory cache (index as userdata) 555e134da0SJan Kneschke * 565e134da0SJan Kneschke * stat <<-> directory <-> FAMRequest 575e134da0SJan Kneschke * 585e134da0SJan Kneschke * if file is deleted, directory is dirty, file is rechecked ... 595e134da0SJan Kneschke * if directory is deleted, directory mapping is removed 605e134da0SJan Kneschke * 615e134da0SJan Kneschke * */ 625e134da0SJan Kneschke 635e134da0SJan Kneschke #ifdef HAVE_FAM_H 645e134da0SJan Kneschke typedef struct { 655e134da0SJan Kneschke FAMRequest *req; 665e134da0SJan Kneschke 675e134da0SJan Kneschke buffer *name; 685e134da0SJan Kneschke 695e134da0SJan Kneschke int version; 705e134da0SJan Kneschke } fam_dir_entry; 715e134da0SJan Kneschke #endif 725e134da0SJan Kneschke 735e134da0SJan Kneschke /* the directory name is too long to always compare on it 745e134da0SJan Kneschke * - we need a hash 755e134da0SJan Kneschke * - the hash-key is used as sorting criteria for a tree 765e134da0SJan Kneschke * - a splay-tree is used as we can use the caching effect of it 775e134da0SJan Kneschke */ 785e134da0SJan Kneschke 795e134da0SJan Kneschke /* we want to cleanup the stat-cache every few seconds, let's say 10 805e134da0SJan Kneschke * 815e134da0SJan Kneschke * - remove entries which are outdated since 30s 825e134da0SJan Kneschke * - remove entries which are fresh but havn't been used since 60s 835e134da0SJan Kneschke * - if we don't have a stat-cache entry for a directory, release it from the monitor 845e134da0SJan Kneschke */ 855e134da0SJan Kneschke 86b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 87b795fd36SJan Kneschke typedef struct { 88b795fd36SJan Kneschke int *ptr; 89b795fd36SJan Kneschke 90b795fd36SJan Kneschke size_t used; 91b795fd36SJan Kneschke size_t size; 92b795fd36SJan Kneschke } fake_keys; 93b795fd36SJan Kneschke 94b795fd36SJan Kneschke static fake_keys ctrl; 95b795fd36SJan Kneschke #endif 96b795fd36SJan Kneschke 97e1164797SGlenn Strauss typedef struct stat_cache { 98e1164797SGlenn Strauss splay_tree *files; /* the nodes of the tree are stat_cache_entry's */ 99e1164797SGlenn Strauss 100e1164797SGlenn Strauss buffer *dir_name; /* for building the dirname from the filename */ 101e1164797SGlenn Strauss #ifdef HAVE_FAM_H 102e1164797SGlenn Strauss splay_tree *dirs; /* the nodes of the tree are fam_dir_entry */ 103e1164797SGlenn Strauss 104e1164797SGlenn Strauss FAMConnection fam; 105e1164797SGlenn Strauss int fam_fcce_ndx; 106e1164797SGlenn Strauss #endif 107e1164797SGlenn Strauss buffer *hash_key; /* temp-store for the hash-key */ 108e1164797SGlenn Strauss } stat_cache; 109e1164797SGlenn Strauss 110e1164797SGlenn Strauss static handler_t stat_cache_handle_fdevent(server *srv, void *_fce, int revent); 111e1164797SGlenn Strauss 112e1164797SGlenn Strauss stat_cache *stat_cache_init(server *srv) { 113d8b363c1SStefan Bühler stat_cache *sc = NULL; 1145e134da0SJan Kneschke 115d8b363c1SStefan Bühler sc = calloc(1, sizeof(*sc)); 116566cf8deSStefan Bühler force_assert(NULL != sc); 1175e134da0SJan Kneschke 118d8b363c1SStefan Bühler sc->dir_name = buffer_init(); 119d8b363c1SStefan Bühler sc->hash_key = buffer_init(); 120d8b363c1SStefan Bühler 1215e134da0SJan Kneschke #ifdef HAVE_FAM_H 122d8b363c1SStefan Bühler sc->fam_fcce_ndx = -1; 12379d3bc44SJan Kneschke #endif 1245e134da0SJan Kneschke 125b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 126b795fd36SJan Kneschke ctrl.size = 0; 127b795fd36SJan Kneschke #endif 128b795fd36SJan Kneschke 129e1164797SGlenn Strauss #ifdef HAVE_FAM_H 130e1164797SGlenn Strauss /* setup FAM */ 131e1164797SGlenn Strauss if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { 1328e97c84cSGlenn Strauss if (0 != FAMOpen2(&sc->fam, "lighttpd")) { 133e1164797SGlenn Strauss log_error_write(srv, __FILE__, __LINE__, "s", 134e1164797SGlenn Strauss "could not open a fam connection, dieing."); 1358e97c84cSGlenn Strauss free(sc); 136e1164797SGlenn Strauss return NULL; 137e1164797SGlenn Strauss } 138e1164797SGlenn Strauss #ifdef HAVE_FAMNOEXISTS 1398e97c84cSGlenn Strauss FAMNoExists(&sc->fam); 140e1164797SGlenn Strauss #endif 141e1164797SGlenn Strauss 1428e97c84cSGlenn Strauss fd_close_on_exec(FAMCONNECTION_GETFD(&sc->fam)); 1438e97c84cSGlenn Strauss fdevent_register(srv->ev, FAMCONNECTION_GETFD(&sc->fam), stat_cache_handle_fdevent, NULL); 1448e97c84cSGlenn Strauss fdevent_event_set(srv->ev, &(sc->fam_fcce_ndx), FAMCONNECTION_GETFD(&sc->fam), FDEVENT_IN); 145e1164797SGlenn Strauss } 146e1164797SGlenn Strauss #endif 147e1164797SGlenn Strauss 148d8b363c1SStefan Bühler return sc; 1495e134da0SJan Kneschke } 1505e134da0SJan Kneschke 1515e134da0SJan Kneschke static stat_cache_entry * stat_cache_entry_init(void) { 1525e134da0SJan Kneschke stat_cache_entry *sce = NULL; 1535e134da0SJan Kneschke 1545e134da0SJan Kneschke sce = calloc(1, sizeof(*sce)); 155566cf8deSStefan Bühler force_assert(NULL != sce); 1565e134da0SJan Kneschke 1575e134da0SJan Kneschke sce->name = buffer_init(); 1585e134da0SJan Kneschke sce->etag = buffer_init(); 1595e134da0SJan Kneschke sce->content_type = buffer_init(); 1605e134da0SJan Kneschke 1615e134da0SJan Kneschke return sce; 1625e134da0SJan Kneschke } 1635e134da0SJan Kneschke 1645e134da0SJan Kneschke static void stat_cache_entry_free(void *data) { 1655e134da0SJan Kneschke stat_cache_entry *sce = data; 1665e134da0SJan Kneschke if (!sce) return; 1675e134da0SJan Kneschke 1685e134da0SJan Kneschke buffer_free(sce->etag); 1695e134da0SJan Kneschke buffer_free(sce->name); 1705e134da0SJan Kneschke buffer_free(sce->content_type); 1715e134da0SJan Kneschke 1725e134da0SJan Kneschke free(sce); 1735e134da0SJan Kneschke } 1745e134da0SJan Kneschke 1755e134da0SJan Kneschke #ifdef HAVE_FAM_H 1765e134da0SJan Kneschke static fam_dir_entry * fam_dir_entry_init(void) { 1775e134da0SJan Kneschke fam_dir_entry *fam_dir = NULL; 1785e134da0SJan Kneschke 1795e134da0SJan Kneschke fam_dir = calloc(1, sizeof(*fam_dir)); 180566cf8deSStefan Bühler force_assert(NULL != fam_dir); 1815e134da0SJan Kneschke 1825e134da0SJan Kneschke fam_dir->name = buffer_init(); 1835e134da0SJan Kneschke 1845e134da0SJan Kneschke return fam_dir; 1855e134da0SJan Kneschke } 1865e134da0SJan Kneschke 187d8b363c1SStefan Bühler static void fam_dir_entry_free(FAMConnection *fc, void *data) { 1885e134da0SJan Kneschke fam_dir_entry *fam_dir = data; 1895e134da0SJan Kneschke 1905e134da0SJan Kneschke if (!fam_dir) return; 1915e134da0SJan Kneschke 192d8b363c1SStefan Bühler FAMCancelMonitor(fc, fam_dir->req); 1935e134da0SJan Kneschke 1945e134da0SJan Kneschke buffer_free(fam_dir->name); 1955e134da0SJan Kneschke free(fam_dir->req); 1965e134da0SJan Kneschke 1975e134da0SJan Kneschke free(fam_dir); 1985e134da0SJan Kneschke } 1995e134da0SJan Kneschke #endif 2005e134da0SJan Kneschke 201d1e70da8SJan Kneschke void stat_cache_free(stat_cache *sc) { 202d1e70da8SJan Kneschke while (sc->files) { 203ecb30c4eSJan Kneschke int osize; 204d1e70da8SJan Kneschke splay_tree *node = sc->files; 2055e134da0SJan Kneschke 206ecb30c4eSJan Kneschke osize = sc->files->size; 207ecb30c4eSJan Kneschke 208d1e70da8SJan Kneschke stat_cache_entry_free(node->data); 209d1e70da8SJan Kneschke sc->files = splaytree_delete(sc->files, node->key); 210d1e70da8SJan Kneschke 21107dd0bd0SStefan Bühler force_assert(osize - 1 == splaytree_size(sc->files)); 212d1e70da8SJan Kneschke } 213d1e70da8SJan Kneschke 214d1e70da8SJan Kneschke buffer_free(sc->dir_name); 2159de38074SJan Kneschke buffer_free(sc->hash_key); 2165e134da0SJan Kneschke 2175e134da0SJan Kneschke #ifdef HAVE_FAM_H 218d1e70da8SJan Kneschke while (sc->dirs) { 219ecb30c4eSJan Kneschke int osize; 220d1e70da8SJan Kneschke splay_tree *node = sc->dirs; 2215e134da0SJan Kneschke 222ecb30c4eSJan Kneschke osize = sc->dirs->size; 223ecb30c4eSJan Kneschke 224d8b363c1SStefan Bühler fam_dir_entry_free(&sc->fam, node->data); 225d1e70da8SJan Kneschke sc->dirs = splaytree_delete(sc->dirs, node->key); 226d1e70da8SJan Kneschke 227ecb30c4eSJan Kneschke if (osize == 1) { 22807dd0bd0SStefan Bühler force_assert(NULL == sc->dirs); 229ecb30c4eSJan Kneschke } else { 23007dd0bd0SStefan Bühler force_assert(osize == (sc->dirs->size + 1)); 231ecb30c4eSJan Kneschke } 232d1e70da8SJan Kneschke } 233d1e70da8SJan Kneschke 234d8b363c1SStefan Bühler if (-1 != sc->fam_fcce_ndx) { 235d8b363c1SStefan Bühler /* fd events already gone */ 236d8b363c1SStefan Bühler sc->fam_fcce_ndx = -1; 237d8b363c1SStefan Bühler 238d8b363c1SStefan Bühler FAMClose(&sc->fam); 23979d3bc44SJan Kneschke } 2405e134da0SJan Kneschke #endif 241d1e70da8SJan Kneschke free(sc); 2425e134da0SJan Kneschke } 2435e134da0SJan Kneschke 2444d55d4adSMoritz Wilhelmy #if defined(HAVE_XATTR) 245d17d48e0SGlenn Strauss static int stat_cache_attr_get(buffer *buf, char *name, char *xattrname) { 2465e134da0SJan Kneschke int attrlen; 2475e134da0SJan Kneschke int ret; 2485e134da0SJan Kneschke 2494365bdbeSStefan Bühler buffer_string_prepare_copy(buf, 1023); 2506afad87dSStefan Bühler attrlen = buf->size - 1; 251d17d48e0SGlenn Strauss if(0 == (ret = attr_get(name, xattrname, buf->ptr, &attrlen, 0))) { 252ad3e93eaSStefan Bühler buffer_commit(buf, attrlen); 2535e134da0SJan Kneschke } 2545e134da0SJan Kneschke return ret; 2555e134da0SJan Kneschke } 2564d55d4adSMoritz Wilhelmy #elif defined(HAVE_EXTATTR) 257d17d48e0SGlenn Strauss static int stat_cache_attr_get(buffer *buf, char *name, char *xattrname) { 2584365bdbeSStefan Bühler ssize_t attrlen; 2594d55d4adSMoritz Wilhelmy 2609257d7dfSStefan Bühler buffer_string_prepare_copy(buf, 1023); 2614d55d4adSMoritz Wilhelmy 262d17d48e0SGlenn Strauss if (-1 != (attrlen = extattr_get_file(name, EXTATTR_NAMESPACE_USER, xattrname, buf->ptr, buf->size - 1))) { 2634d55d4adSMoritz Wilhelmy buf->used = attrlen + 1; 2644d55d4adSMoritz Wilhelmy buf->ptr[attrlen] = '\0'; 2654d55d4adSMoritz Wilhelmy return 0; 2664d55d4adSMoritz Wilhelmy } 2674d55d4adSMoritz Wilhelmy return -1; 2684d55d4adSMoritz Wilhelmy } 2695e134da0SJan Kneschke #endif 2705e134da0SJan Kneschke 2710cc7556aSGlenn Strauss const buffer * stat_cache_mimetype_by_ext(const connection *con, const char *name, size_t nlen) 2720cc7556aSGlenn Strauss { 2730cc7556aSGlenn Strauss const char *end = name + nlen; /*(end of string)*/ 2740cc7556aSGlenn Strauss const size_t used = con->conf.mimetypes->used; 2750cc7556aSGlenn Strauss if (used < 16) { 2760cc7556aSGlenn Strauss for (size_t i = 0; i < used; ++i) { 2770cc7556aSGlenn Strauss /* suffix match */ 2780cc7556aSGlenn Strauss const data_string *ds = (data_string *)con->conf.mimetypes->data[i]; 2790cc7556aSGlenn Strauss const size_t klen = buffer_string_length(ds->key); 2800cc7556aSGlenn Strauss if (klen <= nlen && 0 == strncasecmp(end-klen, ds->key->ptr, klen)) 2810cc7556aSGlenn Strauss return ds->value; 2820cc7556aSGlenn Strauss } 2830cc7556aSGlenn Strauss } 2840cc7556aSGlenn Strauss else { 2850cc7556aSGlenn Strauss const char *s; 2860cc7556aSGlenn Strauss const data_string *ds; 2870cc7556aSGlenn Strauss if (nlen) { 2880cc7556aSGlenn Strauss for (s = end-1; s != name && *s != '/'; --s) ; /*(like memrchr())*/ 2890cc7556aSGlenn Strauss if (*s == '/') ++s; 2900cc7556aSGlenn Strauss } 2910cc7556aSGlenn Strauss else { 2920cc7556aSGlenn Strauss s = name; 2930cc7556aSGlenn Strauss } 2940cc7556aSGlenn Strauss /* search for basename, then longest .ext2.ext1, then .ext1, then "" */ 295*86bb8be2SGlenn Strauss ds = (data_string *)array_get_element_klen(con->conf.mimetypes, s, end - s); 2960cc7556aSGlenn Strauss if (NULL != ds) return ds->value; 2970cc7556aSGlenn Strauss while (++s < end) { 2980cc7556aSGlenn Strauss while (*s != '.' && ++s != end) ; 2999e46b8eaSGlenn Strauss if (s == end) break; 3009e46b8eaSGlenn Strauss /* search ".ext" then "ext" */ 301*86bb8be2SGlenn Strauss ds = (data_string *)array_get_element_klen(con->conf.mimetypes, s, end - s); 3029e46b8eaSGlenn Strauss if (NULL != ds) return ds->value; 3039e46b8eaSGlenn Strauss /* repeat search without leading '.' to handle situation where 3049e46b8eaSGlenn Strauss * admin configured mimetype.assign keys without leading '.' */ 3059e46b8eaSGlenn Strauss if (++s < end) { 3069e46b8eaSGlenn Strauss if (*s == '.') { --s; continue; } 307*86bb8be2SGlenn Strauss ds = (data_string *)array_get_element_klen(con->conf.mimetypes, s, end - s); 3080cc7556aSGlenn Strauss if (NULL != ds) return ds->value; 3090cc7556aSGlenn Strauss } 3100cc7556aSGlenn Strauss } 3119e46b8eaSGlenn Strauss /* search for ""; catchall */ 3129e46b8eaSGlenn Strauss ds = (data_string *)array_get_element(con->conf.mimetypes, ""); 3139e46b8eaSGlenn Strauss if (NULL != ds) return ds->value; 3149e46b8eaSGlenn Strauss } 3150cc7556aSGlenn Strauss 3160cc7556aSGlenn Strauss return NULL; 3170cc7556aSGlenn Strauss } 3180cc7556aSGlenn Strauss 319b795fd36SJan Kneschke /* the famous DJB hash function for strings */ 320b795fd36SJan Kneschke static uint32_t hashme(buffer *str) { 321b795fd36SJan Kneschke uint32_t hash = 5381; 3225e134da0SJan Kneschke const char *s; 3235e134da0SJan Kneschke for (s = str->ptr; *s; s++) { 324b795fd36SJan Kneschke hash = ((hash << 5) + hash) + *s; 3255e134da0SJan Kneschke } 3265e134da0SJan Kneschke 327fa8b1546SStefan Bühler hash &= ~(((uint32_t)1) << 31); /* strip the highest bit */ 328b795fd36SJan Kneschke 3295e134da0SJan Kneschke return hash; 3305e134da0SJan Kneschke } 3315e134da0SJan Kneschke 3325e134da0SJan Kneschke #ifdef HAVE_FAM_H 333e1164797SGlenn Strauss static handler_t stat_cache_handle_fdevent(server *srv, void *_fce, int revent) { 3345e134da0SJan Kneschke size_t i; 3355e134da0SJan Kneschke stat_cache *sc = srv->stat_cache; 3365e134da0SJan Kneschke size_t events; 3375e134da0SJan Kneschke 33856f637b8SJan Kneschke UNUSED(_fce); 3395e134da0SJan Kneschke /* */ 3405e134da0SJan Kneschke 341d8b363c1SStefan Bühler if (revent & FDEVENT_IN) { 342d8b363c1SStefan Bühler events = FAMPending(&sc->fam); 3435e134da0SJan Kneschke 3445e134da0SJan Kneschke for (i = 0; i < events; i++) { 3455e134da0SJan Kneschke FAMEvent fe; 3465e134da0SJan Kneschke fam_dir_entry *fam_dir; 3475e134da0SJan Kneschke splay_tree *node; 3489de38074SJan Kneschke int ndx, j; 3495e134da0SJan Kneschke 350d8b363c1SStefan Bühler FAMNextEvent(&sc->fam, &fe); 3515e134da0SJan Kneschke 3525e134da0SJan Kneschke /* handle event */ 3535e134da0SJan Kneschke 3545e134da0SJan Kneschke switch(fe.code) { 3555e134da0SJan Kneschke case FAMChanged: 3565e134da0SJan Kneschke case FAMDeleted: 3575e134da0SJan Kneschke case FAMMoved: 3585e134da0SJan Kneschke /* if the filename is a directory remove the entry */ 3595e134da0SJan Kneschke 3605e134da0SJan Kneschke fam_dir = fe.userdata; 3615e134da0SJan Kneschke fam_dir->version++; 3625e134da0SJan Kneschke 3635e134da0SJan Kneschke /* file/dir is still here */ 3645e134da0SJan Kneschke if (fe.code == FAMChanged) break; 3655e134da0SJan Kneschke 3669de38074SJan Kneschke /* we have 2 versions, follow and no-follow-symlink */ 3675e134da0SJan Kneschke 3689de38074SJan Kneschke for (j = 0; j < 2; j++) { 3699de38074SJan Kneschke buffer_copy_string(sc->hash_key, fe.filename); 3706afad87dSStefan Bühler buffer_append_int(sc->hash_key, j); 3719de38074SJan Kneschke 3729de38074SJan Kneschke ndx = hashme(sc->hash_key); 3735e134da0SJan Kneschke 3745e134da0SJan Kneschke sc->dirs = splaytree_splay(sc->dirs, ndx); 3755e134da0SJan Kneschke node = sc->dirs; 3765e134da0SJan Kneschke 3775e134da0SJan Kneschke if (node && (node->key == ndx)) { 378b795fd36SJan Kneschke int osize = splaytree_size(sc->dirs); 379d1e70da8SJan Kneschke 380d8b363c1SStefan Bühler fam_dir_entry_free(&sc->fam, node->data); 3815e134da0SJan Kneschke sc->dirs = splaytree_delete(sc->dirs, ndx); 382d1e70da8SJan Kneschke 38307dd0bd0SStefan Bühler force_assert(osize - 1 == splaytree_size(sc->dirs)); 3845e134da0SJan Kneschke } 3859de38074SJan Kneschke } 3865e134da0SJan Kneschke break; 3875e134da0SJan Kneschke default: 3885e134da0SJan Kneschke break; 3895e134da0SJan Kneschke } 3905e134da0SJan Kneschke } 39179d3bc44SJan Kneschke } 39279d3bc44SJan Kneschke 39379d3bc44SJan Kneschke if (revent & FDEVENT_HUP) { 39479d3bc44SJan Kneschke /* fam closed the connection */ 395d8b363c1SStefan Bühler fdevent_event_del(srv->ev, &(sc->fam_fcce_ndx), FAMCONNECTION_GETFD(&sc->fam)); 396d8b363c1SStefan Bühler fdevent_unregister(srv->ev, FAMCONNECTION_GETFD(&sc->fam)); 39779d3bc44SJan Kneschke 398d8b363c1SStefan Bühler FAMClose(&sc->fam); 39979d3bc44SJan Kneschke } 4005e134da0SJan Kneschke 4015e134da0SJan Kneschke return HANDLER_GO_ON; 4025e134da0SJan Kneschke } 4035e134da0SJan Kneschke 4045e134da0SJan Kneschke static int buffer_copy_dirname(buffer *dst, buffer *file) { 4055e134da0SJan Kneschke size_t i; 4065e134da0SJan Kneschke 4076afad87dSStefan Bühler if (buffer_string_is_empty(file)) return -1; 4085e134da0SJan Kneschke 409ad3e93eaSStefan Bühler for (i = buffer_string_length(file); i > 0; i--) { 4105e134da0SJan Kneschke if (file->ptr[i] == '/') { 4115e134da0SJan Kneschke buffer_copy_string_len(dst, file->ptr, i); 4125e134da0SJan Kneschke return 0; 4135e134da0SJan Kneschke } 4145e134da0SJan Kneschke } 4155e134da0SJan Kneschke 4165e134da0SJan Kneschke return -1; 4175e134da0SJan Kneschke } 4185e134da0SJan Kneschke #endif 4195e134da0SJan Kneschke 420657a024dSMarcus Rückert #ifdef HAVE_LSTAT 42108994e47SJan Kneschke static int stat_cache_lstat(server *srv, buffer *dname, struct stat *lst) { 42208994e47SJan Kneschke if (lstat(dname->ptr, lst) == 0) { 423657a024dSMarcus Rückert return S_ISLNK(lst->st_mode) ? 0 : 1; 424657a024dSMarcus Rückert } 425657a024dSMarcus Rückert else { 42608994e47SJan Kneschke log_error_write(srv, __FILE__, __LINE__, "sbs", 427657a024dSMarcus Rückert "lstat failed for:", 428657a024dSMarcus Rückert dname, strerror(errno)); 429657a024dSMarcus Rückert }; 430657a024dSMarcus Rückert return -1; 431657a024dSMarcus Rückert } 432657a024dSMarcus Rückert #endif 433657a024dSMarcus Rückert 4345e134da0SJan Kneschke /*** 4355e134da0SJan Kneschke * 4365e134da0SJan Kneschke * 4375e134da0SJan Kneschke * 4385e134da0SJan Kneschke * returns: 4395e134da0SJan Kneschke * - HANDLER_FINISHED on cache-miss (don't forget to reopen the file) 4405e134da0SJan Kneschke * - HANDLER_ERROR on stat() failed -> see errno for problem 4415e134da0SJan Kneschke */ 4425e134da0SJan Kneschke 4435e134da0SJan Kneschke handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_cache_entry **ret_sce) { 4445e134da0SJan Kneschke #ifdef HAVE_FAM_H 4455e134da0SJan Kneschke fam_dir_entry *fam_dir = NULL; 4465e134da0SJan Kneschke int dir_ndx = -1; 4475e134da0SJan Kneschke #endif 4485e134da0SJan Kneschke stat_cache_entry *sce = NULL; 4495e134da0SJan Kneschke stat_cache *sc; 4505e134da0SJan Kneschke struct stat st; 4517d875859SJan Kneschke int fd; 45208994e47SJan Kneschke struct stat lst; 45356f637b8SJan Kneschke #ifdef DEBUG_STAT_CACHE 454b795fd36SJan Kneschke size_t i; 45556f637b8SJan Kneschke #endif 4565e134da0SJan Kneschke 4575e134da0SJan Kneschke int file_ndx; 4585e134da0SJan Kneschke 4595e134da0SJan Kneschke *ret_sce = NULL; 4605e134da0SJan Kneschke 4615e134da0SJan Kneschke /* 4625e134da0SJan Kneschke * check if the directory for this file has changed 4635e134da0SJan Kneschke */ 4645e134da0SJan Kneschke 4655e134da0SJan Kneschke sc = srv->stat_cache; 4665e134da0SJan Kneschke 4676afad87dSStefan Bühler buffer_copy_buffer(sc->hash_key, name); 4686afad87dSStefan Bühler buffer_append_int(sc->hash_key, con->conf.follow_symlink); 4699de38074SJan Kneschke 4709de38074SJan Kneschke file_ndx = hashme(sc->hash_key); 4715e134da0SJan Kneschke sc->files = splaytree_splay(sc->files, file_ndx); 4725e134da0SJan Kneschke 473b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 474b795fd36SJan Kneschke for (i = 0; i < ctrl.used; i++) { 475b795fd36SJan Kneschke if (ctrl.ptr[i] == file_ndx) break; 476b795fd36SJan Kneschke } 477b795fd36SJan Kneschke #endif 478b795fd36SJan Kneschke 4795e134da0SJan Kneschke if (sc->files && (sc->files->key == file_ndx)) { 480b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 481b795fd36SJan Kneschke /* it was in the cache */ 48207dd0bd0SStefan Bühler force_assert(i < ctrl.used); 483b795fd36SJan Kneschke #endif 484b795fd36SJan Kneschke 4855e134da0SJan Kneschke /* we have seen this file already and 4865e134da0SJan Kneschke * don't stat() it again in the same second */ 4875e134da0SJan Kneschke 48869f890e2SStefan Bühler sce = sc->files->data; 4895e134da0SJan Kneschke 490ecb30c4eSJan Kneschke /* check if the name is the same, we might have a collision */ 491ecb30c4eSJan Kneschke 492ecb30c4eSJan Kneschke if (buffer_is_equal(name, sce->name)) { 4935e134da0SJan Kneschke if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_SIMPLE) { 494acd5e450SGlenn Strauss if (sce->stat_ts == srv->cur_ts && con->conf.follow_symlink) { 4955e134da0SJan Kneschke *ret_sce = sce; 4965e134da0SJan Kneschke return HANDLER_GO_ON; 4975e134da0SJan Kneschke } 4985e134da0SJan Kneschke } 499ecb30c4eSJan Kneschke } else { 50069f890e2SStefan Bühler /* collision, forget about the entry */ 50169f890e2SStefan Bühler sce = NULL; 502ecb30c4eSJan Kneschke } 503b795fd36SJan Kneschke } else { 504b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 505b795fd36SJan Kneschke if (i != ctrl.used) { 50638f2d1ddSStefan Bühler log_error_write(srv, __FILE__, __LINE__, "xSB", 50738f2d1ddSStefan Bühler file_ndx, "was already inserted but not found in cache, ", name); 508b795fd36SJan Kneschke } 50907dd0bd0SStefan Bühler force_assert(i == ctrl.used); 510b795fd36SJan Kneschke #endif 5115e134da0SJan Kneschke } 5125e134da0SJan Kneschke 5135e134da0SJan Kneschke #ifdef HAVE_FAM_H 5145e134da0SJan Kneschke /* dir-check */ 5155e134da0SJan Kneschke if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { 5165e134da0SJan Kneschke if (0 != buffer_copy_dirname(sc->dir_name, name)) { 5177a64f2f8SStefan Bühler log_error_write(srv, __FILE__, __LINE__, "sb", 5187a64f2f8SStefan Bühler "no '/' found in filename:", name); 5197a64f2f8SStefan Bühler return HANDLER_ERROR; 5205e134da0SJan Kneschke } 5215e134da0SJan Kneschke 5226afad87dSStefan Bühler buffer_copy_buffer(sc->hash_key, sc->dir_name); 5236afad87dSStefan Bühler buffer_append_int(sc->hash_key, con->conf.follow_symlink); 5249de38074SJan Kneschke 5259de38074SJan Kneschke dir_ndx = hashme(sc->hash_key); 5265e134da0SJan Kneschke 5275e134da0SJan Kneschke sc->dirs = splaytree_splay(sc->dirs, dir_ndx); 5285e134da0SJan Kneschke 52969f890e2SStefan Bühler if ((NULL != sc->dirs) && (sc->dirs->key == dir_ndx)) { 53069f890e2SStefan Bühler fam_dir = sc->dirs->data; 5315e134da0SJan Kneschke 53269f890e2SStefan Bühler /* check whether we got a collision */ 53369f890e2SStefan Bühler if (buffer_is_equal(sc->dir_name, fam_dir->name)) { 53469f890e2SStefan Bühler /* test whether a found file cache entry is still ok */ 53569f890e2SStefan Bühler if ((NULL != sce) && (fam_dir->version == sce->dir_version)) { 5365e134da0SJan Kneschke /* the stat()-cache entry is still ok */ 5375e134da0SJan Kneschke 5385e134da0SJan Kneschke *ret_sce = sce; 5395e134da0SJan Kneschke return HANDLER_GO_ON; 5405e134da0SJan Kneschke } 54169f890e2SStefan Bühler } else { 54269f890e2SStefan Bühler /* hash collision, forget about the entry */ 54369f890e2SStefan Bühler fam_dir = NULL; 54469f890e2SStefan Bühler } 5455e134da0SJan Kneschke } 5465e134da0SJan Kneschke } 5475e134da0SJan Kneschke #endif 548478cb34bSJan Kneschke 549478cb34bSJan Kneschke /* 550478cb34bSJan Kneschke * *lol* 551478cb34bSJan Kneschke * - open() + fstat() on a named-pipe results in a (intended) hang. 552657a024dSMarcus Rückert * - stat() if regular file + open() to see if we can read from it is better 553478cb34bSJan Kneschke * 554478cb34bSJan Kneschke * */ 555478cb34bSJan Kneschke if (-1 == stat(name->ptr, &st)) { 556478cb34bSJan Kneschke return HANDLER_ERROR; 557478cb34bSJan Kneschke } 558478cb34bSJan Kneschke 559478cb34bSJan Kneschke 560478cb34bSJan Kneschke if (S_ISREG(st.st_mode)) { 56157066345SStefan Bühler /* fix broken stat/open for symlinks to reg files with appended slash on freebsd,osx */ 562ad3e93eaSStefan Bühler if (name->ptr[buffer_string_length(name) - 1] == '/') { 56357066345SStefan Bühler errno = ENOTDIR; 56457066345SStefan Bühler return HANDLER_ERROR; 56557066345SStefan Bühler } 56657066345SStefan Bühler 567478cb34bSJan Kneschke /* try to open the file to check if we can read it */ 568603d6aceSJan Kneschke if (-1 == (fd = open(name->ptr, O_RDONLY))) { 569603d6aceSJan Kneschke return HANDLER_ERROR; 570603d6aceSJan Kneschke } 571603d6aceSJan Kneschke close(fd); 572603d6aceSJan Kneschke } 573603d6aceSJan Kneschke 5745e134da0SJan Kneschke if (NULL == sce) { 575ecb30c4eSJan Kneschke 5765e134da0SJan Kneschke sce = stat_cache_entry_init(); 5776afad87dSStefan Bühler buffer_copy_buffer(sce->name, name); 5785e134da0SJan Kneschke 57969f890e2SStefan Bühler /* already splayed file_ndx */ 58069f890e2SStefan Bühler if ((NULL != sc->files) && (sc->files->key == file_ndx)) { 58169f890e2SStefan Bühler /* hash collision: replace old entry */ 58269f890e2SStefan Bühler stat_cache_entry_free(sc->files->data); 58369f890e2SStefan Bühler sc->files->data = sce; 58469f890e2SStefan Bühler } else { 58569f890e2SStefan Bühler int osize = splaytree_size(sc->files); 58669f890e2SStefan Bühler 5875e134da0SJan Kneschke sc->files = splaytree_insert(sc->files, file_ndx, sce); 58869f890e2SStefan Bühler force_assert(osize + 1 == splaytree_size(sc->files)); 58969f890e2SStefan Bühler 590b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 591b795fd36SJan Kneschke if (ctrl.size == 0) { 592b795fd36SJan Kneschke ctrl.size = 16; 593b795fd36SJan Kneschke ctrl.used = 0; 594b795fd36SJan Kneschke ctrl.ptr = malloc(ctrl.size * sizeof(*ctrl.ptr)); 595566cf8deSStefan Bühler force_assert(NULL != ctrl.ptr); 596b795fd36SJan Kneschke } else if (ctrl.size == ctrl.used) { 597b795fd36SJan Kneschke ctrl.size += 16; 598b795fd36SJan Kneschke ctrl.ptr = realloc(ctrl.ptr, ctrl.size * sizeof(*ctrl.ptr)); 599566cf8deSStefan Bühler force_assert(NULL != ctrl.ptr); 600b795fd36SJan Kneschke } 601b795fd36SJan Kneschke 602b795fd36SJan Kneschke ctrl.ptr[ctrl.used++] = file_ndx; 60369f890e2SStefan Bühler #endif 60469f890e2SStefan Bühler } 60507dd0bd0SStefan Bühler force_assert(sc->files); 60607dd0bd0SStefan Bühler force_assert(sc->files->data == sce); 6075e134da0SJan Kneschke } 6085e134da0SJan Kneschke 6095e134da0SJan Kneschke sce->st = st; 6105e134da0SJan Kneschke sce->stat_ts = srv->cur_ts; 6115e134da0SJan Kneschke 6127d875859SJan Kneschke /* catch the obvious symlinks 6137d875859SJan Kneschke * 6147d875859SJan Kneschke * this is not a secure check as we still have a race-condition between 6157d875859SJan Kneschke * the stat() and the open. We can only solve this by 6167d875859SJan Kneschke * 1. open() the file 6177d875859SJan Kneschke * 2. fstat() the fd 6187d875859SJan Kneschke * 6197d875859SJan Kneschke * and keeping the file open for the rest of the time. But this can 6207d875859SJan Kneschke * only be done at network level. 6217d875859SJan Kneschke * 62208994e47SJan Kneschke * per default it is not a symlink 6237d875859SJan Kneschke * */ 624657a024dSMarcus Rückert #ifdef HAVE_LSTAT 625657a024dSMarcus Rückert sce->is_symlink = 0; 6269de38074SJan Kneschke 6279de38074SJan Kneschke /* we want to only check for symlinks if we should block symlinks. 628a57bb820SMarcus Rückert */ 6299de38074SJan Kneschke if (!con->conf.follow_symlink) { 63008994e47SJan Kneschke if (stat_cache_lstat(srv, name, &lst) == 0) { 631657a024dSMarcus Rückert #ifdef DEBUG_STAT_CACHE 632657a024dSMarcus Rückert log_error_write(srv, __FILE__, __LINE__, "sb", 633657a024dSMarcus Rückert "found symlink", name); 634657a024dSMarcus Rückert #endif 635657a024dSMarcus Rückert sce->is_symlink = 1; 6367d875859SJan Kneschke } 6377d875859SJan Kneschke 638657a024dSMarcus Rückert /* 639657a024dSMarcus Rückert * we assume "/" can not be symlink, so 640657a024dSMarcus Rückert * skip the symlink stuff if our path is / 641657a024dSMarcus Rückert **/ 642ad3e93eaSStefan Bühler else if (buffer_string_length(name) > 1) { 64308994e47SJan Kneschke buffer *dname; 64408994e47SJan Kneschke char *s_cur; 645657a024dSMarcus Rückert 64608994e47SJan Kneschke dname = buffer_init(); 6476afad87dSStefan Bühler buffer_copy_buffer(dname, name); 64808994e47SJan Kneschke 64908994e47SJan Kneschke while ((s_cur = strrchr(dname->ptr, '/'))) { 650ad3e93eaSStefan Bühler buffer_string_set_length(dname, s_cur - dname->ptr); 65108994e47SJan Kneschke if (dname->ptr == s_cur) { 652657a024dSMarcus Rückert #ifdef DEBUG_STAT_CACHE 653657a024dSMarcus Rückert log_error_write(srv, __FILE__, __LINE__, "s", "reached /"); 654657a024dSMarcus Rückert #endif 655657a024dSMarcus Rückert break; 656657a024dSMarcus Rückert } 657657a024dSMarcus Rückert #ifdef DEBUG_STAT_CACHE 65808994e47SJan Kneschke log_error_write(srv, __FILE__, __LINE__, "sbs", 659657a024dSMarcus Rückert "checking if", dname, "is a symlink"); 660657a024dSMarcus Rückert #endif 661657a024dSMarcus Rückert if (stat_cache_lstat(srv, dname, &lst) == 0) { 662657a024dSMarcus Rückert sce->is_symlink = 1; 663657a024dSMarcus Rückert #ifdef DEBUG_STAT_CACHE 66408994e47SJan Kneschke log_error_write(srv, __FILE__, __LINE__, "sb", 665657a024dSMarcus Rückert "found symlink", dname); 666657a024dSMarcus Rückert #endif 667657a024dSMarcus Rückert break; 668657a024dSMarcus Rückert }; 669657a024dSMarcus Rückert }; 67008994e47SJan Kneschke buffer_free(dname); 671657a024dSMarcus Rückert }; 67205919041SMarcus Rückert }; 673657a024dSMarcus Rückert #endif 674657a024dSMarcus Rückert 6755e134da0SJan Kneschke if (S_ISREG(st.st_mode)) { 6765e134da0SJan Kneschke /* determine mimetype */ 6775e134da0SJan Kneschke buffer_reset(sce->content_type); 6784d55d4adSMoritz Wilhelmy #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) 67907d9517dSStefan Bühler if (con->conf.use_xattr) { 680d17d48e0SGlenn Strauss stat_cache_attr_get(sce->content_type, name->ptr, srv->srvconf.xattr_name->ptr); 681b3027fefSStefan Bühler } 682b3027fefSStefan Bühler #endif 683b3027fefSStefan Bühler /* xattr did not set a content-type. ask the config */ 6846afad87dSStefan Bühler if (buffer_string_is_empty(sce->content_type)) { 6850cc7556aSGlenn Strauss const buffer *type = stat_cache_mimetype_by_ext(con, CONST_BUF_LEN(name)); 6860cc7556aSGlenn Strauss if (NULL != type) { 6870cc7556aSGlenn Strauss buffer_copy_buffer(sce->content_type, type); 6885e134da0SJan Kneschke } 6895e134da0SJan Kneschke } 690b3027fefSStefan Bühler etag_create(sce->etag, &(sce->st), con->etag_flags); 6917d4d8195SJan Kneschke } else if (S_ISDIR(st.st_mode)) { 6925a583661SMarcus Rückert etag_create(sce->etag, &(sce->st), con->etag_flags); 6935e134da0SJan Kneschke } 6945e134da0SJan Kneschke 6955e134da0SJan Kneschke #ifdef HAVE_FAM_H 696d8b363c1SStefan Bühler if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { 6975e134da0SJan Kneschke /* is this directory already registered ? */ 69869f890e2SStefan Bühler if (NULL == fam_dir) { 6995e134da0SJan Kneschke fam_dir = fam_dir_entry_init(); 7005e134da0SJan Kneschke 7016afad87dSStefan Bühler buffer_copy_buffer(fam_dir->name, sc->dir_name); 7025e134da0SJan Kneschke 7035e134da0SJan Kneschke fam_dir->version = 1; 7045e134da0SJan Kneschke 7055e134da0SJan Kneschke fam_dir->req = calloc(1, sizeof(FAMRequest)); 706566cf8deSStefan Bühler force_assert(NULL != fam_dir); 7075e134da0SJan Kneschke 708d8b363c1SStefan Bühler if (0 != FAMMonitorDirectory(&sc->fam, fam_dir->name->ptr, 7095e134da0SJan Kneschke fam_dir->req, fam_dir)) { 7105e134da0SJan Kneschke 711e745fd6aSJan Kneschke log_error_write(srv, __FILE__, __LINE__, "sbsbs", 7125e134da0SJan Kneschke "monitoring dir failed:", 7135e134da0SJan Kneschke fam_dir->name, 714e745fd6aSJan Kneschke "file:", name, 7155e134da0SJan Kneschke FamErrlist[FAMErrno]); 7165e134da0SJan Kneschke 717d8b363c1SStefan Bühler fam_dir_entry_free(&sc->fam, fam_dir); 718ae133550SStefan Bühler fam_dir = NULL; 7195e134da0SJan Kneschke } else { 72069f890e2SStefan Bühler int osize = splaytree_size(sc->dirs); 721ecb30c4eSJan Kneschke 72269f890e2SStefan Bühler /* already splayed dir_ndx */ 72369f890e2SStefan Bühler if ((NULL != sc->dirs) && (sc->dirs->key == dir_ndx)) { 72469f890e2SStefan Bühler /* hash collision: replace old entry */ 72569f890e2SStefan Bühler fam_dir_entry_free(&sc->fam, sc->dirs->data); 72669f890e2SStefan Bühler sc->dirs->data = fam_dir; 72769f890e2SStefan Bühler } else { 72869f890e2SStefan Bühler sc->dirs = splaytree_insert(sc->dirs, dir_ndx, fam_dir); 72969f890e2SStefan Bühler force_assert(osize == (splaytree_size(sc->dirs) - 1)); 730ecb30c4eSJan Kneschke } 731ecb30c4eSJan Kneschke 73207dd0bd0SStefan Bühler force_assert(sc->dirs); 73307dd0bd0SStefan Bühler force_assert(sc->dirs->data == fam_dir); 7345e134da0SJan Kneschke } 7355e134da0SJan Kneschke } 7365e134da0SJan Kneschke 7375e134da0SJan Kneschke /* bind the fam_fc to the stat() cache entry */ 7385e134da0SJan Kneschke 7395e134da0SJan Kneschke if (fam_dir) { 7405e134da0SJan Kneschke sce->dir_version = fam_dir->version; 7415e134da0SJan Kneschke } 7425e134da0SJan Kneschke } 7435e134da0SJan Kneschke #endif 74451978868SJan Kneschke 7455e134da0SJan Kneschke *ret_sce = sce; 7465e134da0SJan Kneschke 7475e134da0SJan Kneschke return HANDLER_GO_ON; 7485e134da0SJan Kneschke } 7495e134da0SJan Kneschke 750a65c57a5SGlenn Strauss int stat_cache_open_rdonly_fstat (server *srv, connection *con, buffer *name, struct stat *st) { 751a65c57a5SGlenn Strauss /*(Note: O_NOFOLLOW affects only the final path segment, the target file, 752a65c57a5SGlenn Strauss * not any intermediate symlinks along the path)*/ 753a65c57a5SGlenn Strauss #ifndef O_BINARY 754a65c57a5SGlenn Strauss #define O_BINARY 0 755a65c57a5SGlenn Strauss #endif 756a65c57a5SGlenn Strauss #ifndef O_LARGEFILE 757a65c57a5SGlenn Strauss #define O_LARGEFILE 0 758a65c57a5SGlenn Strauss #endif 759a65c57a5SGlenn Strauss #ifndef O_NOCTTY 760a65c57a5SGlenn Strauss #define O_NOCTTY 0 761a65c57a5SGlenn Strauss #endif 762a65c57a5SGlenn Strauss #ifndef O_NONBLOCK 763a65c57a5SGlenn Strauss #define O_NONBLOCK 0 764a65c57a5SGlenn Strauss #endif 765a65c57a5SGlenn Strauss #ifndef O_NOFOLLOW 766a65c57a5SGlenn Strauss #define O_NOFOLLOW 0 767a65c57a5SGlenn Strauss #endif 768a65c57a5SGlenn Strauss const int oflags = O_BINARY | O_LARGEFILE | O_NOCTTY | O_NONBLOCK 769a65c57a5SGlenn Strauss | (con->conf.follow_symlink ? 0 : O_NOFOLLOW); 770a65c57a5SGlenn Strauss const int fd = open(name->ptr, O_RDONLY | oflags); 771a65c57a5SGlenn Strauss if (fd >= 0) { 772a65c57a5SGlenn Strauss if (0 == fstat(fd, st)) { 773a65c57a5SGlenn Strauss return fd; 774a65c57a5SGlenn Strauss } else { 775a65c57a5SGlenn Strauss close(fd); 776a65c57a5SGlenn Strauss } 777a65c57a5SGlenn Strauss } 778a65c57a5SGlenn Strauss UNUSED(srv); /*(might log_error_write(srv, ...) in the future)*/ 779a65c57a5SGlenn Strauss return -1; 780a65c57a5SGlenn Strauss } 781a65c57a5SGlenn Strauss 7825e134da0SJan Kneschke /** 7835e134da0SJan Kneschke * remove stat() from cache which havn't been stat()ed for 7845e134da0SJan Kneschke * more than 10 seconds 7855e134da0SJan Kneschke * 7865e134da0SJan Kneschke * 7875e134da0SJan Kneschke * walk though the stat-cache, collect the ids which are too old 7885e134da0SJan Kneschke * and remove them in a second loop 7895e134da0SJan Kneschke */ 7905e134da0SJan Kneschke 7915e134da0SJan Kneschke static int stat_cache_tag_old_entries(server *srv, splay_tree *t, int *keys, size_t *ndx) { 7925e134da0SJan Kneschke stat_cache_entry *sce; 7935e134da0SJan Kneschke 7945e134da0SJan Kneschke if (!t) return 0; 7955e134da0SJan Kneschke 7965e134da0SJan Kneschke stat_cache_tag_old_entries(srv, t->left, keys, ndx); 7975e134da0SJan Kneschke stat_cache_tag_old_entries(srv, t->right, keys, ndx); 7985e134da0SJan Kneschke 7995e134da0SJan Kneschke sce = t->data; 8005e134da0SJan Kneschke 801b795fd36SJan Kneschke if (srv->cur_ts - sce->stat_ts > 2) { 8025e134da0SJan Kneschke keys[(*ndx)++] = t->key; 8035e134da0SJan Kneschke } 8045e134da0SJan Kneschke 8055e134da0SJan Kneschke return 0; 8065e134da0SJan Kneschke } 8075e134da0SJan Kneschke 8085e134da0SJan Kneschke int stat_cache_trigger_cleanup(server *srv) { 8095e134da0SJan Kneschke stat_cache *sc; 81056f637b8SJan Kneschke size_t max_ndx = 0, i; 8115e134da0SJan Kneschke int *keys; 8125e134da0SJan Kneschke 8135e134da0SJan Kneschke sc = srv->stat_cache; 8145e134da0SJan Kneschke 8155e134da0SJan Kneschke if (!sc->files) return 0; 8165e134da0SJan Kneschke 8176f208cfdSStefan Bühler keys = calloc(1, sizeof(int) * sc->files->size); 818566cf8deSStefan Bühler force_assert(NULL != keys); 8195e134da0SJan Kneschke 8205e134da0SJan Kneschke stat_cache_tag_old_entries(srv, sc->files, keys, &max_ndx); 8215e134da0SJan Kneschke 8225e134da0SJan Kneschke for (i = 0; i < max_ndx; i++) { 8235e134da0SJan Kneschke int ndx = keys[i]; 8245e134da0SJan Kneschke splay_tree *node; 8255e134da0SJan Kneschke 8265e134da0SJan Kneschke sc->files = splaytree_splay(sc->files, ndx); 8275e134da0SJan Kneschke 8285e134da0SJan Kneschke node = sc->files; 8295e134da0SJan Kneschke 8305e134da0SJan Kneschke if (node && (node->key == ndx)) { 83156f637b8SJan Kneschke #ifdef DEBUG_STAT_CACHE 83256f637b8SJan Kneschke size_t j; 833b795fd36SJan Kneschke int osize = splaytree_size(sc->files); 834b795fd36SJan Kneschke stat_cache_entry *sce = node->data; 83556f637b8SJan Kneschke #endif 8365e134da0SJan Kneschke stat_cache_entry_free(node->data); 8375e134da0SJan Kneschke sc->files = splaytree_delete(sc->files, ndx); 838d1e70da8SJan Kneschke 839b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 840b795fd36SJan Kneschke for (j = 0; j < ctrl.used; j++) { 841b795fd36SJan Kneschke if (ctrl.ptr[j] == ndx) { 842b795fd36SJan Kneschke ctrl.ptr[j] = ctrl.ptr[--ctrl.used]; 843b795fd36SJan Kneschke break; 844ecb30c4eSJan Kneschke } 8455e134da0SJan Kneschke } 846b795fd36SJan Kneschke 84707dd0bd0SStefan Bühler force_assert(osize - 1 == splaytree_size(sc->files)); 848b795fd36SJan Kneschke #endif 849b795fd36SJan Kneschke } 8505e134da0SJan Kneschke } 8515e134da0SJan Kneschke 8525e134da0SJan Kneschke free(keys); 8535e134da0SJan Kneschke 8545e134da0SJan Kneschke return 0; 8555e134da0SJan Kneschke } 856