122e8b456SStefan Bühler #include "log.h" 222e8b456SStefan Bühler #include "stat_cache.h" 322e8b456SStefan Bühler #include "fdevent.h" 422e8b456SStefan Bühler #include "etag.h" 55e134da0SJan Kneschke 65e134da0SJan Kneschke #include <sys/types.h> 75e134da0SJan Kneschke #include <sys/stat.h> 85e134da0SJan Kneschke 95e134da0SJan Kneschke #include <stdlib.h> 105e134da0SJan Kneschke #include <string.h> 115e134da0SJan Kneschke #include <errno.h> 125e134da0SJan Kneschke #include <unistd.h> 135e134da0SJan Kneschke #include <stdio.h> 145e134da0SJan Kneschke #include <fcntl.h> 15ecb30c4eSJan Kneschke #include <assert.h> 165e134da0SJan Kneschke 175e134da0SJan Kneschke #ifdef HAVE_ATTR_ATTRIBUTES_H 185e134da0SJan Kneschke # include <attr/attributes.h> 195e134da0SJan Kneschke #endif 205e134da0SJan Kneschke 215e134da0SJan Kneschke #ifdef HAVE_FAM_H 225e134da0SJan Kneschke # include <fam.h> 235e134da0SJan Kneschke #endif 245e134da0SJan Kneschke 255e134da0SJan Kneschke #include "sys-mmap.h" 265e134da0SJan Kneschke 275e134da0SJan Kneschke /* NetBSD 1.3.x needs it */ 285e134da0SJan Kneschke #ifndef MAP_FAILED 295e134da0SJan Kneschke # define MAP_FAILED -1 305e134da0SJan Kneschke #endif 315e134da0SJan Kneschke 325e134da0SJan Kneschke #ifndef O_LARGEFILE 335e134da0SJan Kneschke # define O_LARGEFILE 0 345e134da0SJan Kneschke #endif 355e134da0SJan Kneschke 365e134da0SJan Kneschke #ifndef HAVE_LSTAT 375e134da0SJan Kneschke # define lstat stat 385e134da0SJan Kneschke #endif 395e134da0SJan Kneschke 400f190e16SMarcus Rückert #if 0 41b795fd36SJan Kneschke /* enables debug code for testing if all nodes in the stat-cache as accessable */ 42b795fd36SJan Kneschke #define DEBUG_STAT_CACHE 43b795fd36SJan Kneschke #endif 44b795fd36SJan Kneschke 455e134da0SJan Kneschke /* 465e134da0SJan Kneschke * stat-cache 475e134da0SJan Kneschke * 485e134da0SJan Kneschke * we cache the stat() calls in our own storage 495e134da0SJan Kneschke * the directories are cached in FAM 505e134da0SJan Kneschke * 515e134da0SJan Kneschke * if we get a change-event from FAM, we increment the version in the FAM->dir mapping 525e134da0SJan Kneschke * 535e134da0SJan Kneschke * if the stat()-cache is queried we check if the version id for the directory is the 545e134da0SJan Kneschke * same and return immediatly. 555e134da0SJan Kneschke * 565e134da0SJan Kneschke * 575e134da0SJan Kneschke * What we need: 585e134da0SJan Kneschke * 595e134da0SJan Kneschke * - for each stat-cache entry we need a fast indirect lookup on the directory name 605e134da0SJan Kneschke * - for each FAMRequest we have to find the version in the directory cache (index as userdata) 615e134da0SJan Kneschke * 625e134da0SJan Kneschke * stat <<-> directory <-> FAMRequest 635e134da0SJan Kneschke * 645e134da0SJan Kneschke * if file is deleted, directory is dirty, file is rechecked ... 655e134da0SJan Kneschke * if directory is deleted, directory mapping is removed 665e134da0SJan Kneschke * 675e134da0SJan Kneschke * */ 685e134da0SJan Kneschke 695e134da0SJan Kneschke #ifdef HAVE_FAM_H 705e134da0SJan Kneschke typedef struct { 715e134da0SJan Kneschke FAMRequest *req; 725e134da0SJan Kneschke 735e134da0SJan Kneschke buffer *name; 745e134da0SJan Kneschke 755e134da0SJan Kneschke int version; 765e134da0SJan Kneschke } fam_dir_entry; 775e134da0SJan Kneschke #endif 785e134da0SJan Kneschke 795e134da0SJan Kneschke /* the directory name is too long to always compare on it 805e134da0SJan Kneschke * - we need a hash 815e134da0SJan Kneschke * - the hash-key is used as sorting criteria for a tree 825e134da0SJan Kneschke * - a splay-tree is used as we can use the caching effect of it 835e134da0SJan Kneschke */ 845e134da0SJan Kneschke 855e134da0SJan Kneschke /* we want to cleanup the stat-cache every few seconds, let's say 10 865e134da0SJan Kneschke * 875e134da0SJan Kneschke * - remove entries which are outdated since 30s 885e134da0SJan Kneschke * - remove entries which are fresh but havn't been used since 60s 895e134da0SJan Kneschke * - if we don't have a stat-cache entry for a directory, release it from the monitor 905e134da0SJan Kneschke */ 915e134da0SJan Kneschke 92b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 93b795fd36SJan Kneschke typedef struct { 94b795fd36SJan Kneschke int *ptr; 95b795fd36SJan Kneschke 96b795fd36SJan Kneschke size_t used; 97b795fd36SJan Kneschke size_t size; 98b795fd36SJan Kneschke } fake_keys; 99b795fd36SJan Kneschke 100b795fd36SJan Kneschke static fake_keys ctrl; 101b795fd36SJan Kneschke #endif 102b795fd36SJan Kneschke 1035e134da0SJan Kneschke stat_cache *stat_cache_init(void) { 104d8b363c1SStefan Bühler stat_cache *sc = NULL; 1055e134da0SJan Kneschke 106d8b363c1SStefan Bühler sc = calloc(1, sizeof(*sc)); 1075e134da0SJan Kneschke 108d8b363c1SStefan Bühler sc->dir_name = buffer_init(); 109d8b363c1SStefan Bühler sc->hash_key = buffer_init(); 110d8b363c1SStefan Bühler 1115e134da0SJan Kneschke #ifdef HAVE_FAM_H 112d8b363c1SStefan Bühler sc->fam_fcce_ndx = -1; 11379d3bc44SJan Kneschke #endif 1145e134da0SJan Kneschke 115b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 116b795fd36SJan Kneschke ctrl.size = 0; 117b795fd36SJan Kneschke #endif 118b795fd36SJan Kneschke 119d8b363c1SStefan Bühler return sc; 1205e134da0SJan Kneschke } 1215e134da0SJan Kneschke 1225e134da0SJan Kneschke static stat_cache_entry * stat_cache_entry_init(void) { 1235e134da0SJan Kneschke stat_cache_entry *sce = NULL; 1245e134da0SJan Kneschke 1255e134da0SJan Kneschke sce = calloc(1, sizeof(*sce)); 1265e134da0SJan Kneschke 1275e134da0SJan Kneschke sce->name = buffer_init(); 1285e134da0SJan Kneschke sce->etag = buffer_init(); 1295e134da0SJan Kneschke sce->content_type = buffer_init(); 1305e134da0SJan Kneschke 1315e134da0SJan Kneschke return sce; 1325e134da0SJan Kneschke } 1335e134da0SJan Kneschke 1345e134da0SJan Kneschke static void stat_cache_entry_free(void *data) { 1355e134da0SJan Kneschke stat_cache_entry *sce = data; 1365e134da0SJan Kneschke if (!sce) return; 1375e134da0SJan Kneschke 1385e134da0SJan Kneschke buffer_free(sce->etag); 1395e134da0SJan Kneschke buffer_free(sce->name); 1405e134da0SJan Kneschke buffer_free(sce->content_type); 1415e134da0SJan Kneschke 1425e134da0SJan Kneschke free(sce); 1435e134da0SJan Kneschke } 1445e134da0SJan Kneschke 1455e134da0SJan Kneschke #ifdef HAVE_FAM_H 1465e134da0SJan Kneschke static fam_dir_entry * fam_dir_entry_init(void) { 1475e134da0SJan Kneschke fam_dir_entry *fam_dir = NULL; 1485e134da0SJan Kneschke 1495e134da0SJan Kneschke fam_dir = calloc(1, sizeof(*fam_dir)); 1505e134da0SJan Kneschke 1515e134da0SJan Kneschke fam_dir->name = buffer_init(); 1525e134da0SJan Kneschke 1535e134da0SJan Kneschke return fam_dir; 1545e134da0SJan Kneschke } 1555e134da0SJan Kneschke 156d8b363c1SStefan Bühler static void fam_dir_entry_free(FAMConnection *fc, void *data) { 1575e134da0SJan Kneschke fam_dir_entry *fam_dir = data; 1585e134da0SJan Kneschke 1595e134da0SJan Kneschke if (!fam_dir) return; 1605e134da0SJan Kneschke 161d8b363c1SStefan Bühler FAMCancelMonitor(fc, fam_dir->req); 1625e134da0SJan Kneschke 1635e134da0SJan Kneschke buffer_free(fam_dir->name); 1645e134da0SJan Kneschke free(fam_dir->req); 1655e134da0SJan Kneschke 1665e134da0SJan Kneschke free(fam_dir); 1675e134da0SJan Kneschke } 1685e134da0SJan Kneschke #endif 1695e134da0SJan Kneschke 170d1e70da8SJan Kneschke void stat_cache_free(stat_cache *sc) { 171d1e70da8SJan Kneschke while (sc->files) { 172ecb30c4eSJan Kneschke int osize; 173d1e70da8SJan Kneschke splay_tree *node = sc->files; 1745e134da0SJan Kneschke 175ecb30c4eSJan Kneschke osize = sc->files->size; 176ecb30c4eSJan Kneschke 177d1e70da8SJan Kneschke stat_cache_entry_free(node->data); 178d1e70da8SJan Kneschke sc->files = splaytree_delete(sc->files, node->key); 179d1e70da8SJan Kneschke 180*07dd0bd0SStefan Bühler force_assert(osize - 1 == splaytree_size(sc->files)); 181d1e70da8SJan Kneschke } 182d1e70da8SJan Kneschke 183d1e70da8SJan Kneschke buffer_free(sc->dir_name); 1849de38074SJan Kneschke buffer_free(sc->hash_key); 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 193d8b363c1SStefan Bühler fam_dir_entry_free(&sc->fam, node->data); 194d1e70da8SJan Kneschke sc->dirs = splaytree_delete(sc->dirs, node->key); 195d1e70da8SJan Kneschke 196ecb30c4eSJan Kneschke if (osize == 1) { 197*07dd0bd0SStefan Bühler force_assert(NULL == sc->dirs); 198ecb30c4eSJan Kneschke } else { 199*07dd0bd0SStefan Bühler force_assert(osize == (sc->dirs->size + 1)); 200ecb30c4eSJan Kneschke } 201d1e70da8SJan Kneschke } 202d1e70da8SJan Kneschke 203d8b363c1SStefan Bühler if (-1 != sc->fam_fcce_ndx) { 204d8b363c1SStefan Bühler /* fd events already gone */ 205d8b363c1SStefan Bühler sc->fam_fcce_ndx = -1; 206d8b363c1SStefan Bühler 207d8b363c1SStefan Bühler FAMClose(&sc->fam); 20879d3bc44SJan Kneschke } 2095e134da0SJan Kneschke #endif 210d1e70da8SJan Kneschke free(sc); 2115e134da0SJan Kneschke } 2125e134da0SJan Kneschke 2135e134da0SJan Kneschke #ifdef HAVE_XATTR 21430b19732SJan Kneschke static int stat_cache_attr_get(buffer *buf, char *name) { 2155e134da0SJan Kneschke int attrlen; 2165e134da0SJan Kneschke int ret; 2175e134da0SJan Kneschke 2185e134da0SJan Kneschke attrlen = 1024; 2195e134da0SJan Kneschke buffer_prepare_copy(buf, attrlen); 2205e134da0SJan Kneschke attrlen--; 2215e134da0SJan Kneschke if(0 == (ret = attr_get(name, "Content-Type", buf->ptr, &attrlen, 0))) { 2225e134da0SJan Kneschke buf->used = attrlen + 1; 2235e134da0SJan Kneschke buf->ptr[attrlen] = '\0'; 2245e134da0SJan Kneschke } 2255e134da0SJan Kneschke return ret; 2265e134da0SJan Kneschke } 2275e134da0SJan Kneschke #endif 2285e134da0SJan Kneschke 229b795fd36SJan Kneschke /* the famous DJB hash function for strings */ 230b795fd36SJan Kneschke static uint32_t hashme(buffer *str) { 231b795fd36SJan Kneschke uint32_t hash = 5381; 2325e134da0SJan Kneschke const char *s; 2335e134da0SJan Kneschke for (s = str->ptr; *s; s++) { 234b795fd36SJan Kneschke hash = ((hash << 5) + hash) + *s; 2355e134da0SJan Kneschke } 2365e134da0SJan Kneschke 237b795fd36SJan Kneschke hash &= ~(1 << 31); /* strip the highest bit */ 238b795fd36SJan Kneschke 2395e134da0SJan Kneschke return hash; 2405e134da0SJan Kneschke } 2415e134da0SJan Kneschke 2425e134da0SJan Kneschke #ifdef HAVE_FAM_H 24338f2d1ddSStefan Bühler handler_t stat_cache_handle_fdevent(server *srv, void *_fce, int revent) { 2445e134da0SJan Kneschke size_t i; 2455e134da0SJan Kneschke stat_cache *sc = srv->stat_cache; 2465e134da0SJan Kneschke size_t events; 2475e134da0SJan Kneschke 24856f637b8SJan Kneschke UNUSED(_fce); 2495e134da0SJan Kneschke /* */ 2505e134da0SJan Kneschke 251d8b363c1SStefan Bühler if (revent & FDEVENT_IN) { 252d8b363c1SStefan Bühler events = FAMPending(&sc->fam); 2535e134da0SJan Kneschke 2545e134da0SJan Kneschke for (i = 0; i < events; i++) { 2555e134da0SJan Kneschke FAMEvent fe; 2565e134da0SJan Kneschke fam_dir_entry *fam_dir; 2575e134da0SJan Kneschke splay_tree *node; 2589de38074SJan Kneschke int ndx, j; 2595e134da0SJan Kneschke 260d8b363c1SStefan Bühler FAMNextEvent(&sc->fam, &fe); 2615e134da0SJan Kneschke 2625e134da0SJan Kneschke /* handle event */ 2635e134da0SJan Kneschke 2645e134da0SJan Kneschke switch(fe.code) { 2655e134da0SJan Kneschke case FAMChanged: 2665e134da0SJan Kneschke case FAMDeleted: 2675e134da0SJan Kneschke case FAMMoved: 2685e134da0SJan Kneschke /* if the filename is a directory remove the entry */ 2695e134da0SJan Kneschke 2705e134da0SJan Kneschke fam_dir = fe.userdata; 2715e134da0SJan Kneschke fam_dir->version++; 2725e134da0SJan Kneschke 2735e134da0SJan Kneschke /* file/dir is still here */ 2745e134da0SJan Kneschke if (fe.code == FAMChanged) break; 2755e134da0SJan Kneschke 2769de38074SJan Kneschke /* we have 2 versions, follow and no-follow-symlink */ 2775e134da0SJan Kneschke 2789de38074SJan Kneschke for (j = 0; j < 2; j++) { 2799de38074SJan Kneschke buffer_copy_string(sc->hash_key, fe.filename); 2809de38074SJan Kneschke buffer_append_long(sc->hash_key, j); 2819de38074SJan Kneschke 2829de38074SJan Kneschke ndx = hashme(sc->hash_key); 2835e134da0SJan Kneschke 2845e134da0SJan Kneschke sc->dirs = splaytree_splay(sc->dirs, ndx); 2855e134da0SJan Kneschke node = sc->dirs; 2865e134da0SJan Kneschke 2875e134da0SJan Kneschke if (node && (node->key == ndx)) { 288b795fd36SJan Kneschke int osize = splaytree_size(sc->dirs); 289d1e70da8SJan Kneschke 290d8b363c1SStefan Bühler fam_dir_entry_free(&sc->fam, node->data); 2915e134da0SJan Kneschke sc->dirs = splaytree_delete(sc->dirs, ndx); 292d1e70da8SJan Kneschke 293*07dd0bd0SStefan Bühler force_assert(osize - 1 == splaytree_size(sc->dirs)); 2945e134da0SJan Kneschke } 2959de38074SJan Kneschke } 2965e134da0SJan Kneschke break; 2975e134da0SJan Kneschke default: 2985e134da0SJan Kneschke break; 2995e134da0SJan Kneschke } 3005e134da0SJan Kneschke } 30179d3bc44SJan Kneschke } 30279d3bc44SJan Kneschke 30379d3bc44SJan Kneschke if (revent & FDEVENT_HUP) { 30479d3bc44SJan Kneschke /* fam closed the connection */ 305d8b363c1SStefan Bühler fdevent_event_del(srv->ev, &(sc->fam_fcce_ndx), FAMCONNECTION_GETFD(&sc->fam)); 306d8b363c1SStefan Bühler fdevent_unregister(srv->ev, FAMCONNECTION_GETFD(&sc->fam)); 30779d3bc44SJan Kneschke 308d8b363c1SStefan Bühler FAMClose(&sc->fam); 30979d3bc44SJan Kneschke } 3105e134da0SJan Kneschke 3115e134da0SJan Kneschke return HANDLER_GO_ON; 3125e134da0SJan Kneschke } 3135e134da0SJan Kneschke 3145e134da0SJan Kneschke static int buffer_copy_dirname(buffer *dst, buffer *file) { 3155e134da0SJan Kneschke size_t i; 3165e134da0SJan Kneschke 3175e134da0SJan Kneschke if (buffer_is_empty(file)) return -1; 3185e134da0SJan Kneschke 3195e134da0SJan Kneschke for (i = file->used - 1; i+1 > 0; i--) { 3205e134da0SJan Kneschke if (file->ptr[i] == '/') { 3215e134da0SJan Kneschke buffer_copy_string_len(dst, file->ptr, i); 3225e134da0SJan Kneschke return 0; 3235e134da0SJan Kneschke } 3245e134da0SJan Kneschke } 3255e134da0SJan Kneschke 3265e134da0SJan Kneschke return -1; 3275e134da0SJan Kneschke } 3285e134da0SJan Kneschke #endif 3295e134da0SJan Kneschke 330657a024dSMarcus Rückert #ifdef HAVE_LSTAT 33108994e47SJan Kneschke static int stat_cache_lstat(server *srv, buffer *dname, struct stat *lst) { 33208994e47SJan Kneschke if (lstat(dname->ptr, lst) == 0) { 333657a024dSMarcus Rückert return S_ISLNK(lst->st_mode) ? 0 : 1; 334657a024dSMarcus Rückert } 335657a024dSMarcus Rückert else { 33608994e47SJan Kneschke log_error_write(srv, __FILE__, __LINE__, "sbs", 337657a024dSMarcus Rückert "lstat failed for:", 338657a024dSMarcus Rückert dname, strerror(errno)); 339657a024dSMarcus Rückert }; 340657a024dSMarcus Rückert return -1; 341657a024dSMarcus Rückert } 342657a024dSMarcus Rückert #endif 343657a024dSMarcus Rückert 3445e134da0SJan Kneschke /*** 3455e134da0SJan Kneschke * 3465e134da0SJan Kneschke * 3475e134da0SJan Kneschke * 3485e134da0SJan Kneschke * returns: 3495e134da0SJan Kneschke * - HANDLER_FINISHED on cache-miss (don't forget to reopen the file) 3505e134da0SJan Kneschke * - HANDLER_ERROR on stat() failed -> see errno for problem 3515e134da0SJan Kneschke */ 3525e134da0SJan Kneschke 3535e134da0SJan Kneschke handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_cache_entry **ret_sce) { 3545e134da0SJan Kneschke #ifdef HAVE_FAM_H 3555e134da0SJan Kneschke fam_dir_entry *fam_dir = NULL; 3565e134da0SJan Kneschke int dir_ndx = -1; 3575e134da0SJan Kneschke splay_tree *dir_node = NULL; 3585e134da0SJan Kneschke #endif 3595e134da0SJan Kneschke stat_cache_entry *sce = NULL; 3605e134da0SJan Kneschke stat_cache *sc; 3615e134da0SJan Kneschke struct stat st; 36251978868SJan Kneschke size_t k; 3637d875859SJan Kneschke int fd; 36408994e47SJan Kneschke struct stat lst; 36556f637b8SJan Kneschke #ifdef DEBUG_STAT_CACHE 366b795fd36SJan Kneschke size_t i; 36756f637b8SJan Kneschke #endif 3685e134da0SJan Kneschke 3695e134da0SJan Kneschke int file_ndx; 3705e134da0SJan Kneschke splay_tree *file_node = NULL; 3715e134da0SJan Kneschke 3725e134da0SJan Kneschke *ret_sce = NULL; 3735e134da0SJan Kneschke 3745e134da0SJan Kneschke /* 3755e134da0SJan Kneschke * check if the directory for this file has changed 3765e134da0SJan Kneschke */ 3775e134da0SJan Kneschke 3785e134da0SJan Kneschke sc = srv->stat_cache; 3795e134da0SJan Kneschke 3809de38074SJan Kneschke buffer_copy_string_buffer(sc->hash_key, name); 3819de38074SJan Kneschke buffer_append_long(sc->hash_key, con->conf.follow_symlink); 3829de38074SJan Kneschke 3839de38074SJan Kneschke file_ndx = hashme(sc->hash_key); 3845e134da0SJan Kneschke sc->files = splaytree_splay(sc->files, file_ndx); 3855e134da0SJan Kneschke 386b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 387b795fd36SJan Kneschke for (i = 0; i < ctrl.used; i++) { 388b795fd36SJan Kneschke if (ctrl.ptr[i] == file_ndx) break; 389b795fd36SJan Kneschke } 390b795fd36SJan Kneschke #endif 391b795fd36SJan Kneschke 3925e134da0SJan Kneschke if (sc->files && (sc->files->key == file_ndx)) { 393b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 394b795fd36SJan Kneschke /* it was in the cache */ 395*07dd0bd0SStefan Bühler force_assert(i < ctrl.used); 396b795fd36SJan Kneschke #endif 397b795fd36SJan Kneschke 3985e134da0SJan Kneschke /* we have seen this file already and 3995e134da0SJan Kneschke * don't stat() it again in the same second */ 4005e134da0SJan Kneschke 4015e134da0SJan Kneschke file_node = sc->files; 4025e134da0SJan Kneschke 4035e134da0SJan Kneschke sce = file_node->data; 4045e134da0SJan Kneschke 405ecb30c4eSJan Kneschke /* check if the name is the same, we might have a collision */ 406ecb30c4eSJan Kneschke 407ecb30c4eSJan Kneschke if (buffer_is_equal(name, sce->name)) { 4085e134da0SJan Kneschke if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_SIMPLE) { 4095e134da0SJan Kneschke if (sce->stat_ts == srv->cur_ts) { 4105e134da0SJan Kneschke *ret_sce = sce; 4115e134da0SJan Kneschke return HANDLER_GO_ON; 4125e134da0SJan Kneschke } 4135e134da0SJan Kneschke } 414ecb30c4eSJan Kneschke } else { 415ecb30c4eSJan Kneschke /* oops, a collision, 416ecb30c4eSJan Kneschke * 417ecb30c4eSJan Kneschke * file_node is used by the FAM check below to see if we know this file 418ecb30c4eSJan Kneschke * and if we can save a stat(). 419ecb30c4eSJan Kneschke * 420ecb30c4eSJan Kneschke * BUT, the sce is not reset here as the entry into the cache is ok, we 421ecb30c4eSJan Kneschke * it is just not pointing to our requested file. 422ecb30c4eSJan Kneschke * 423ecb30c4eSJan Kneschke * */ 424ecb30c4eSJan Kneschke 425ecb30c4eSJan Kneschke file_node = NULL; 426ecb30c4eSJan Kneschke } 427b795fd36SJan Kneschke } else { 428b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 429b795fd36SJan Kneschke if (i != ctrl.used) { 43038f2d1ddSStefan Bühler log_error_write(srv, __FILE__, __LINE__, "xSB", 43138f2d1ddSStefan Bühler file_ndx, "was already inserted but not found in cache, ", name); 432b795fd36SJan Kneschke } 433*07dd0bd0SStefan Bühler force_assert(i == ctrl.used); 434b795fd36SJan Kneschke #endif 4355e134da0SJan Kneschke } 4365e134da0SJan Kneschke 4375e134da0SJan Kneschke #ifdef HAVE_FAM_H 4385e134da0SJan Kneschke /* dir-check */ 4395e134da0SJan Kneschke if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { 4405e134da0SJan Kneschke if (0 != buffer_copy_dirname(sc->dir_name, name)) { 4417a64f2f8SStefan Bühler log_error_write(srv, __FILE__, __LINE__, "sb", 4427a64f2f8SStefan Bühler "no '/' found in filename:", name); 4437a64f2f8SStefan Bühler return HANDLER_ERROR; 4445e134da0SJan Kneschke } 4455e134da0SJan Kneschke 4469de38074SJan Kneschke buffer_copy_string_buffer(sc->hash_key, sc->dir_name); 4479de38074SJan Kneschke buffer_append_long(sc->hash_key, con->conf.follow_symlink); 4489de38074SJan Kneschke 4499de38074SJan Kneschke dir_ndx = hashme(sc->hash_key); 4505e134da0SJan Kneschke 4515e134da0SJan Kneschke sc->dirs = splaytree_splay(sc->dirs, dir_ndx); 4525e134da0SJan Kneschke 4535e134da0SJan Kneschke if (sc->dirs && (sc->dirs->key == dir_ndx)) { 4545e134da0SJan Kneschke dir_node = sc->dirs; 4555e134da0SJan Kneschke } 4565e134da0SJan Kneschke 4575e134da0SJan Kneschke if (dir_node && file_node) { 4585e134da0SJan Kneschke /* we found a file */ 4595e134da0SJan Kneschke 4605e134da0SJan Kneschke sce = file_node->data; 4615e134da0SJan Kneschke fam_dir = dir_node->data; 4625e134da0SJan Kneschke 4635e134da0SJan Kneschke if (fam_dir->version == sce->dir_version) { 4645e134da0SJan Kneschke /* the stat()-cache entry is still ok */ 4655e134da0SJan Kneschke 4665e134da0SJan Kneschke *ret_sce = sce; 4675e134da0SJan Kneschke return HANDLER_GO_ON; 4685e134da0SJan Kneschke } 4695e134da0SJan Kneschke } 4705e134da0SJan Kneschke } 4715e134da0SJan Kneschke #endif 472478cb34bSJan Kneschke 473478cb34bSJan Kneschke /* 474478cb34bSJan Kneschke * *lol* 475478cb34bSJan Kneschke * - open() + fstat() on a named-pipe results in a (intended) hang. 476657a024dSMarcus Rückert * - stat() if regular file + open() to see if we can read from it is better 477478cb34bSJan Kneschke * 478478cb34bSJan Kneschke * */ 479478cb34bSJan Kneschke if (-1 == stat(name->ptr, &st)) { 480478cb34bSJan Kneschke return HANDLER_ERROR; 481478cb34bSJan Kneschke } 482478cb34bSJan Kneschke 483478cb34bSJan Kneschke 484478cb34bSJan Kneschke if (S_ISREG(st.st_mode)) { 48557066345SStefan Bühler /* fix broken stat/open for symlinks to reg files with appended slash on freebsd,osx */ 48657066345SStefan Bühler if (name->ptr[name->used-2] == '/') { 48757066345SStefan Bühler errno = ENOTDIR; 48857066345SStefan Bühler return HANDLER_ERROR; 48957066345SStefan Bühler } 49057066345SStefan Bühler 491478cb34bSJan Kneschke /* try to open the file to check if we can read it */ 492603d6aceSJan Kneschke if (-1 == (fd = open(name->ptr, O_RDONLY))) { 493603d6aceSJan Kneschke return HANDLER_ERROR; 494603d6aceSJan Kneschke } 495603d6aceSJan Kneschke close(fd); 496603d6aceSJan Kneschke } 497603d6aceSJan Kneschke 4985e134da0SJan Kneschke if (NULL == sce) { 4994df22f2aSStefan Bühler #ifdef DEBUG_STAT_CACHE 5004df22f2aSStefan Bühler int osize = splaytree_size(sc->files); 5014df22f2aSStefan Bühler #endif 502ecb30c4eSJan Kneschke 5035e134da0SJan Kneschke sce = stat_cache_entry_init(); 5045e134da0SJan Kneschke buffer_copy_string_buffer(sce->name, name); 5055e134da0SJan Kneschke 5065e134da0SJan Kneschke sc->files = splaytree_insert(sc->files, file_ndx, sce); 507b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 508b795fd36SJan Kneschke if (ctrl.size == 0) { 509b795fd36SJan Kneschke ctrl.size = 16; 510b795fd36SJan Kneschke ctrl.used = 0; 511b795fd36SJan Kneschke ctrl.ptr = malloc(ctrl.size * sizeof(*ctrl.ptr)); 512b795fd36SJan Kneschke } else if (ctrl.size == ctrl.used) { 513b795fd36SJan Kneschke ctrl.size += 16; 514b795fd36SJan Kneschke ctrl.ptr = realloc(ctrl.ptr, ctrl.size * sizeof(*ctrl.ptr)); 515b795fd36SJan Kneschke } 516b795fd36SJan Kneschke 517b795fd36SJan Kneschke ctrl.ptr[ctrl.used++] = file_ndx; 518ecb30c4eSJan Kneschke 519*07dd0bd0SStefan Bühler force_assert(sc->files); 520*07dd0bd0SStefan Bühler force_assert(sc->files->data == sce); 521*07dd0bd0SStefan Bühler force_assert(osize + 1 == splaytree_size(sc->files)); 522b795fd36SJan Kneschke #endif 5235e134da0SJan Kneschke } 5245e134da0SJan Kneschke 5255e134da0SJan Kneschke sce->st = st; 5265e134da0SJan Kneschke sce->stat_ts = srv->cur_ts; 5275e134da0SJan Kneschke 5287d875859SJan Kneschke /* catch the obvious symlinks 5297d875859SJan Kneschke * 5307d875859SJan Kneschke * this is not a secure check as we still have a race-condition between 5317d875859SJan Kneschke * the stat() and the open. We can only solve this by 5327d875859SJan Kneschke * 1. open() the file 5337d875859SJan Kneschke * 2. fstat() the fd 5347d875859SJan Kneschke * 5357d875859SJan Kneschke * and keeping the file open for the rest of the time. But this can 5367d875859SJan Kneschke * only be done at network level. 5377d875859SJan Kneschke * 53808994e47SJan Kneschke * per default it is not a symlink 5397d875859SJan Kneschke * */ 540657a024dSMarcus Rückert #ifdef HAVE_LSTAT 541657a024dSMarcus Rückert sce->is_symlink = 0; 5429de38074SJan Kneschke 5439de38074SJan Kneschke /* we want to only check for symlinks if we should block symlinks. 544a57bb820SMarcus Rückert */ 5459de38074SJan Kneschke if (!con->conf.follow_symlink) { 54608994e47SJan Kneschke if (stat_cache_lstat(srv, name, &lst) == 0) { 547657a024dSMarcus Rückert #ifdef DEBUG_STAT_CACHE 548657a024dSMarcus Rückert log_error_write(srv, __FILE__, __LINE__, "sb", 549657a024dSMarcus Rückert "found symlink", name); 550657a024dSMarcus Rückert #endif 551657a024dSMarcus Rückert sce->is_symlink = 1; 5527d875859SJan Kneschke } 5537d875859SJan Kneschke 554657a024dSMarcus Rückert /* 555657a024dSMarcus Rückert * we assume "/" can not be symlink, so 556657a024dSMarcus Rückert * skip the symlink stuff if our path is / 557657a024dSMarcus Rückert **/ 558657a024dSMarcus Rückert else if ((name->used > 2)) { 55908994e47SJan Kneschke buffer *dname; 56008994e47SJan Kneschke char *s_cur; 561657a024dSMarcus Rückert 56208994e47SJan Kneschke dname = buffer_init(); 56308994e47SJan Kneschke buffer_copy_string_buffer(dname, name); 56408994e47SJan Kneschke 56508994e47SJan Kneschke while ((s_cur = strrchr(dname->ptr,'/'))) { 566657a024dSMarcus Rückert *s_cur = '\0'; 567a57bb820SMarcus Rückert dname->used = s_cur - dname->ptr + 1; 56808994e47SJan Kneschke if (dname->ptr == s_cur) { 569657a024dSMarcus Rückert #ifdef DEBUG_STAT_CACHE 570657a024dSMarcus Rückert log_error_write(srv, __FILE__, __LINE__, "s", "reached /"); 571657a024dSMarcus Rückert #endif 572657a024dSMarcus Rückert break; 573657a024dSMarcus Rückert } 574657a024dSMarcus Rückert #ifdef DEBUG_STAT_CACHE 57508994e47SJan Kneschke log_error_write(srv, __FILE__, __LINE__, "sbs", 576657a024dSMarcus Rückert "checking if", dname, "is a symlink"); 577657a024dSMarcus Rückert #endif 578657a024dSMarcus Rückert if (stat_cache_lstat(srv, dname, &lst) == 0) { 579657a024dSMarcus Rückert sce->is_symlink = 1; 580657a024dSMarcus Rückert #ifdef DEBUG_STAT_CACHE 58108994e47SJan Kneschke log_error_write(srv, __FILE__, __LINE__, "sb", 582657a024dSMarcus Rückert "found symlink", dname); 583657a024dSMarcus Rückert #endif 584657a024dSMarcus Rückert break; 585657a024dSMarcus Rückert }; 586657a024dSMarcus Rückert }; 58708994e47SJan Kneschke buffer_free(dname); 588657a024dSMarcus Rückert }; 58905919041SMarcus Rückert }; 590657a024dSMarcus Rückert #endif 591657a024dSMarcus Rückert 5925e134da0SJan Kneschke if (S_ISREG(st.st_mode)) { 5935e134da0SJan Kneschke /* determine mimetype */ 5945e134da0SJan Kneschke buffer_reset(sce->content_type); 595b3027fefSStefan Bühler #ifdef HAVE_XATTR 59607d9517dSStefan Bühler if (con->conf.use_xattr) { 597b3027fefSStefan Bühler stat_cache_attr_get(sce->content_type, name->ptr); 598b3027fefSStefan Bühler } 599b3027fefSStefan Bühler #endif 600b3027fefSStefan Bühler /* xattr did not set a content-type. ask the config */ 601b3027fefSStefan Bühler if (buffer_is_empty(sce->content_type)) { 6025e134da0SJan Kneschke for (k = 0; k < con->conf.mimetypes->used; k++) { 6035e134da0SJan Kneschke data_string *ds = (data_string *)con->conf.mimetypes->data[k]; 60402125345SJan Kneschke buffer *type = ds->key; 6055e134da0SJan Kneschke 60602125345SJan Kneschke if (type->used == 0) continue; 6075e134da0SJan Kneschke 60802125345SJan Kneschke /* check if the right side is the same */ 60902125345SJan Kneschke if (type->used > name->used) continue; 61002125345SJan Kneschke 61102125345SJan Kneschke if (0 == strncasecmp(name->ptr + name->used - type->used, type->ptr, type->used - 1)) { 6125e134da0SJan Kneschke buffer_copy_string_buffer(sce->content_type, ds->value); 6135e134da0SJan Kneschke break; 6145e134da0SJan Kneschke } 6155e134da0SJan Kneschke } 6165e134da0SJan Kneschke } 617b3027fefSStefan Bühler etag_create(sce->etag, &(sce->st), con->etag_flags); 6187d4d8195SJan Kneschke } else if (S_ISDIR(st.st_mode)) { 6195a583661SMarcus Rückert etag_create(sce->etag, &(sce->st), con->etag_flags); 6205e134da0SJan Kneschke } 6215e134da0SJan Kneschke 6225e134da0SJan Kneschke #ifdef HAVE_FAM_H 623d8b363c1SStefan Bühler if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { 6245e134da0SJan Kneschke /* is this directory already registered ? */ 6255e134da0SJan Kneschke if (!dir_node) { 6265e134da0SJan Kneschke fam_dir = fam_dir_entry_init(); 6275e134da0SJan Kneschke 6285e134da0SJan Kneschke buffer_copy_string_buffer(fam_dir->name, sc->dir_name); 6295e134da0SJan Kneschke 6305e134da0SJan Kneschke fam_dir->version = 1; 6315e134da0SJan Kneschke 6325e134da0SJan Kneschke fam_dir->req = calloc(1, sizeof(FAMRequest)); 6335e134da0SJan Kneschke 634d8b363c1SStefan Bühler if (0 != FAMMonitorDirectory(&sc->fam, fam_dir->name->ptr, 6355e134da0SJan Kneschke fam_dir->req, fam_dir)) { 6365e134da0SJan Kneschke 637e745fd6aSJan Kneschke log_error_write(srv, __FILE__, __LINE__, "sbsbs", 6385e134da0SJan Kneschke "monitoring dir failed:", 6395e134da0SJan Kneschke fam_dir->name, 640e745fd6aSJan Kneschke "file:", name, 6415e134da0SJan Kneschke FamErrlist[FAMErrno]); 6425e134da0SJan Kneschke 643d8b363c1SStefan Bühler fam_dir_entry_free(&sc->fam, fam_dir); 644ae133550SStefan Bühler fam_dir = NULL; 6455e134da0SJan Kneschke } else { 646ecb30c4eSJan Kneschke int osize = 0; 647ecb30c4eSJan Kneschke 648ecb30c4eSJan Kneschke if (sc->dirs) { 649ecb30c4eSJan Kneschke osize = sc->dirs->size; 650ecb30c4eSJan Kneschke } 651ecb30c4eSJan Kneschke 6525e134da0SJan Kneschke sc->dirs = splaytree_insert(sc->dirs, dir_ndx, fam_dir); 653*07dd0bd0SStefan Bühler force_assert(sc->dirs); 654*07dd0bd0SStefan Bühler force_assert(sc->dirs->data == fam_dir); 655*07dd0bd0SStefan Bühler force_assert(osize == (sc->dirs->size - 1)); 6565e134da0SJan Kneschke } 6575e134da0SJan Kneschke } else { 6585e134da0SJan Kneschke fam_dir = dir_node->data; 6595e134da0SJan Kneschke } 6605e134da0SJan Kneschke 6615e134da0SJan Kneschke /* bind the fam_fc to the stat() cache entry */ 6625e134da0SJan Kneschke 6635e134da0SJan Kneschke if (fam_dir) { 6645e134da0SJan Kneschke sce->dir_version = fam_dir->version; 6655e134da0SJan Kneschke } 6665e134da0SJan Kneschke } 6675e134da0SJan Kneschke #endif 66851978868SJan Kneschke 6695e134da0SJan Kneschke *ret_sce = sce; 6705e134da0SJan Kneschke 6715e134da0SJan Kneschke return HANDLER_GO_ON; 6725e134da0SJan Kneschke } 6735e134da0SJan Kneschke 6745e134da0SJan Kneschke /** 6755e134da0SJan Kneschke * remove stat() from cache which havn't been stat()ed for 6765e134da0SJan Kneschke * more than 10 seconds 6775e134da0SJan Kneschke * 6785e134da0SJan Kneschke * 6795e134da0SJan Kneschke * walk though the stat-cache, collect the ids which are too old 6805e134da0SJan Kneschke * and remove them in a second loop 6815e134da0SJan Kneschke */ 6825e134da0SJan Kneschke 6835e134da0SJan Kneschke static int stat_cache_tag_old_entries(server *srv, splay_tree *t, int *keys, size_t *ndx) { 6845e134da0SJan Kneschke stat_cache_entry *sce; 6855e134da0SJan Kneschke 6865e134da0SJan Kneschke if (!t) return 0; 6875e134da0SJan Kneschke 6885e134da0SJan Kneschke stat_cache_tag_old_entries(srv, t->left, keys, ndx); 6895e134da0SJan Kneschke stat_cache_tag_old_entries(srv, t->right, keys, ndx); 6905e134da0SJan Kneschke 6915e134da0SJan Kneschke sce = t->data; 6925e134da0SJan Kneschke 693b795fd36SJan Kneschke if (srv->cur_ts - sce->stat_ts > 2) { 6945e134da0SJan Kneschke keys[(*ndx)++] = t->key; 6955e134da0SJan Kneschke } 6965e134da0SJan Kneschke 6975e134da0SJan Kneschke return 0; 6985e134da0SJan Kneschke } 6995e134da0SJan Kneschke 7005e134da0SJan Kneschke int stat_cache_trigger_cleanup(server *srv) { 7015e134da0SJan Kneschke stat_cache *sc; 70256f637b8SJan Kneschke size_t max_ndx = 0, i; 7035e134da0SJan Kneschke int *keys; 7045e134da0SJan Kneschke 7055e134da0SJan Kneschke sc = srv->stat_cache; 7065e134da0SJan Kneschke 7075e134da0SJan Kneschke if (!sc->files) return 0; 7085e134da0SJan Kneschke 7096f208cfdSStefan Bühler keys = calloc(1, sizeof(int) * sc->files->size); 7105e134da0SJan Kneschke 7115e134da0SJan Kneschke stat_cache_tag_old_entries(srv, sc->files, keys, &max_ndx); 7125e134da0SJan Kneschke 7135e134da0SJan Kneschke for (i = 0; i < max_ndx; i++) { 7145e134da0SJan Kneschke int ndx = keys[i]; 7155e134da0SJan Kneschke splay_tree *node; 7165e134da0SJan Kneschke 7175e134da0SJan Kneschke sc->files = splaytree_splay(sc->files, ndx); 7185e134da0SJan Kneschke 7195e134da0SJan Kneschke node = sc->files; 7205e134da0SJan Kneschke 7215e134da0SJan Kneschke if (node && (node->key == ndx)) { 72256f637b8SJan Kneschke #ifdef DEBUG_STAT_CACHE 72356f637b8SJan Kneschke size_t j; 724b795fd36SJan Kneschke int osize = splaytree_size(sc->files); 725b795fd36SJan Kneschke stat_cache_entry *sce = node->data; 72656f637b8SJan Kneschke #endif 7275e134da0SJan Kneschke stat_cache_entry_free(node->data); 7285e134da0SJan Kneschke sc->files = splaytree_delete(sc->files, ndx); 729d1e70da8SJan Kneschke 730b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 731b795fd36SJan Kneschke for (j = 0; j < ctrl.used; j++) { 732b795fd36SJan Kneschke if (ctrl.ptr[j] == ndx) { 733b795fd36SJan Kneschke ctrl.ptr[j] = ctrl.ptr[--ctrl.used]; 734b795fd36SJan Kneschke break; 735ecb30c4eSJan Kneschke } 7365e134da0SJan Kneschke } 737b795fd36SJan Kneschke 738*07dd0bd0SStefan Bühler force_assert(osize - 1 == splaytree_size(sc->files)); 739b795fd36SJan Kneschke #endif 740b795fd36SJan Kneschke } 7415e134da0SJan Kneschke } 7425e134da0SJan Kneschke 7435e134da0SJan Kneschke free(keys); 7445e134da0SJan Kneschke 7455e134da0SJan Kneschke return 0; 7465e134da0SJan Kneschke } 747