15e134da0SJan Kneschke #define _GNU_SOURCE 25e134da0SJan Kneschke 35e134da0SJan Kneschke #include <sys/types.h> 45e134da0SJan Kneschke #include <sys/stat.h> 55e134da0SJan Kneschke 65e134da0SJan Kneschke #include <stdlib.h> 75e134da0SJan Kneschke #include <string.h> 85e134da0SJan Kneschke #include <errno.h> 95e134da0SJan Kneschke #include <unistd.h> 105e134da0SJan Kneschke #include <stdio.h> 115e134da0SJan Kneschke #include <fcntl.h> 12ecb30c4eSJan Kneschke #include <assert.h> 135e134da0SJan Kneschke 145e134da0SJan Kneschke #include "log.h" 155e134da0SJan Kneschke #include "stat_cache.h" 165e134da0SJan Kneschke #include "fdevent.h" 175e134da0SJan Kneschke #include "etag.h" 185e134da0SJan Kneschke 195e134da0SJan Kneschke #ifdef HAVE_ATTR_ATTRIBUTES_H 205e134da0SJan Kneschke #include <attr/attributes.h> 215e134da0SJan Kneschke #endif 225e134da0SJan Kneschke 235e134da0SJan Kneschke #ifdef HAVE_FAM_H 245e134da0SJan Kneschke # include <fam.h> 255e134da0SJan Kneschke #endif 265e134da0SJan Kneschke 275e134da0SJan Kneschke #include "sys-mmap.h" 285e134da0SJan Kneschke 295e134da0SJan Kneschke /* NetBSD 1.3.x needs it */ 305e134da0SJan Kneschke #ifndef MAP_FAILED 315e134da0SJan Kneschke # define MAP_FAILED -1 325e134da0SJan Kneschke #endif 335e134da0SJan Kneschke 345e134da0SJan Kneschke #ifndef O_LARGEFILE 355e134da0SJan Kneschke # define O_LARGEFILE 0 365e134da0SJan Kneschke #endif 375e134da0SJan Kneschke 385e134da0SJan Kneschke #ifndef HAVE_LSTAT 395e134da0SJan Kneschke #define lstat stat 405e134da0SJan Kneschke #endif 415e134da0SJan Kneschke 42b795fd36SJan Kneschke #if 0 43b795fd36SJan Kneschke /* enables debug code for testing if all nodes in the stat-cache as accessable */ 44b795fd36SJan Kneschke #define DEBUG_STAT_CACHE 45b795fd36SJan Kneschke #endif 46b795fd36SJan Kneschke 475e134da0SJan Kneschke /* 485e134da0SJan Kneschke * stat-cache 495e134da0SJan Kneschke * 505e134da0SJan Kneschke * we cache the stat() calls in our own storage 515e134da0SJan Kneschke * the directories are cached in FAM 525e134da0SJan Kneschke * 535e134da0SJan Kneschke * if we get a change-event from FAM, we increment the version in the FAM->dir mapping 545e134da0SJan Kneschke * 555e134da0SJan Kneschke * if the stat()-cache is queried we check if the version id for the directory is the 565e134da0SJan Kneschke * same and return immediatly. 575e134da0SJan Kneschke * 585e134da0SJan Kneschke * 595e134da0SJan Kneschke * What we need: 605e134da0SJan Kneschke * 615e134da0SJan Kneschke * - for each stat-cache entry we need a fast indirect lookup on the directory name 625e134da0SJan Kneschke * - for each FAMRequest we have to find the version in the directory cache (index as userdata) 635e134da0SJan Kneschke * 645e134da0SJan Kneschke * stat <<-> directory <-> FAMRequest 655e134da0SJan Kneschke * 665e134da0SJan Kneschke * if file is deleted, directory is dirty, file is rechecked ... 675e134da0SJan Kneschke * if directory is deleted, directory mapping is removed 685e134da0SJan Kneschke * 695e134da0SJan Kneschke * */ 705e134da0SJan Kneschke 715e134da0SJan Kneschke #ifdef HAVE_FAM_H 725e134da0SJan Kneschke typedef struct { 735e134da0SJan Kneschke FAMRequest *req; 745e134da0SJan Kneschke FAMConnection *fc; 755e134da0SJan Kneschke 765e134da0SJan Kneschke buffer *name; 775e134da0SJan Kneschke 785e134da0SJan Kneschke int version; 795e134da0SJan Kneschke } fam_dir_entry; 805e134da0SJan Kneschke #endif 815e134da0SJan Kneschke 825e134da0SJan Kneschke /* the directory name is too long to always compare on it 835e134da0SJan Kneschke * - we need a hash 845e134da0SJan Kneschke * - the hash-key is used as sorting criteria for a tree 855e134da0SJan Kneschke * - a splay-tree is used as we can use the caching effect of it 865e134da0SJan Kneschke */ 875e134da0SJan Kneschke 885e134da0SJan Kneschke /* we want to cleanup the stat-cache every few seconds, let's say 10 895e134da0SJan Kneschke * 905e134da0SJan Kneschke * - remove entries which are outdated since 30s 915e134da0SJan Kneschke * - remove entries which are fresh but havn't been used since 60s 925e134da0SJan Kneschke * - if we don't have a stat-cache entry for a directory, release it from the monitor 935e134da0SJan Kneschke */ 945e134da0SJan Kneschke 95b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 96b795fd36SJan Kneschke typedef struct { 97b795fd36SJan Kneschke int *ptr; 98b795fd36SJan Kneschke 99b795fd36SJan Kneschke size_t used; 100b795fd36SJan Kneschke size_t size; 101b795fd36SJan Kneschke } fake_keys; 102b795fd36SJan Kneschke 103b795fd36SJan Kneschke static fake_keys ctrl; 104b795fd36SJan Kneschke #endif 105b795fd36SJan Kneschke 1065e134da0SJan Kneschke stat_cache *stat_cache_init(void) { 1075e134da0SJan Kneschke stat_cache *fc = NULL; 1085e134da0SJan Kneschke 1095e134da0SJan Kneschke fc = calloc(1, sizeof(*fc)); 1105e134da0SJan Kneschke 1115e134da0SJan Kneschke fc->dir_name = buffer_init(); 1125e134da0SJan Kneschke #ifdef HAVE_FAM_H 1135e134da0SJan Kneschke fc->fam = calloc(1, sizeof(*fc->fam)); 11479d3bc44SJan Kneschke #endif 1155e134da0SJan Kneschke 116b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 117b795fd36SJan Kneschke ctrl.size = 0; 118b795fd36SJan Kneschke #endif 119b795fd36SJan Kneschke 1205e134da0SJan Kneschke return fc; 1215e134da0SJan Kneschke } 1225e134da0SJan Kneschke 1235e134da0SJan Kneschke static stat_cache_entry * stat_cache_entry_init(void) { 1245e134da0SJan Kneschke stat_cache_entry *sce = NULL; 1255e134da0SJan Kneschke 1265e134da0SJan Kneschke sce = calloc(1, sizeof(*sce)); 1275e134da0SJan Kneschke 1285e134da0SJan Kneschke sce->name = buffer_init(); 1295e134da0SJan Kneschke sce->etag = buffer_init(); 1305e134da0SJan Kneschke sce->content_type = buffer_init(); 1315e134da0SJan Kneschke 1325e134da0SJan Kneschke return sce; 1335e134da0SJan Kneschke } 1345e134da0SJan Kneschke 1355e134da0SJan Kneschke static void stat_cache_entry_free(void *data) { 1365e134da0SJan Kneschke stat_cache_entry *sce = data; 1375e134da0SJan Kneschke if (!sce) return; 1385e134da0SJan Kneschke 1395e134da0SJan Kneschke buffer_free(sce->etag); 1405e134da0SJan Kneschke buffer_free(sce->name); 1415e134da0SJan Kneschke buffer_free(sce->content_type); 1425e134da0SJan Kneschke 1435e134da0SJan Kneschke free(sce); 1445e134da0SJan Kneschke } 1455e134da0SJan Kneschke 1465e134da0SJan Kneschke #ifdef HAVE_FAM_H 1475e134da0SJan Kneschke static fam_dir_entry * fam_dir_entry_init(void) { 1485e134da0SJan Kneschke fam_dir_entry *fam_dir = NULL; 1495e134da0SJan Kneschke 1505e134da0SJan Kneschke fam_dir = calloc(1, sizeof(*fam_dir)); 1515e134da0SJan Kneschke 1525e134da0SJan Kneschke fam_dir->name = buffer_init(); 1535e134da0SJan Kneschke 1545e134da0SJan Kneschke return fam_dir; 1555e134da0SJan Kneschke } 1565e134da0SJan Kneschke 1575e134da0SJan Kneschke static void fam_dir_entry_free(void *data) { 1585e134da0SJan Kneschke fam_dir_entry *fam_dir = data; 1595e134da0SJan Kneschke 1605e134da0SJan Kneschke if (!fam_dir) return; 1615e134da0SJan Kneschke 1625e134da0SJan Kneschke FAMCancelMonitor(fam_dir->fc, fam_dir->req); 1635e134da0SJan Kneschke 1645e134da0SJan Kneschke buffer_free(fam_dir->name); 1655e134da0SJan Kneschke free(fam_dir->req); 1665e134da0SJan Kneschke 1675e134da0SJan Kneschke free(fam_dir); 1685e134da0SJan Kneschke } 1695e134da0SJan Kneschke #endif 1705e134da0SJan Kneschke 171d1e70da8SJan Kneschke void stat_cache_free(stat_cache *sc) { 172d1e70da8SJan Kneschke while (sc->files) { 173ecb30c4eSJan Kneschke int osize; 174d1e70da8SJan Kneschke splay_tree *node = sc->files; 1755e134da0SJan Kneschke 176ecb30c4eSJan Kneschke osize = sc->files->size; 177ecb30c4eSJan Kneschke 178d1e70da8SJan Kneschke stat_cache_entry_free(node->data); 179d1e70da8SJan Kneschke sc->files = splaytree_delete(sc->files, node->key); 180d1e70da8SJan Kneschke 181b795fd36SJan Kneschke assert(osize - 1 == splaytree_size(sc->files)); 182d1e70da8SJan Kneschke } 183d1e70da8SJan Kneschke 184d1e70da8SJan Kneschke buffer_free(sc->dir_name); 1855e134da0SJan Kneschke 1865e134da0SJan Kneschke #ifdef HAVE_FAM_H 187d1e70da8SJan Kneschke while (sc->dirs) { 188ecb30c4eSJan Kneschke int osize; 189d1e70da8SJan Kneschke splay_tree *node = sc->dirs; 1905e134da0SJan Kneschke 191ecb30c4eSJan Kneschke osize = sc->dirs->size; 192ecb30c4eSJan Kneschke 193d1e70da8SJan Kneschke fam_dir_entry_free(node->data); 194d1e70da8SJan Kneschke sc->dirs = splaytree_delete(sc->dirs, node->key); 195d1e70da8SJan Kneschke 196ecb30c4eSJan Kneschke if (osize == 1) { 197ecb30c4eSJan Kneschke assert(NULL == sc->dirs); 198ecb30c4eSJan Kneschke } else { 199ecb30c4eSJan Kneschke assert(osize == (sc->dirs->size + 1)); 200ecb30c4eSJan Kneschke } 201d1e70da8SJan Kneschke } 202d1e70da8SJan Kneschke 203d1e70da8SJan Kneschke if (sc->fam) { 204d1e70da8SJan Kneschke FAMClose(sc->fam); 205d1e70da8SJan Kneschke free(sc->fam); 20679d3bc44SJan Kneschke } 2075e134da0SJan Kneschke #endif 208d1e70da8SJan Kneschke free(sc); 2095e134da0SJan Kneschke } 2105e134da0SJan Kneschke 2115e134da0SJan Kneschke #ifdef HAVE_XATTR 21230b19732SJan Kneschke static int stat_cache_attr_get(buffer *buf, char *name) { 2135e134da0SJan Kneschke int attrlen; 2145e134da0SJan Kneschke int ret; 2155e134da0SJan Kneschke 2165e134da0SJan Kneschke attrlen = 1024; 2175e134da0SJan Kneschke buffer_prepare_copy(buf, attrlen); 2185e134da0SJan Kneschke attrlen--; 2195e134da0SJan Kneschke if(0 == (ret = attr_get(name, "Content-Type", buf->ptr, &attrlen, 0))) { 2205e134da0SJan Kneschke buf->used = attrlen + 1; 2215e134da0SJan Kneschke buf->ptr[attrlen] = '\0'; 2225e134da0SJan Kneschke } 2235e134da0SJan Kneschke return ret; 2245e134da0SJan Kneschke } 2255e134da0SJan Kneschke #endif 2265e134da0SJan Kneschke 227b795fd36SJan Kneschke /* the famous DJB hash function for strings */ 228b795fd36SJan Kneschke static uint32_t hashme(buffer *str) { 229b795fd36SJan Kneschke uint32_t hash = 5381; 2305e134da0SJan Kneschke const char *s; 2315e134da0SJan Kneschke for (s = str->ptr; *s; s++) { 232b795fd36SJan Kneschke hash = ((hash << 5) + hash) + *s; 2335e134da0SJan Kneschke } 2345e134da0SJan Kneschke 235b795fd36SJan Kneschke hash &= ~(1 << 31); /* strip the highest bit */ 236b795fd36SJan Kneschke 2375e134da0SJan Kneschke return hash; 2385e134da0SJan Kneschke } 2395e134da0SJan Kneschke 2405e134da0SJan Kneschke #ifdef HAVE_FAM_H 2415e134da0SJan Kneschke handler_t stat_cache_handle_fdevent(void *_srv, void *_fce, int revent) { 2425e134da0SJan Kneschke size_t i; 2435e134da0SJan Kneschke server *srv = _srv; 2445e134da0SJan Kneschke stat_cache *sc = srv->stat_cache; 2455e134da0SJan Kneschke size_t events; 2465e134da0SJan Kneschke 24756f637b8SJan Kneschke UNUSED(_fce); 2485e134da0SJan Kneschke /* */ 2495e134da0SJan Kneschke 25079d3bc44SJan Kneschke if ((revent & FDEVENT_IN) && 25179d3bc44SJan Kneschke sc->fam) { 25279d3bc44SJan Kneschke 2535e134da0SJan Kneschke events = FAMPending(sc->fam); 2545e134da0SJan Kneschke 2555e134da0SJan Kneschke for (i = 0; i < events; i++) { 2565e134da0SJan Kneschke FAMEvent fe; 2575e134da0SJan Kneschke fam_dir_entry *fam_dir; 2585e134da0SJan Kneschke splay_tree *node; 2595e134da0SJan Kneschke int ndx; 2605e134da0SJan Kneschke 2615e134da0SJan Kneschke FAMNextEvent(sc->fam, &fe); 2625e134da0SJan Kneschke 2635e134da0SJan Kneschke /* handle event */ 2645e134da0SJan Kneschke 2655e134da0SJan Kneschke switch(fe.code) { 2665e134da0SJan Kneschke case FAMChanged: 2675e134da0SJan Kneschke case FAMDeleted: 2685e134da0SJan Kneschke case FAMMoved: 2695e134da0SJan Kneschke /* if the filename is a directory remove the entry */ 2705e134da0SJan Kneschke 2715e134da0SJan Kneschke fam_dir = fe.userdata; 2725e134da0SJan Kneschke fam_dir->version++; 2735e134da0SJan Kneschke 2745e134da0SJan Kneschke /* file/dir is still here */ 2755e134da0SJan Kneschke if (fe.code == FAMChanged) break; 2765e134da0SJan Kneschke 2775e134da0SJan Kneschke buffer_copy_string(sc->dir_name, fe.filename); 2785e134da0SJan Kneschke 2795e134da0SJan Kneschke ndx = hashme(sc->dir_name); 2805e134da0SJan Kneschke 2815e134da0SJan Kneschke sc->dirs = splaytree_splay(sc->dirs, ndx); 2825e134da0SJan Kneschke node = sc->dirs; 2835e134da0SJan Kneschke 2845e134da0SJan Kneschke if (node && (node->key == ndx)) { 285b795fd36SJan Kneschke int osize = splaytree_size(sc->dirs); 286d1e70da8SJan Kneschke 287ecb30c4eSJan Kneschke fam_dir_entry_free(node->data); 2885e134da0SJan Kneschke sc->dirs = splaytree_delete(sc->dirs, ndx); 289d1e70da8SJan Kneschke 290b795fd36SJan Kneschke assert(osize - 1 == splaytree_size(sc->dirs)); 2915e134da0SJan Kneschke } 2925e134da0SJan Kneschke break; 2935e134da0SJan Kneschke default: 2945e134da0SJan Kneschke break; 2955e134da0SJan Kneschke } 2965e134da0SJan Kneschke } 29779d3bc44SJan Kneschke } 29879d3bc44SJan Kneschke 29979d3bc44SJan Kneschke if (revent & FDEVENT_HUP) { 30079d3bc44SJan Kneschke /* fam closed the connection */ 30179d3bc44SJan Kneschke srv->stat_cache->fam_fcce_ndx = -1; 30279d3bc44SJan Kneschke 30379d3bc44SJan Kneschke fdevent_event_del(srv->ev, &(sc->fam_fcce_ndx), FAMCONNECTION_GETFD(sc->fam)); 30479d3bc44SJan Kneschke fdevent_unregister(srv->ev, FAMCONNECTION_GETFD(sc->fam)); 30579d3bc44SJan Kneschke 30679d3bc44SJan Kneschke FAMClose(sc->fam); 30779d3bc44SJan Kneschke free(sc->fam); 30879d3bc44SJan Kneschke 30979d3bc44SJan Kneschke sc->fam = NULL; 31079d3bc44SJan Kneschke } 3115e134da0SJan Kneschke 3125e134da0SJan Kneschke return HANDLER_GO_ON; 3135e134da0SJan Kneschke } 3145e134da0SJan Kneschke 3155e134da0SJan Kneschke static int buffer_copy_dirname(buffer *dst, buffer *file) { 3165e134da0SJan Kneschke size_t i; 3175e134da0SJan Kneschke 3185e134da0SJan Kneschke if (buffer_is_empty(file)) return -1; 3195e134da0SJan Kneschke 3205e134da0SJan Kneschke for (i = file->used - 1; i+1 > 0; i--) { 3215e134da0SJan Kneschke if (file->ptr[i] == '/') { 3225e134da0SJan Kneschke buffer_copy_string_len(dst, file->ptr, i); 3235e134da0SJan Kneschke return 0; 3245e134da0SJan Kneschke } 3255e134da0SJan Kneschke } 3265e134da0SJan Kneschke 3275e134da0SJan Kneschke return -1; 3285e134da0SJan Kneschke } 3295e134da0SJan Kneschke #endif 3305e134da0SJan Kneschke 3315e134da0SJan Kneschke /*** 3325e134da0SJan Kneschke * 3335e134da0SJan Kneschke * 3345e134da0SJan Kneschke * 3355e134da0SJan Kneschke * returns: 3365e134da0SJan Kneschke * - HANDLER_FINISHED on cache-miss (don't forget to reopen the file) 3375e134da0SJan Kneschke * - HANDLER_ERROR on stat() failed -> see errno for problem 3385e134da0SJan Kneschke */ 3395e134da0SJan Kneschke 3405e134da0SJan Kneschke handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_cache_entry **ret_sce) { 3415e134da0SJan Kneschke #ifdef HAVE_FAM_H 3425e134da0SJan Kneschke fam_dir_entry *fam_dir = NULL; 3435e134da0SJan Kneschke int dir_ndx = -1; 3445e134da0SJan Kneschke splay_tree *dir_node = NULL; 3455e134da0SJan Kneschke #endif 3465e134da0SJan Kneschke stat_cache_entry *sce = NULL; 3475e134da0SJan Kneschke stat_cache *sc; 3485e134da0SJan Kneschke struct stat st; 34951978868SJan Kneschke size_t k; 350*7d875859SJan Kneschke int fd; 35156f637b8SJan Kneschke #ifdef DEBUG_STAT_CACHE 352b795fd36SJan Kneschke size_t i; 35356f637b8SJan Kneschke #endif 3545e134da0SJan Kneschke 3555e134da0SJan Kneschke int file_ndx; 3565e134da0SJan Kneschke splay_tree *file_node = NULL; 3575e134da0SJan Kneschke 3585e134da0SJan Kneschke *ret_sce = NULL; 3595e134da0SJan Kneschke 3605e134da0SJan Kneschke /* 3615e134da0SJan Kneschke * check if the directory for this file has changed 3625e134da0SJan Kneschke */ 3635e134da0SJan Kneschke 3645e134da0SJan Kneschke sc = srv->stat_cache; 3655e134da0SJan Kneschke 3665e134da0SJan Kneschke file_ndx = hashme(name); 3675e134da0SJan Kneschke sc->files = splaytree_splay(sc->files, file_ndx); 3685e134da0SJan Kneschke 369b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 370b795fd36SJan Kneschke for (i = 0; i < ctrl.used; i++) { 371b795fd36SJan Kneschke if (ctrl.ptr[i] == file_ndx) break; 372b795fd36SJan Kneschke } 373b795fd36SJan Kneschke #endif 374b795fd36SJan Kneschke 3755e134da0SJan Kneschke if (sc->files && (sc->files->key == file_ndx)) { 376b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 377b795fd36SJan Kneschke /* it was in the cache */ 378b795fd36SJan Kneschke assert(i < ctrl.used); 379b795fd36SJan Kneschke #endif 380b795fd36SJan Kneschke 3815e134da0SJan Kneschke /* we have seen this file already and 3825e134da0SJan Kneschke * don't stat() it again in the same second */ 3835e134da0SJan Kneschke 3845e134da0SJan Kneschke file_node = sc->files; 3855e134da0SJan Kneschke 3865e134da0SJan Kneschke sce = file_node->data; 3875e134da0SJan Kneschke 388ecb30c4eSJan Kneschke /* check if the name is the same, we might have a collision */ 389ecb30c4eSJan Kneschke 390ecb30c4eSJan Kneschke if (buffer_is_equal(name, sce->name)) { 3915e134da0SJan Kneschke if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_SIMPLE) { 3925e134da0SJan Kneschke if (sce->stat_ts == srv->cur_ts) { 3935e134da0SJan Kneschke *ret_sce = sce; 3945e134da0SJan Kneschke return HANDLER_GO_ON; 3955e134da0SJan Kneschke } 3965e134da0SJan Kneschke } 397ecb30c4eSJan Kneschke } else { 398ecb30c4eSJan Kneschke /* oops, a collision, 399ecb30c4eSJan Kneschke * 400ecb30c4eSJan Kneschke * file_node is used by the FAM check below to see if we know this file 401ecb30c4eSJan Kneschke * and if we can save a stat(). 402ecb30c4eSJan Kneschke * 403ecb30c4eSJan Kneschke * BUT, the sce is not reset here as the entry into the cache is ok, we 404ecb30c4eSJan Kneschke * it is just not pointing to our requested file. 405ecb30c4eSJan Kneschke * 406ecb30c4eSJan Kneschke * */ 407ecb30c4eSJan Kneschke 408ecb30c4eSJan Kneschke file_node = NULL; 409ecb30c4eSJan Kneschke } 410b795fd36SJan Kneschke } else { 411b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 412b795fd36SJan Kneschke if (i != ctrl.used) { 413b795fd36SJan Kneschke fprintf(stderr, "%s.%d: %08x was already inserted but not found in cache, %s\n", __FILE__, __LINE__, file_ndx, name->ptr); 414b795fd36SJan Kneschke } 415b795fd36SJan Kneschke assert(i == ctrl.used); 416b795fd36SJan Kneschke #endif 4175e134da0SJan Kneschke } 4185e134da0SJan Kneschke 4195e134da0SJan Kneschke #ifdef HAVE_FAM_H 4205e134da0SJan Kneschke /* dir-check */ 4215e134da0SJan Kneschke if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { 4225e134da0SJan Kneschke if (0 != buffer_copy_dirname(sc->dir_name, name)) { 4235e134da0SJan Kneschke SEGFAULT(); 4245e134da0SJan Kneschke } 4255e134da0SJan Kneschke 4265e134da0SJan Kneschke dir_ndx = hashme(sc->dir_name); 4275e134da0SJan Kneschke 4285e134da0SJan Kneschke sc->dirs = splaytree_splay(sc->dirs, dir_ndx); 4295e134da0SJan Kneschke 4305e134da0SJan Kneschke if (sc->dirs && (sc->dirs->key == dir_ndx)) { 4315e134da0SJan Kneschke dir_node = sc->dirs; 4325e134da0SJan Kneschke } 4335e134da0SJan Kneschke 4345e134da0SJan Kneschke if (dir_node && file_node) { 4355e134da0SJan Kneschke /* we found a file */ 4365e134da0SJan Kneschke 4375e134da0SJan Kneschke sce = file_node->data; 4385e134da0SJan Kneschke fam_dir = dir_node->data; 4395e134da0SJan Kneschke 4405e134da0SJan Kneschke if (fam_dir->version == sce->dir_version) { 4415e134da0SJan Kneschke /* the stat()-cache entry is still ok */ 4425e134da0SJan Kneschke 4435e134da0SJan Kneschke *ret_sce = sce; 4445e134da0SJan Kneschke return HANDLER_GO_ON; 4455e134da0SJan Kneschke } 4465e134da0SJan Kneschke } 4475e134da0SJan Kneschke } 4485e134da0SJan Kneschke #endif 449*7d875859SJan Kneschke /* try to open the file */ 450603d6aceSJan Kneschke if (-1 == (fd = open(name->ptr, O_RDONLY))) { 451603d6aceSJan Kneschke return HANDLER_ERROR; 452603d6aceSJan Kneschke } 453*7d875859SJan Kneschke 454*7d875859SJan Kneschke if (-1 == fstat(fd, &st)) { 455603d6aceSJan Kneschke close(fd); 456*7d875859SJan Kneschke return HANDLER_ERROR; 457603d6aceSJan Kneschke } 458603d6aceSJan Kneschke 459*7d875859SJan Kneschke close(fd); 460*7d875859SJan Kneschke 4615e134da0SJan Kneschke if (NULL == sce) { 462ecb30c4eSJan Kneschke int osize = 0; 463ecb30c4eSJan Kneschke 464ecb30c4eSJan Kneschke if (sc->files) { 465ecb30c4eSJan Kneschke osize = sc->files->size; 466ecb30c4eSJan Kneschke } 467ecb30c4eSJan Kneschke 4685e134da0SJan Kneschke sce = stat_cache_entry_init(); 4695e134da0SJan Kneschke buffer_copy_string_buffer(sce->name, name); 4705e134da0SJan Kneschke 4715e134da0SJan Kneschke sc->files = splaytree_insert(sc->files, file_ndx, sce); 472b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 473b795fd36SJan Kneschke if (ctrl.size == 0) { 474b795fd36SJan Kneschke ctrl.size = 16; 475b795fd36SJan Kneschke ctrl.used = 0; 476b795fd36SJan Kneschke ctrl.ptr = malloc(ctrl.size * sizeof(*ctrl.ptr)); 477b795fd36SJan Kneschke } else if (ctrl.size == ctrl.used) { 478b795fd36SJan Kneschke ctrl.size += 16; 479b795fd36SJan Kneschke ctrl.ptr = realloc(ctrl.ptr, ctrl.size * sizeof(*ctrl.ptr)); 480b795fd36SJan Kneschke } 481b795fd36SJan Kneschke 482b795fd36SJan Kneschke ctrl.ptr[ctrl.used++] = file_ndx; 483ecb30c4eSJan Kneschke 484ecb30c4eSJan Kneschke assert(sc->files); 485b795fd36SJan Kneschke assert(sc->files->data == sce); 486b795fd36SJan Kneschke assert(osize + 1 == splaytree_size(sc->files)); 487b795fd36SJan Kneschke #endif 4885e134da0SJan Kneschke } 4895e134da0SJan Kneschke 4905e134da0SJan Kneschke sce->st = st; 4915e134da0SJan Kneschke sce->stat_ts = srv->cur_ts; 4925e134da0SJan Kneschke 493*7d875859SJan Kneschke /* catch the obvious symlinks 494*7d875859SJan Kneschke * 495*7d875859SJan Kneschke * this is not a secure check as we still have a race-condition between 496*7d875859SJan Kneschke * the stat() and the open. We can only solve this by 497*7d875859SJan Kneschke * 1. open() the file 498*7d875859SJan Kneschke * 2. fstat() the fd 499*7d875859SJan Kneschke * 500*7d875859SJan Kneschke * and keeping the file open for the rest of the time. But this can 501*7d875859SJan Kneschke * only be done at network level. 502*7d875859SJan Kneschke * 503*7d875859SJan Kneschke * */ 504*7d875859SJan Kneschke if (S_ISLNK(st.st_mode) && !con->conf.follow_symlink) { 505*7d875859SJan Kneschke return HANDLER_ERROR; 506*7d875859SJan Kneschke } 507*7d875859SJan Kneschke 5085e134da0SJan Kneschke if (S_ISREG(st.st_mode)) { 5095e134da0SJan Kneschke /* determine mimetype */ 5105e134da0SJan Kneschke buffer_reset(sce->content_type); 5115e134da0SJan Kneschke 5125e134da0SJan Kneschke for (k = 0; k < con->conf.mimetypes->used; k++) { 5135e134da0SJan Kneschke data_string *ds = (data_string *)con->conf.mimetypes->data[k]; 51402125345SJan Kneschke buffer *type = ds->key; 5155e134da0SJan Kneschke 51602125345SJan Kneschke if (type->used == 0) continue; 5175e134da0SJan Kneschke 51802125345SJan Kneschke /* check if the right side is the same */ 51902125345SJan Kneschke if (type->used > name->used) continue; 52002125345SJan Kneschke 52102125345SJan Kneschke if (0 == strncasecmp(name->ptr + name->used - type->used, type->ptr, type->used - 1)) { 5225e134da0SJan Kneschke buffer_copy_string_buffer(sce->content_type, ds->value); 5235e134da0SJan Kneschke break; 5245e134da0SJan Kneschke } 5255e134da0SJan Kneschke } 5265e134da0SJan Kneschke etag_create(sce->etag, &(sce->st)); 5275e134da0SJan Kneschke #ifdef HAVE_XATTR 5285e134da0SJan Kneschke if (buffer_is_empty(sce->content_type)) { 5295e134da0SJan Kneschke stat_cache_attr_get(sce->content_type, name->ptr); 5305e134da0SJan Kneschke } 5315e134da0SJan Kneschke #endif 5325e134da0SJan Kneschke } 5335e134da0SJan Kneschke 5345e134da0SJan Kneschke #ifdef HAVE_FAM_H 53579d3bc44SJan Kneschke if (sc->fam && 53679d3bc44SJan Kneschke (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM)) { 5375e134da0SJan Kneschke /* is this directory already registered ? */ 5385e134da0SJan Kneschke if (!dir_node) { 5395e134da0SJan Kneschke fam_dir = fam_dir_entry_init(); 5405e134da0SJan Kneschke fam_dir->fc = sc->fam; 5415e134da0SJan Kneschke 5425e134da0SJan Kneschke buffer_copy_string_buffer(fam_dir->name, sc->dir_name); 5435e134da0SJan Kneschke 5445e134da0SJan Kneschke fam_dir->version = 1; 5455e134da0SJan Kneschke 5465e134da0SJan Kneschke fam_dir->req = calloc(1, sizeof(FAMRequest)); 5475e134da0SJan Kneschke 5485e134da0SJan Kneschke if (0 != FAMMonitorDirectory(sc->fam, fam_dir->name->ptr, 5495e134da0SJan Kneschke fam_dir->req, fam_dir)) { 5505e134da0SJan Kneschke 5515e134da0SJan Kneschke log_error_write(srv, __FILE__, __LINE__, "sbs", 5525e134da0SJan Kneschke "monitoring dir failed:", 5535e134da0SJan Kneschke fam_dir->name, 5545e134da0SJan Kneschke FamErrlist[FAMErrno]); 5555e134da0SJan Kneschke 5565e134da0SJan Kneschke fam_dir_entry_free(fam_dir); 5575e134da0SJan Kneschke } else { 558ecb30c4eSJan Kneschke int osize = 0; 559ecb30c4eSJan Kneschke 560ecb30c4eSJan Kneschke if (sc->dirs) { 561ecb30c4eSJan Kneschke osize = sc->dirs->size; 562ecb30c4eSJan Kneschke } 563ecb30c4eSJan Kneschke 5645e134da0SJan Kneschke sc->dirs = splaytree_insert(sc->dirs, dir_ndx, fam_dir); 565ecb30c4eSJan Kneschke assert(sc->dirs); 566b795fd36SJan Kneschke assert(sc->dirs->data == fam_dir); 567ecb30c4eSJan Kneschke assert(osize == (sc->dirs->size - 1)); 5685e134da0SJan Kneschke } 5695e134da0SJan Kneschke } else { 5705e134da0SJan Kneschke fam_dir = dir_node->data; 5715e134da0SJan Kneschke } 5725e134da0SJan Kneschke 5735e134da0SJan Kneschke /* bind the fam_fc to the stat() cache entry */ 5745e134da0SJan Kneschke 5755e134da0SJan Kneschke if (fam_dir) { 5765e134da0SJan Kneschke sce->dir_version = fam_dir->version; 5775e134da0SJan Kneschke sce->dir_ndx = dir_ndx; 5785e134da0SJan Kneschke } 5795e134da0SJan Kneschke } 5805e134da0SJan Kneschke #endif 58151978868SJan Kneschke 5825e134da0SJan Kneschke *ret_sce = sce; 5835e134da0SJan Kneschke 5845e134da0SJan Kneschke return HANDLER_GO_ON; 5855e134da0SJan Kneschke } 5865e134da0SJan Kneschke 5875e134da0SJan Kneschke /** 5885e134da0SJan Kneschke * remove stat() from cache which havn't been stat()ed for 5895e134da0SJan Kneschke * more than 10 seconds 5905e134da0SJan Kneschke * 5915e134da0SJan Kneschke * 5925e134da0SJan Kneschke * walk though the stat-cache, collect the ids which are too old 5935e134da0SJan Kneschke * and remove them in a second loop 5945e134da0SJan Kneschke */ 5955e134da0SJan Kneschke 5965e134da0SJan Kneschke static int stat_cache_tag_old_entries(server *srv, splay_tree *t, int *keys, size_t *ndx) { 5975e134da0SJan Kneschke stat_cache_entry *sce; 5985e134da0SJan Kneschke 5995e134da0SJan Kneschke if (!t) return 0; 6005e134da0SJan Kneschke 6015e134da0SJan Kneschke stat_cache_tag_old_entries(srv, t->left, keys, ndx); 6025e134da0SJan Kneschke stat_cache_tag_old_entries(srv, t->right, keys, ndx); 6035e134da0SJan Kneschke 6045e134da0SJan Kneschke sce = t->data; 6055e134da0SJan Kneschke 606b795fd36SJan Kneschke if (srv->cur_ts - sce->stat_ts > 2) { 6075e134da0SJan Kneschke keys[(*ndx)++] = t->key; 6085e134da0SJan Kneschke } 6095e134da0SJan Kneschke 6105e134da0SJan Kneschke return 0; 6115e134da0SJan Kneschke } 6125e134da0SJan Kneschke 6135e134da0SJan Kneschke int stat_cache_trigger_cleanup(server *srv) { 6145e134da0SJan Kneschke stat_cache *sc; 61556f637b8SJan Kneschke size_t max_ndx = 0, i; 6165e134da0SJan Kneschke int *keys; 6175e134da0SJan Kneschke 6185e134da0SJan Kneschke sc = srv->stat_cache; 6195e134da0SJan Kneschke 6205e134da0SJan Kneschke if (!sc->files) return 0; 6215e134da0SJan Kneschke 6225e134da0SJan Kneschke keys = calloc(1, sizeof(size_t) * sc->files->size); 6235e134da0SJan Kneschke 6245e134da0SJan Kneschke stat_cache_tag_old_entries(srv, sc->files, keys, &max_ndx); 6255e134da0SJan Kneschke 6265e134da0SJan Kneschke for (i = 0; i < max_ndx; i++) { 6275e134da0SJan Kneschke int ndx = keys[i]; 6285e134da0SJan Kneschke splay_tree *node; 6295e134da0SJan Kneschke 6305e134da0SJan Kneschke sc->files = splaytree_splay(sc->files, ndx); 6315e134da0SJan Kneschke 6325e134da0SJan Kneschke node = sc->files; 6335e134da0SJan Kneschke 6345e134da0SJan Kneschke if (node && (node->key == ndx)) { 63556f637b8SJan Kneschke #ifdef DEBUG_STAT_CACHE 63656f637b8SJan Kneschke size_t j; 637b795fd36SJan Kneschke int osize = splaytree_size(sc->files); 638b795fd36SJan Kneschke stat_cache_entry *sce = node->data; 63956f637b8SJan Kneschke #endif 6405e134da0SJan Kneschke stat_cache_entry_free(node->data); 6415e134da0SJan Kneschke sc->files = splaytree_delete(sc->files, ndx); 642d1e70da8SJan Kneschke 643b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 644b795fd36SJan Kneschke for (j = 0; j < ctrl.used; j++) { 645b795fd36SJan Kneschke if (ctrl.ptr[j] == ndx) { 646b795fd36SJan Kneschke ctrl.ptr[j] = ctrl.ptr[--ctrl.used]; 647b795fd36SJan Kneschke break; 648ecb30c4eSJan Kneschke } 6495e134da0SJan Kneschke } 650b795fd36SJan Kneschke 651b795fd36SJan Kneschke assert(osize - 1 == splaytree_size(sc->files)); 652b795fd36SJan Kneschke #endif 653b795fd36SJan Kneschke } 6545e134da0SJan Kneschke } 6555e134da0SJan Kneschke 6565e134da0SJan Kneschke free(keys); 6575e134da0SJan Kneschke 6585e134da0SJan Kneschke return 0; 6595e134da0SJan Kneschke } 660