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 214d55d4adSMoritz Wilhelmy #ifdef HAVE_SYS_EXTATTR_H 224d55d4adSMoritz Wilhelmy # include <sys/extattr.h> 234d55d4adSMoritz Wilhelmy #endif 244d55d4adSMoritz Wilhelmy 255e134da0SJan Kneschke #ifdef HAVE_FAM_H 265e134da0SJan Kneschke # include <fam.h> 275e134da0SJan Kneschke #endif 285e134da0SJan Kneschke 295e134da0SJan Kneschke #include "sys-mmap.h" 305e134da0SJan Kneschke 315e134da0SJan Kneschke /* NetBSD 1.3.x needs it */ 325e134da0SJan Kneschke #ifndef MAP_FAILED 335e134da0SJan Kneschke # define MAP_FAILED -1 345e134da0SJan Kneschke #endif 355e134da0SJan Kneschke 365e134da0SJan Kneschke #ifndef O_LARGEFILE 375e134da0SJan Kneschke # define O_LARGEFILE 0 385e134da0SJan Kneschke #endif 395e134da0SJan Kneschke 405e134da0SJan Kneschke #ifndef HAVE_LSTAT 415e134da0SJan Kneschke # define lstat stat 425e134da0SJan Kneschke #endif 435e134da0SJan Kneschke 440f190e16SMarcus Rückert #if 0 45b795fd36SJan Kneschke /* enables debug code for testing if all nodes in the stat-cache as accessable */ 46b795fd36SJan Kneschke #define DEBUG_STAT_CACHE 47b795fd36SJan Kneschke #endif 48b795fd36SJan Kneschke 495e134da0SJan Kneschke /* 505e134da0SJan Kneschke * stat-cache 515e134da0SJan Kneschke * 525e134da0SJan Kneschke * we cache the stat() calls in our own storage 535e134da0SJan Kneschke * the directories are cached in FAM 545e134da0SJan Kneschke * 555e134da0SJan Kneschke * if we get a change-event from FAM, we increment the version in the FAM->dir mapping 565e134da0SJan Kneschke * 575e134da0SJan Kneschke * if the stat()-cache is queried we check if the version id for the directory is the 585e134da0SJan Kneschke * same and return immediatly. 595e134da0SJan Kneschke * 605e134da0SJan Kneschke * 615e134da0SJan Kneschke * What we need: 625e134da0SJan Kneschke * 635e134da0SJan Kneschke * - for each stat-cache entry we need a fast indirect lookup on the directory name 645e134da0SJan Kneschke * - for each FAMRequest we have to find the version in the directory cache (index as userdata) 655e134da0SJan Kneschke * 665e134da0SJan Kneschke * stat <<-> directory <-> FAMRequest 675e134da0SJan Kneschke * 685e134da0SJan Kneschke * if file is deleted, directory is dirty, file is rechecked ... 695e134da0SJan Kneschke * if directory is deleted, directory mapping is removed 705e134da0SJan Kneschke * 715e134da0SJan Kneschke * */ 725e134da0SJan Kneschke 735e134da0SJan Kneschke #ifdef HAVE_FAM_H 745e134da0SJan Kneschke typedef struct { 755e134da0SJan Kneschke FAMRequest *req; 765e134da0SJan Kneschke 775e134da0SJan Kneschke buffer *name; 785e134da0SJan Kneschke 795e134da0SJan Kneschke int version; 805e134da0SJan Kneschke } fam_dir_entry; 815e134da0SJan Kneschke #endif 825e134da0SJan Kneschke 835e134da0SJan Kneschke /* the directory name is too long to always compare on it 845e134da0SJan Kneschke * - we need a hash 855e134da0SJan Kneschke * - the hash-key is used as sorting criteria for a tree 865e134da0SJan Kneschke * - a splay-tree is used as we can use the caching effect of it 875e134da0SJan Kneschke */ 885e134da0SJan Kneschke 895e134da0SJan Kneschke /* we want to cleanup the stat-cache every few seconds, let's say 10 905e134da0SJan Kneschke * 915e134da0SJan Kneschke * - remove entries which are outdated since 30s 925e134da0SJan Kneschke * - remove entries which are fresh but havn't been used since 60s 935e134da0SJan Kneschke * - if we don't have a stat-cache entry for a directory, release it from the monitor 945e134da0SJan Kneschke */ 955e134da0SJan Kneschke 96b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 97b795fd36SJan Kneschke typedef struct { 98b795fd36SJan Kneschke int *ptr; 99b795fd36SJan Kneschke 100b795fd36SJan Kneschke size_t used; 101b795fd36SJan Kneschke size_t size; 102b795fd36SJan Kneschke } fake_keys; 103b795fd36SJan Kneschke 104b795fd36SJan Kneschke static fake_keys ctrl; 105b795fd36SJan Kneschke #endif 106b795fd36SJan Kneschke 1075e134da0SJan Kneschke stat_cache *stat_cache_init(void) { 108d8b363c1SStefan Bühler stat_cache *sc = NULL; 1095e134da0SJan Kneschke 110d8b363c1SStefan Bühler sc = calloc(1, sizeof(*sc)); 111*566cf8deSStefan Bühler force_assert(NULL != sc); 1125e134da0SJan Kneschke 113d8b363c1SStefan Bühler sc->dir_name = buffer_init(); 114d8b363c1SStefan Bühler sc->hash_key = buffer_init(); 115d8b363c1SStefan Bühler 1165e134da0SJan Kneschke #ifdef HAVE_FAM_H 117d8b363c1SStefan Bühler sc->fam_fcce_ndx = -1; 11879d3bc44SJan Kneschke #endif 1195e134da0SJan Kneschke 120b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 121b795fd36SJan Kneschke ctrl.size = 0; 122b795fd36SJan Kneschke #endif 123b795fd36SJan Kneschke 124d8b363c1SStefan Bühler return sc; 1255e134da0SJan Kneschke } 1265e134da0SJan Kneschke 1275e134da0SJan Kneschke static stat_cache_entry * stat_cache_entry_init(void) { 1285e134da0SJan Kneschke stat_cache_entry *sce = NULL; 1295e134da0SJan Kneschke 1305e134da0SJan Kneschke sce = calloc(1, sizeof(*sce)); 131*566cf8deSStefan Bühler force_assert(NULL != sce); 1325e134da0SJan Kneschke 1335e134da0SJan Kneschke sce->name = buffer_init(); 1345e134da0SJan Kneschke sce->etag = buffer_init(); 1355e134da0SJan Kneschke sce->content_type = buffer_init(); 1365e134da0SJan Kneschke 1375e134da0SJan Kneschke return sce; 1385e134da0SJan Kneschke } 1395e134da0SJan Kneschke 1405e134da0SJan Kneschke static void stat_cache_entry_free(void *data) { 1415e134da0SJan Kneschke stat_cache_entry *sce = data; 1425e134da0SJan Kneschke if (!sce) return; 1435e134da0SJan Kneschke 1445e134da0SJan Kneschke buffer_free(sce->etag); 1455e134da0SJan Kneschke buffer_free(sce->name); 1465e134da0SJan Kneschke buffer_free(sce->content_type); 1475e134da0SJan Kneschke 1485e134da0SJan Kneschke free(sce); 1495e134da0SJan Kneschke } 1505e134da0SJan Kneschke 1515e134da0SJan Kneschke #ifdef HAVE_FAM_H 1525e134da0SJan Kneschke static fam_dir_entry * fam_dir_entry_init(void) { 1535e134da0SJan Kneschke fam_dir_entry *fam_dir = NULL; 1545e134da0SJan Kneschke 1555e134da0SJan Kneschke fam_dir = calloc(1, sizeof(*fam_dir)); 156*566cf8deSStefan Bühler force_assert(NULL != fam_dir); 1575e134da0SJan Kneschke 1585e134da0SJan Kneschke fam_dir->name = buffer_init(); 1595e134da0SJan Kneschke 1605e134da0SJan Kneschke return fam_dir; 1615e134da0SJan Kneschke } 1625e134da0SJan Kneschke 163d8b363c1SStefan Bühler static void fam_dir_entry_free(FAMConnection *fc, void *data) { 1645e134da0SJan Kneschke fam_dir_entry *fam_dir = data; 1655e134da0SJan Kneschke 1665e134da0SJan Kneschke if (!fam_dir) return; 1675e134da0SJan Kneschke 168d8b363c1SStefan Bühler FAMCancelMonitor(fc, fam_dir->req); 1695e134da0SJan Kneschke 1705e134da0SJan Kneschke buffer_free(fam_dir->name); 1715e134da0SJan Kneschke free(fam_dir->req); 1725e134da0SJan Kneschke 1735e134da0SJan Kneschke free(fam_dir); 1745e134da0SJan Kneschke } 1755e134da0SJan Kneschke #endif 1765e134da0SJan Kneschke 177d1e70da8SJan Kneschke void stat_cache_free(stat_cache *sc) { 178d1e70da8SJan Kneschke while (sc->files) { 179ecb30c4eSJan Kneschke int osize; 180d1e70da8SJan Kneschke splay_tree *node = sc->files; 1815e134da0SJan Kneschke 182ecb30c4eSJan Kneschke osize = sc->files->size; 183ecb30c4eSJan Kneschke 184d1e70da8SJan Kneschke stat_cache_entry_free(node->data); 185d1e70da8SJan Kneschke sc->files = splaytree_delete(sc->files, node->key); 186d1e70da8SJan Kneschke 18707dd0bd0SStefan Bühler force_assert(osize - 1 == splaytree_size(sc->files)); 188d1e70da8SJan Kneschke } 189d1e70da8SJan Kneschke 190d1e70da8SJan Kneschke buffer_free(sc->dir_name); 1919de38074SJan Kneschke buffer_free(sc->hash_key); 1925e134da0SJan Kneschke 1935e134da0SJan Kneschke #ifdef HAVE_FAM_H 194d1e70da8SJan Kneschke while (sc->dirs) { 195ecb30c4eSJan Kneschke int osize; 196d1e70da8SJan Kneschke splay_tree *node = sc->dirs; 1975e134da0SJan Kneschke 198ecb30c4eSJan Kneschke osize = sc->dirs->size; 199ecb30c4eSJan Kneschke 200d8b363c1SStefan Bühler fam_dir_entry_free(&sc->fam, node->data); 201d1e70da8SJan Kneschke sc->dirs = splaytree_delete(sc->dirs, node->key); 202d1e70da8SJan Kneschke 203ecb30c4eSJan Kneschke if (osize == 1) { 20407dd0bd0SStefan Bühler force_assert(NULL == sc->dirs); 205ecb30c4eSJan Kneschke } else { 20607dd0bd0SStefan Bühler force_assert(osize == (sc->dirs->size + 1)); 207ecb30c4eSJan Kneschke } 208d1e70da8SJan Kneschke } 209d1e70da8SJan Kneschke 210d8b363c1SStefan Bühler if (-1 != sc->fam_fcce_ndx) { 211d8b363c1SStefan Bühler /* fd events already gone */ 212d8b363c1SStefan Bühler sc->fam_fcce_ndx = -1; 213d8b363c1SStefan Bühler 214d8b363c1SStefan Bühler FAMClose(&sc->fam); 21579d3bc44SJan Kneschke } 2165e134da0SJan Kneschke #endif 217d1e70da8SJan Kneschke free(sc); 2185e134da0SJan Kneschke } 2195e134da0SJan Kneschke 2204d55d4adSMoritz Wilhelmy #if defined(HAVE_XATTR) 22130b19732SJan Kneschke static int stat_cache_attr_get(buffer *buf, char *name) { 2225e134da0SJan Kneschke int attrlen; 2235e134da0SJan Kneschke int ret; 2245e134da0SJan Kneschke 2254365bdbeSStefan Bühler buffer_string_prepare_copy(buf, 1023); 2266afad87dSStefan Bühler attrlen = buf->size - 1; 2275e134da0SJan Kneschke if(0 == (ret = attr_get(name, "Content-Type", buf->ptr, &attrlen, 0))) { 228ad3e93eaSStefan Bühler buffer_commit(buf, attrlen); 2295e134da0SJan Kneschke } 2305e134da0SJan Kneschke return ret; 2315e134da0SJan Kneschke } 2324d55d4adSMoritz Wilhelmy #elif defined(HAVE_EXTATTR) 2334d55d4adSMoritz Wilhelmy static int stat_cache_attr_get(buffer *buf, char *name) { 2344365bdbeSStefan Bühler ssize_t attrlen; 2354d55d4adSMoritz Wilhelmy 2369257d7dfSStefan Bühler buffer_string_prepare_copy(buf, 1023); 2374d55d4adSMoritz Wilhelmy 2386afad87dSStefan Bühler if (-1 != (attrlen = extattr_get_file(name, EXTATTR_NAMESPACE_USER, "Content-Type", buf->ptr, buf->size - 1))) { 2394d55d4adSMoritz Wilhelmy buf->used = attrlen + 1; 2404d55d4adSMoritz Wilhelmy buf->ptr[attrlen] = '\0'; 2414d55d4adSMoritz Wilhelmy return 0; 2424d55d4adSMoritz Wilhelmy } 2434d55d4adSMoritz Wilhelmy return -1; 2444d55d4adSMoritz Wilhelmy } 2455e134da0SJan Kneschke #endif 2465e134da0SJan Kneschke 247b795fd36SJan Kneschke /* the famous DJB hash function for strings */ 248b795fd36SJan Kneschke static uint32_t hashme(buffer *str) { 249b795fd36SJan Kneschke uint32_t hash = 5381; 2505e134da0SJan Kneschke const char *s; 2515e134da0SJan Kneschke for (s = str->ptr; *s; s++) { 252b795fd36SJan Kneschke hash = ((hash << 5) + hash) + *s; 2535e134da0SJan Kneschke } 2545e134da0SJan Kneschke 255fa8b1546SStefan Bühler hash &= ~(((uint32_t)1) << 31); /* strip the highest bit */ 256b795fd36SJan Kneschke 2575e134da0SJan Kneschke return hash; 2585e134da0SJan Kneschke } 2595e134da0SJan Kneschke 2605e134da0SJan Kneschke #ifdef HAVE_FAM_H 26138f2d1ddSStefan Bühler handler_t stat_cache_handle_fdevent(server *srv, void *_fce, int revent) { 2625e134da0SJan Kneschke size_t i; 2635e134da0SJan Kneschke stat_cache *sc = srv->stat_cache; 2645e134da0SJan Kneschke size_t events; 2655e134da0SJan Kneschke 26656f637b8SJan Kneschke UNUSED(_fce); 2675e134da0SJan Kneschke /* */ 2685e134da0SJan Kneschke 269d8b363c1SStefan Bühler if (revent & FDEVENT_IN) { 270d8b363c1SStefan Bühler events = FAMPending(&sc->fam); 2715e134da0SJan Kneschke 2725e134da0SJan Kneschke for (i = 0; i < events; i++) { 2735e134da0SJan Kneschke FAMEvent fe; 2745e134da0SJan Kneschke fam_dir_entry *fam_dir; 2755e134da0SJan Kneschke splay_tree *node; 2769de38074SJan Kneschke int ndx, j; 2775e134da0SJan Kneschke 278d8b363c1SStefan Bühler FAMNextEvent(&sc->fam, &fe); 2795e134da0SJan Kneschke 2805e134da0SJan Kneschke /* handle event */ 2815e134da0SJan Kneschke 2825e134da0SJan Kneschke switch(fe.code) { 2835e134da0SJan Kneschke case FAMChanged: 2845e134da0SJan Kneschke case FAMDeleted: 2855e134da0SJan Kneschke case FAMMoved: 2865e134da0SJan Kneschke /* if the filename is a directory remove the entry */ 2875e134da0SJan Kneschke 2885e134da0SJan Kneschke fam_dir = fe.userdata; 2895e134da0SJan Kneschke fam_dir->version++; 2905e134da0SJan Kneschke 2915e134da0SJan Kneschke /* file/dir is still here */ 2925e134da0SJan Kneschke if (fe.code == FAMChanged) break; 2935e134da0SJan Kneschke 2949de38074SJan Kneschke /* we have 2 versions, follow and no-follow-symlink */ 2955e134da0SJan Kneschke 2969de38074SJan Kneschke for (j = 0; j < 2; j++) { 2979de38074SJan Kneschke buffer_copy_string(sc->hash_key, fe.filename); 2986afad87dSStefan Bühler buffer_append_int(sc->hash_key, j); 2999de38074SJan Kneschke 3009de38074SJan Kneschke ndx = hashme(sc->hash_key); 3015e134da0SJan Kneschke 3025e134da0SJan Kneschke sc->dirs = splaytree_splay(sc->dirs, ndx); 3035e134da0SJan Kneschke node = sc->dirs; 3045e134da0SJan Kneschke 3055e134da0SJan Kneschke if (node && (node->key == ndx)) { 306b795fd36SJan Kneschke int osize = splaytree_size(sc->dirs); 307d1e70da8SJan Kneschke 308d8b363c1SStefan Bühler fam_dir_entry_free(&sc->fam, node->data); 3095e134da0SJan Kneschke sc->dirs = splaytree_delete(sc->dirs, ndx); 310d1e70da8SJan Kneschke 31107dd0bd0SStefan Bühler force_assert(osize - 1 == splaytree_size(sc->dirs)); 3125e134da0SJan Kneschke } 3139de38074SJan Kneschke } 3145e134da0SJan Kneschke break; 3155e134da0SJan Kneschke default: 3165e134da0SJan Kneschke break; 3175e134da0SJan Kneschke } 3185e134da0SJan Kneschke } 31979d3bc44SJan Kneschke } 32079d3bc44SJan Kneschke 32179d3bc44SJan Kneschke if (revent & FDEVENT_HUP) { 32279d3bc44SJan Kneschke /* fam closed the connection */ 323d8b363c1SStefan Bühler fdevent_event_del(srv->ev, &(sc->fam_fcce_ndx), FAMCONNECTION_GETFD(&sc->fam)); 324d8b363c1SStefan Bühler fdevent_unregister(srv->ev, FAMCONNECTION_GETFD(&sc->fam)); 32579d3bc44SJan Kneschke 326d8b363c1SStefan Bühler FAMClose(&sc->fam); 32779d3bc44SJan Kneschke } 3285e134da0SJan Kneschke 3295e134da0SJan Kneschke return HANDLER_GO_ON; 3305e134da0SJan Kneschke } 3315e134da0SJan Kneschke 3325e134da0SJan Kneschke static int buffer_copy_dirname(buffer *dst, buffer *file) { 3335e134da0SJan Kneschke size_t i; 3345e134da0SJan Kneschke 3356afad87dSStefan Bühler if (buffer_string_is_empty(file)) return -1; 3365e134da0SJan Kneschke 337ad3e93eaSStefan Bühler for (i = buffer_string_length(file); i > 0; i--) { 3385e134da0SJan Kneschke if (file->ptr[i] == '/') { 3395e134da0SJan Kneschke buffer_copy_string_len(dst, file->ptr, i); 3405e134da0SJan Kneschke return 0; 3415e134da0SJan Kneschke } 3425e134da0SJan Kneschke } 3435e134da0SJan Kneschke 3445e134da0SJan Kneschke return -1; 3455e134da0SJan Kneschke } 3465e134da0SJan Kneschke #endif 3475e134da0SJan Kneschke 348657a024dSMarcus Rückert #ifdef HAVE_LSTAT 34908994e47SJan Kneschke static int stat_cache_lstat(server *srv, buffer *dname, struct stat *lst) { 35008994e47SJan Kneschke if (lstat(dname->ptr, lst) == 0) { 351657a024dSMarcus Rückert return S_ISLNK(lst->st_mode) ? 0 : 1; 352657a024dSMarcus Rückert } 353657a024dSMarcus Rückert else { 35408994e47SJan Kneschke log_error_write(srv, __FILE__, __LINE__, "sbs", 355657a024dSMarcus Rückert "lstat failed for:", 356657a024dSMarcus Rückert dname, strerror(errno)); 357657a024dSMarcus Rückert }; 358657a024dSMarcus Rückert return -1; 359657a024dSMarcus Rückert } 360657a024dSMarcus Rückert #endif 361657a024dSMarcus Rückert 3625e134da0SJan Kneschke /*** 3635e134da0SJan Kneschke * 3645e134da0SJan Kneschke * 3655e134da0SJan Kneschke * 3665e134da0SJan Kneschke * returns: 3675e134da0SJan Kneschke * - HANDLER_FINISHED on cache-miss (don't forget to reopen the file) 3685e134da0SJan Kneschke * - HANDLER_ERROR on stat() failed -> see errno for problem 3695e134da0SJan Kneschke */ 3705e134da0SJan Kneschke 3715e134da0SJan Kneschke handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_cache_entry **ret_sce) { 3725e134da0SJan Kneschke #ifdef HAVE_FAM_H 3735e134da0SJan Kneschke fam_dir_entry *fam_dir = NULL; 3745e134da0SJan Kneschke int dir_ndx = -1; 3755e134da0SJan Kneschke #endif 3765e134da0SJan Kneschke stat_cache_entry *sce = NULL; 3775e134da0SJan Kneschke stat_cache *sc; 3785e134da0SJan Kneschke struct stat st; 37951978868SJan Kneschke size_t k; 3807d875859SJan Kneschke int fd; 38108994e47SJan Kneschke struct stat lst; 38256f637b8SJan Kneschke #ifdef DEBUG_STAT_CACHE 383b795fd36SJan Kneschke size_t i; 38456f637b8SJan Kneschke #endif 3855e134da0SJan Kneschke 3865e134da0SJan Kneschke int file_ndx; 3875e134da0SJan Kneschke 3885e134da0SJan Kneschke *ret_sce = NULL; 3895e134da0SJan Kneschke 3905e134da0SJan Kneschke /* 3915e134da0SJan Kneschke * check if the directory for this file has changed 3925e134da0SJan Kneschke */ 3935e134da0SJan Kneschke 3945e134da0SJan Kneschke sc = srv->stat_cache; 3955e134da0SJan Kneschke 3966afad87dSStefan Bühler buffer_copy_buffer(sc->hash_key, name); 3976afad87dSStefan Bühler buffer_append_int(sc->hash_key, con->conf.follow_symlink); 3989de38074SJan Kneschke 3999de38074SJan Kneschke file_ndx = hashme(sc->hash_key); 4005e134da0SJan Kneschke sc->files = splaytree_splay(sc->files, file_ndx); 4015e134da0SJan Kneschke 402b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 403b795fd36SJan Kneschke for (i = 0; i < ctrl.used; i++) { 404b795fd36SJan Kneschke if (ctrl.ptr[i] == file_ndx) break; 405b795fd36SJan Kneschke } 406b795fd36SJan Kneschke #endif 407b795fd36SJan Kneschke 4085e134da0SJan Kneschke if (sc->files && (sc->files->key == file_ndx)) { 409b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 410b795fd36SJan Kneschke /* it was in the cache */ 41107dd0bd0SStefan Bühler force_assert(i < ctrl.used); 412b795fd36SJan Kneschke #endif 413b795fd36SJan Kneschke 4145e134da0SJan Kneschke /* we have seen this file already and 4155e134da0SJan Kneschke * don't stat() it again in the same second */ 4165e134da0SJan Kneschke 41769f890e2SStefan Bühler sce = sc->files->data; 4185e134da0SJan Kneschke 419ecb30c4eSJan Kneschke /* check if the name is the same, we might have a collision */ 420ecb30c4eSJan Kneschke 421ecb30c4eSJan Kneschke if (buffer_is_equal(name, sce->name)) { 4225e134da0SJan Kneschke if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_SIMPLE) { 4235e134da0SJan Kneschke if (sce->stat_ts == srv->cur_ts) { 4245e134da0SJan Kneschke *ret_sce = sce; 4255e134da0SJan Kneschke return HANDLER_GO_ON; 4265e134da0SJan Kneschke } 4275e134da0SJan Kneschke } 428ecb30c4eSJan Kneschke } else { 42969f890e2SStefan Bühler /* collision, forget about the entry */ 43069f890e2SStefan Bühler sce = NULL; 431ecb30c4eSJan Kneschke } 432b795fd36SJan Kneschke } else { 433b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 434b795fd36SJan Kneschke if (i != ctrl.used) { 43538f2d1ddSStefan Bühler log_error_write(srv, __FILE__, __LINE__, "xSB", 43638f2d1ddSStefan Bühler file_ndx, "was already inserted but not found in cache, ", name); 437b795fd36SJan Kneschke } 43807dd0bd0SStefan Bühler force_assert(i == ctrl.used); 439b795fd36SJan Kneschke #endif 4405e134da0SJan Kneschke } 4415e134da0SJan Kneschke 4425e134da0SJan Kneschke #ifdef HAVE_FAM_H 4435e134da0SJan Kneschke /* dir-check */ 4445e134da0SJan Kneschke if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { 4455e134da0SJan Kneschke if (0 != buffer_copy_dirname(sc->dir_name, name)) { 4467a64f2f8SStefan Bühler log_error_write(srv, __FILE__, __LINE__, "sb", 4477a64f2f8SStefan Bühler "no '/' found in filename:", name); 4487a64f2f8SStefan Bühler return HANDLER_ERROR; 4495e134da0SJan Kneschke } 4505e134da0SJan Kneschke 4516afad87dSStefan Bühler buffer_copy_buffer(sc->hash_key, sc->dir_name); 4526afad87dSStefan Bühler buffer_append_int(sc->hash_key, con->conf.follow_symlink); 4539de38074SJan Kneschke 4549de38074SJan Kneschke dir_ndx = hashme(sc->hash_key); 4555e134da0SJan Kneschke 4565e134da0SJan Kneschke sc->dirs = splaytree_splay(sc->dirs, dir_ndx); 4575e134da0SJan Kneschke 45869f890e2SStefan Bühler if ((NULL != sc->dirs) && (sc->dirs->key == dir_ndx)) { 45969f890e2SStefan Bühler fam_dir = sc->dirs->data; 4605e134da0SJan Kneschke 46169f890e2SStefan Bühler /* check whether we got a collision */ 46269f890e2SStefan Bühler if (buffer_is_equal(sc->dir_name, fam_dir->name)) { 46369f890e2SStefan Bühler /* test whether a found file cache entry is still ok */ 46469f890e2SStefan Bühler if ((NULL != sce) && (fam_dir->version == sce->dir_version)) { 4655e134da0SJan Kneschke /* the stat()-cache entry is still ok */ 4665e134da0SJan Kneschke 4675e134da0SJan Kneschke *ret_sce = sce; 4685e134da0SJan Kneschke return HANDLER_GO_ON; 4695e134da0SJan Kneschke } 47069f890e2SStefan Bühler } else { 47169f890e2SStefan Bühler /* hash collision, forget about the entry */ 47269f890e2SStefan Bühler fam_dir = NULL; 47369f890e2SStefan Bühler } 4745e134da0SJan Kneschke } 4755e134da0SJan Kneschke } 4765e134da0SJan Kneschke #endif 477478cb34bSJan Kneschke 478478cb34bSJan Kneschke /* 479478cb34bSJan Kneschke * *lol* 480478cb34bSJan Kneschke * - open() + fstat() on a named-pipe results in a (intended) hang. 481657a024dSMarcus Rückert * - stat() if regular file + open() to see if we can read from it is better 482478cb34bSJan Kneschke * 483478cb34bSJan Kneschke * */ 484478cb34bSJan Kneschke if (-1 == stat(name->ptr, &st)) { 485478cb34bSJan Kneschke return HANDLER_ERROR; 486478cb34bSJan Kneschke } 487478cb34bSJan Kneschke 488478cb34bSJan Kneschke 489478cb34bSJan Kneschke if (S_ISREG(st.st_mode)) { 49057066345SStefan Bühler /* fix broken stat/open for symlinks to reg files with appended slash on freebsd,osx */ 491ad3e93eaSStefan Bühler if (name->ptr[buffer_string_length(name) - 1] == '/') { 49257066345SStefan Bühler errno = ENOTDIR; 49357066345SStefan Bühler return HANDLER_ERROR; 49457066345SStefan Bühler } 49557066345SStefan Bühler 496478cb34bSJan Kneschke /* try to open the file to check if we can read it */ 497603d6aceSJan Kneschke if (-1 == (fd = open(name->ptr, O_RDONLY))) { 498603d6aceSJan Kneschke return HANDLER_ERROR; 499603d6aceSJan Kneschke } 500603d6aceSJan Kneschke close(fd); 501603d6aceSJan Kneschke } 502603d6aceSJan Kneschke 5035e134da0SJan Kneschke if (NULL == sce) { 504ecb30c4eSJan Kneschke 5055e134da0SJan Kneschke sce = stat_cache_entry_init(); 5066afad87dSStefan Bühler buffer_copy_buffer(sce->name, name); 5075e134da0SJan Kneschke 50869f890e2SStefan Bühler /* already splayed file_ndx */ 50969f890e2SStefan Bühler if ((NULL != sc->files) && (sc->files->key == file_ndx)) { 51069f890e2SStefan Bühler /* hash collision: replace old entry */ 51169f890e2SStefan Bühler stat_cache_entry_free(sc->files->data); 51269f890e2SStefan Bühler sc->files->data = sce; 51369f890e2SStefan Bühler } else { 51469f890e2SStefan Bühler int osize = splaytree_size(sc->files); 51569f890e2SStefan Bühler 5165e134da0SJan Kneschke sc->files = splaytree_insert(sc->files, file_ndx, sce); 51769f890e2SStefan Bühler force_assert(osize + 1 == splaytree_size(sc->files)); 51869f890e2SStefan Bühler 519b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 520b795fd36SJan Kneschke if (ctrl.size == 0) { 521b795fd36SJan Kneschke ctrl.size = 16; 522b795fd36SJan Kneschke ctrl.used = 0; 523b795fd36SJan Kneschke ctrl.ptr = malloc(ctrl.size * sizeof(*ctrl.ptr)); 524*566cf8deSStefan Bühler force_assert(NULL != ctrl.ptr); 525b795fd36SJan Kneschke } else if (ctrl.size == ctrl.used) { 526b795fd36SJan Kneschke ctrl.size += 16; 527b795fd36SJan Kneschke ctrl.ptr = realloc(ctrl.ptr, ctrl.size * sizeof(*ctrl.ptr)); 528*566cf8deSStefan Bühler force_assert(NULL != ctrl.ptr); 529b795fd36SJan Kneschke } 530b795fd36SJan Kneschke 531b795fd36SJan Kneschke ctrl.ptr[ctrl.used++] = file_ndx; 53269f890e2SStefan Bühler #endif 53369f890e2SStefan Bühler } 53407dd0bd0SStefan Bühler force_assert(sc->files); 53507dd0bd0SStefan Bühler force_assert(sc->files->data == sce); 5365e134da0SJan Kneschke } 5375e134da0SJan Kneschke 5385e134da0SJan Kneschke sce->st = st; 5395e134da0SJan Kneschke sce->stat_ts = srv->cur_ts; 5405e134da0SJan Kneschke 5417d875859SJan Kneschke /* catch the obvious symlinks 5427d875859SJan Kneschke * 5437d875859SJan Kneschke * this is not a secure check as we still have a race-condition between 5447d875859SJan Kneschke * the stat() and the open. We can only solve this by 5457d875859SJan Kneschke * 1. open() the file 5467d875859SJan Kneschke * 2. fstat() the fd 5477d875859SJan Kneschke * 5487d875859SJan Kneschke * and keeping the file open for the rest of the time. But this can 5497d875859SJan Kneschke * only be done at network level. 5507d875859SJan Kneschke * 55108994e47SJan Kneschke * per default it is not a symlink 5527d875859SJan Kneschke * */ 553657a024dSMarcus Rückert #ifdef HAVE_LSTAT 554657a024dSMarcus Rückert sce->is_symlink = 0; 5559de38074SJan Kneschke 5569de38074SJan Kneschke /* we want to only check for symlinks if we should block symlinks. 557a57bb820SMarcus Rückert */ 5589de38074SJan Kneschke if (!con->conf.follow_symlink) { 55908994e47SJan Kneschke if (stat_cache_lstat(srv, name, &lst) == 0) { 560657a024dSMarcus Rückert #ifdef DEBUG_STAT_CACHE 561657a024dSMarcus Rückert log_error_write(srv, __FILE__, __LINE__, "sb", 562657a024dSMarcus Rückert "found symlink", name); 563657a024dSMarcus Rückert #endif 564657a024dSMarcus Rückert sce->is_symlink = 1; 5657d875859SJan Kneschke } 5667d875859SJan Kneschke 567657a024dSMarcus Rückert /* 568657a024dSMarcus Rückert * we assume "/" can not be symlink, so 569657a024dSMarcus Rückert * skip the symlink stuff if our path is / 570657a024dSMarcus Rückert **/ 571ad3e93eaSStefan Bühler else if (buffer_string_length(name) > 1) { 57208994e47SJan Kneschke buffer *dname; 57308994e47SJan Kneschke char *s_cur; 574657a024dSMarcus Rückert 57508994e47SJan Kneschke dname = buffer_init(); 5766afad87dSStefan Bühler buffer_copy_buffer(dname, name); 57708994e47SJan Kneschke 57808994e47SJan Kneschke while ((s_cur = strrchr(dname->ptr, '/'))) { 579ad3e93eaSStefan Bühler buffer_string_set_length(dname, s_cur - dname->ptr); 58008994e47SJan Kneschke if (dname->ptr == s_cur) { 581657a024dSMarcus Rückert #ifdef DEBUG_STAT_CACHE 582657a024dSMarcus Rückert log_error_write(srv, __FILE__, __LINE__, "s", "reached /"); 583657a024dSMarcus Rückert #endif 584657a024dSMarcus Rückert break; 585657a024dSMarcus Rückert } 586657a024dSMarcus Rückert #ifdef DEBUG_STAT_CACHE 58708994e47SJan Kneschke log_error_write(srv, __FILE__, __LINE__, "sbs", 588657a024dSMarcus Rückert "checking if", dname, "is a symlink"); 589657a024dSMarcus Rückert #endif 590657a024dSMarcus Rückert if (stat_cache_lstat(srv, dname, &lst) == 0) { 591657a024dSMarcus Rückert sce->is_symlink = 1; 592657a024dSMarcus Rückert #ifdef DEBUG_STAT_CACHE 59308994e47SJan Kneschke log_error_write(srv, __FILE__, __LINE__, "sb", 594657a024dSMarcus Rückert "found symlink", dname); 595657a024dSMarcus Rückert #endif 596657a024dSMarcus Rückert break; 597657a024dSMarcus Rückert }; 598657a024dSMarcus Rückert }; 59908994e47SJan Kneschke buffer_free(dname); 600657a024dSMarcus Rückert }; 60105919041SMarcus Rückert }; 602657a024dSMarcus Rückert #endif 603657a024dSMarcus Rückert 6045e134da0SJan Kneschke if (S_ISREG(st.st_mode)) { 6055e134da0SJan Kneschke /* determine mimetype */ 6065e134da0SJan Kneschke buffer_reset(sce->content_type); 6074d55d4adSMoritz Wilhelmy #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) 60807d9517dSStefan Bühler if (con->conf.use_xattr) { 609b3027fefSStefan Bühler stat_cache_attr_get(sce->content_type, name->ptr); 610b3027fefSStefan Bühler } 611b3027fefSStefan Bühler #endif 612b3027fefSStefan Bühler /* xattr did not set a content-type. ask the config */ 6136afad87dSStefan Bühler if (buffer_string_is_empty(sce->content_type)) { 614ad3e93eaSStefan Bühler size_t namelen = buffer_string_length(name); 615ad3e93eaSStefan Bühler 6165e134da0SJan Kneschke for (k = 0; k < con->conf.mimetypes->used; k++) { 6175e134da0SJan Kneschke data_string *ds = (data_string *)con->conf.mimetypes->data[k]; 61802125345SJan Kneschke buffer *type = ds->key; 619ad3e93eaSStefan Bühler size_t typelen = buffer_string_length(type); 6205e134da0SJan Kneschke 621ad3e93eaSStefan Bühler if (buffer_is_empty(type)) continue; 6225e134da0SJan Kneschke 62302125345SJan Kneschke /* check if the right side is the same */ 624ad3e93eaSStefan Bühler if (typelen > namelen) continue; 62502125345SJan Kneschke 626ad3e93eaSStefan Bühler if (0 == strncasecmp(name->ptr + namelen - typelen, type->ptr, typelen)) { 6276afad87dSStefan Bühler buffer_copy_buffer(sce->content_type, ds->value); 6285e134da0SJan Kneschke break; 6295e134da0SJan Kneschke } 6305e134da0SJan Kneschke } 6315e134da0SJan Kneschke } 632b3027fefSStefan Bühler etag_create(sce->etag, &(sce->st), con->etag_flags); 6337d4d8195SJan Kneschke } else if (S_ISDIR(st.st_mode)) { 6345a583661SMarcus Rückert etag_create(sce->etag, &(sce->st), con->etag_flags); 6355e134da0SJan Kneschke } 6365e134da0SJan Kneschke 6375e134da0SJan Kneschke #ifdef HAVE_FAM_H 638d8b363c1SStefan Bühler if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { 6395e134da0SJan Kneschke /* is this directory already registered ? */ 64069f890e2SStefan Bühler if (NULL == fam_dir) { 6415e134da0SJan Kneschke fam_dir = fam_dir_entry_init(); 6425e134da0SJan Kneschke 6436afad87dSStefan Bühler buffer_copy_buffer(fam_dir->name, sc->dir_name); 6445e134da0SJan Kneschke 6455e134da0SJan Kneschke fam_dir->version = 1; 6465e134da0SJan Kneschke 6475e134da0SJan Kneschke fam_dir->req = calloc(1, sizeof(FAMRequest)); 648*566cf8deSStefan Bühler force_assert(NULL != fam_dir); 6495e134da0SJan Kneschke 650d8b363c1SStefan Bühler if (0 != FAMMonitorDirectory(&sc->fam, fam_dir->name->ptr, 6515e134da0SJan Kneschke fam_dir->req, fam_dir)) { 6525e134da0SJan Kneschke 653e745fd6aSJan Kneschke log_error_write(srv, __FILE__, __LINE__, "sbsbs", 6545e134da0SJan Kneschke "monitoring dir failed:", 6555e134da0SJan Kneschke fam_dir->name, 656e745fd6aSJan Kneschke "file:", name, 6575e134da0SJan Kneschke FamErrlist[FAMErrno]); 6585e134da0SJan Kneschke 659d8b363c1SStefan Bühler fam_dir_entry_free(&sc->fam, fam_dir); 660ae133550SStefan Bühler fam_dir = NULL; 6615e134da0SJan Kneschke } else { 66269f890e2SStefan Bühler int osize = splaytree_size(sc->dirs); 663ecb30c4eSJan Kneschke 66469f890e2SStefan Bühler /* already splayed dir_ndx */ 66569f890e2SStefan Bühler if ((NULL != sc->dirs) && (sc->dirs->key == dir_ndx)) { 66669f890e2SStefan Bühler /* hash collision: replace old entry */ 66769f890e2SStefan Bühler fam_dir_entry_free(&sc->fam, sc->dirs->data); 66869f890e2SStefan Bühler sc->dirs->data = fam_dir; 66969f890e2SStefan Bühler } else { 67069f890e2SStefan Bühler sc->dirs = splaytree_insert(sc->dirs, dir_ndx, fam_dir); 67169f890e2SStefan Bühler force_assert(osize == (splaytree_size(sc->dirs) - 1)); 672ecb30c4eSJan Kneschke } 673ecb30c4eSJan Kneschke 67407dd0bd0SStefan Bühler force_assert(sc->dirs); 67507dd0bd0SStefan Bühler force_assert(sc->dirs->data == fam_dir); 6765e134da0SJan Kneschke } 6775e134da0SJan Kneschke } 6785e134da0SJan Kneschke 6795e134da0SJan Kneschke /* bind the fam_fc to the stat() cache entry */ 6805e134da0SJan Kneschke 6815e134da0SJan Kneschke if (fam_dir) { 6825e134da0SJan Kneschke sce->dir_version = fam_dir->version; 6835e134da0SJan Kneschke } 6845e134da0SJan Kneschke } 6855e134da0SJan Kneschke #endif 68651978868SJan Kneschke 6875e134da0SJan Kneschke *ret_sce = sce; 6885e134da0SJan Kneschke 6895e134da0SJan Kneschke return HANDLER_GO_ON; 6905e134da0SJan Kneschke } 6915e134da0SJan Kneschke 6925e134da0SJan Kneschke /** 6935e134da0SJan Kneschke * remove stat() from cache which havn't been stat()ed for 6945e134da0SJan Kneschke * more than 10 seconds 6955e134da0SJan Kneschke * 6965e134da0SJan Kneschke * 6975e134da0SJan Kneschke * walk though the stat-cache, collect the ids which are too old 6985e134da0SJan Kneschke * and remove them in a second loop 6995e134da0SJan Kneschke */ 7005e134da0SJan Kneschke 7015e134da0SJan Kneschke static int stat_cache_tag_old_entries(server *srv, splay_tree *t, int *keys, size_t *ndx) { 7025e134da0SJan Kneschke stat_cache_entry *sce; 7035e134da0SJan Kneschke 7045e134da0SJan Kneschke if (!t) return 0; 7055e134da0SJan Kneschke 7065e134da0SJan Kneschke stat_cache_tag_old_entries(srv, t->left, keys, ndx); 7075e134da0SJan Kneschke stat_cache_tag_old_entries(srv, t->right, keys, ndx); 7085e134da0SJan Kneschke 7095e134da0SJan Kneschke sce = t->data; 7105e134da0SJan Kneschke 711b795fd36SJan Kneschke if (srv->cur_ts - sce->stat_ts > 2) { 7125e134da0SJan Kneschke keys[(*ndx)++] = t->key; 7135e134da0SJan Kneschke } 7145e134da0SJan Kneschke 7155e134da0SJan Kneschke return 0; 7165e134da0SJan Kneschke } 7175e134da0SJan Kneschke 7185e134da0SJan Kneschke int stat_cache_trigger_cleanup(server *srv) { 7195e134da0SJan Kneschke stat_cache *sc; 72056f637b8SJan Kneschke size_t max_ndx = 0, i; 7215e134da0SJan Kneschke int *keys; 7225e134da0SJan Kneschke 7235e134da0SJan Kneschke sc = srv->stat_cache; 7245e134da0SJan Kneschke 7255e134da0SJan Kneschke if (!sc->files) return 0; 7265e134da0SJan Kneschke 7276f208cfdSStefan Bühler keys = calloc(1, sizeof(int) * sc->files->size); 728*566cf8deSStefan Bühler force_assert(NULL != keys); 7295e134da0SJan Kneschke 7305e134da0SJan Kneschke stat_cache_tag_old_entries(srv, sc->files, keys, &max_ndx); 7315e134da0SJan Kneschke 7325e134da0SJan Kneschke for (i = 0; i < max_ndx; i++) { 7335e134da0SJan Kneschke int ndx = keys[i]; 7345e134da0SJan Kneschke splay_tree *node; 7355e134da0SJan Kneschke 7365e134da0SJan Kneschke sc->files = splaytree_splay(sc->files, ndx); 7375e134da0SJan Kneschke 7385e134da0SJan Kneschke node = sc->files; 7395e134da0SJan Kneschke 7405e134da0SJan Kneschke if (node && (node->key == ndx)) { 74156f637b8SJan Kneschke #ifdef DEBUG_STAT_CACHE 74256f637b8SJan Kneschke size_t j; 743b795fd36SJan Kneschke int osize = splaytree_size(sc->files); 744b795fd36SJan Kneschke stat_cache_entry *sce = node->data; 74556f637b8SJan Kneschke #endif 7465e134da0SJan Kneschke stat_cache_entry_free(node->data); 7475e134da0SJan Kneschke sc->files = splaytree_delete(sc->files, ndx); 748d1e70da8SJan Kneschke 749b795fd36SJan Kneschke #ifdef DEBUG_STAT_CACHE 750b795fd36SJan Kneschke for (j = 0; j < ctrl.used; j++) { 751b795fd36SJan Kneschke if (ctrl.ptr[j] == ndx) { 752b795fd36SJan Kneschke ctrl.ptr[j] = ctrl.ptr[--ctrl.used]; 753b795fd36SJan Kneschke break; 754ecb30c4eSJan Kneschke } 7555e134da0SJan Kneschke } 756b795fd36SJan Kneschke 75707dd0bd0SStefan Bühler force_assert(osize - 1 == splaytree_size(sc->files)); 758b795fd36SJan Kneschke #endif 759b795fd36SJan Kneschke } 7605e134da0SJan Kneschke } 7615e134da0SJan Kneschke 7625e134da0SJan Kneschke free(keys); 7635e134da0SJan Kneschke 7645e134da0SJan Kneschke return 0; 7655e134da0SJan Kneschke } 766