Annotation of embedaddon/lighttpd/src/stat_cache.c, revision 1.1

1.1     ! misho       1: #include "log.h"
        !             2: #include "stat_cache.h"
        !             3: #include "fdevent.h"
        !             4: #include "etag.h"
        !             5: 
        !             6: #include <sys/types.h>
        !             7: #include <sys/stat.h>
        !             8: 
        !             9: #include <stdlib.h>
        !            10: #include <string.h>
        !            11: #include <errno.h>
        !            12: #include <unistd.h>
        !            13: #include <stdio.h>
        !            14: #include <fcntl.h>
        !            15: #include <assert.h>
        !            16: 
        !            17: #ifdef HAVE_ATTR_ATTRIBUTES_H
        !            18: # include <attr/attributes.h>
        !            19: #endif
        !            20: 
        !            21: #ifdef HAVE_FAM_H
        !            22: # include <fam.h>
        !            23: #endif
        !            24: 
        !            25: #include "sys-mmap.h"
        !            26: 
        !            27: /* NetBSD 1.3.x needs it */
        !            28: #ifndef MAP_FAILED
        !            29: # define MAP_FAILED -1
        !            30: #endif
        !            31: 
        !            32: #ifndef O_LARGEFILE
        !            33: # define O_LARGEFILE 0
        !            34: #endif
        !            35: 
        !            36: #ifndef HAVE_LSTAT
        !            37: # define lstat stat
        !            38: #endif
        !            39: 
        !            40: #if 0
        !            41: /* enables debug code for testing if all nodes in the stat-cache as accessable */
        !            42: #define DEBUG_STAT_CACHE
        !            43: #endif
        !            44: 
        !            45: /*
        !            46:  * stat-cache
        !            47:  *
        !            48:  * we cache the stat() calls in our own storage
        !            49:  * the directories are cached in FAM
        !            50:  *
        !            51:  * if we get a change-event from FAM, we increment the version in the FAM->dir mapping
        !            52:  *
        !            53:  * if the stat()-cache is queried we check if the version id for the directory is the
        !            54:  * same and return immediatly.
        !            55:  *
        !            56:  *
        !            57:  * What we need:
        !            58:  *
        !            59:  * - for each stat-cache entry we need a fast indirect lookup on the directory name
        !            60:  * - for each FAMRequest we have to find the version in the directory cache (index as userdata)
        !            61:  *
        !            62:  * stat <<-> directory <-> FAMRequest
        !            63:  *
        !            64:  * if file is deleted, directory is dirty, file is rechecked ...
        !            65:  * if directory is deleted, directory mapping is removed
        !            66:  *
        !            67:  * */
        !            68: 
        !            69: #ifdef HAVE_FAM_H
        !            70: typedef struct {
        !            71:        FAMRequest *req;
        !            72:        FAMConnection *fc;
        !            73: 
        !            74:        buffer *name;
        !            75: 
        !            76:        int version;
        !            77: } fam_dir_entry;
        !            78: #endif
        !            79: 
        !            80: /* the directory name is too long to always compare on it
        !            81:  * - we need a hash
        !            82:  * - the hash-key is used as sorting criteria for a tree
        !            83:  * - a splay-tree is used as we can use the caching effect of it
        !            84:  */
        !            85: 
        !            86: /* we want to cleanup the stat-cache every few seconds, let's say 10
        !            87:  *
        !            88:  * - remove entries which are outdated since 30s
        !            89:  * - remove entries which are fresh but havn't been used since 60s
        !            90:  * - if we don't have a stat-cache entry for a directory, release it from the monitor
        !            91:  */
        !            92: 
        !            93: #ifdef DEBUG_STAT_CACHE
        !            94: typedef struct {
        !            95:        int *ptr;
        !            96: 
        !            97:        size_t used;
        !            98:        size_t size;
        !            99: } fake_keys;
        !           100: 
        !           101: static fake_keys ctrl;
        !           102: #endif
        !           103: 
        !           104: stat_cache *stat_cache_init(void) {
        !           105:        stat_cache *fc = NULL;
        !           106: 
        !           107:        fc = calloc(1, sizeof(*fc));
        !           108: 
        !           109:        fc->dir_name = buffer_init();
        !           110:        fc->hash_key = buffer_init();
        !           111: #ifdef HAVE_FAM_H
        !           112:        fc->fam = calloc(1, sizeof(*fc->fam));
        !           113: #endif
        !           114: 
        !           115: #ifdef DEBUG_STAT_CACHE
        !           116:        ctrl.size = 0;
        !           117: #endif
        !           118: 
        !           119:        return fc;
        !           120: }
        !           121: 
        !           122: static stat_cache_entry * stat_cache_entry_init(void) {
        !           123:        stat_cache_entry *sce = NULL;
        !           124: 
        !           125:        sce = calloc(1, sizeof(*sce));
        !           126: 
        !           127:        sce->name = buffer_init();
        !           128:        sce->etag = buffer_init();
        !           129:        sce->content_type = buffer_init();
        !           130: 
        !           131:        return sce;
        !           132: }
        !           133: 
        !           134: static void stat_cache_entry_free(void *data) {
        !           135:        stat_cache_entry *sce = data;
        !           136:        if (!sce) return;
        !           137: 
        !           138:        buffer_free(sce->etag);
        !           139:        buffer_free(sce->name);
        !           140:        buffer_free(sce->content_type);
        !           141: 
        !           142:        free(sce);
        !           143: }
        !           144: 
        !           145: #ifdef HAVE_FAM_H
        !           146: static fam_dir_entry * fam_dir_entry_init(void) {
        !           147:        fam_dir_entry *fam_dir = NULL;
        !           148: 
        !           149:        fam_dir = calloc(1, sizeof(*fam_dir));
        !           150: 
        !           151:        fam_dir->name = buffer_init();
        !           152: 
        !           153:        return fam_dir;
        !           154: }
        !           155: 
        !           156: static void fam_dir_entry_free(void *data) {
        !           157:        fam_dir_entry *fam_dir = data;
        !           158: 
        !           159:        if (!fam_dir) return;
        !           160: 
        !           161:        FAMCancelMonitor(fam_dir->fc, fam_dir->req);
        !           162: 
        !           163:        buffer_free(fam_dir->name);
        !           164:        free(fam_dir->req);
        !           165: 
        !           166:        free(fam_dir);
        !           167: }
        !           168: #endif
        !           169: 
        !           170: void stat_cache_free(stat_cache *sc) {
        !           171:        while (sc->files) {
        !           172:                int osize;
        !           173:                splay_tree *node = sc->files;
        !           174: 
        !           175:                osize = sc->files->size;
        !           176: 
        !           177:                stat_cache_entry_free(node->data);
        !           178:                sc->files = splaytree_delete(sc->files, node->key);
        !           179: 
        !           180:                assert(osize - 1 == splaytree_size(sc->files));
        !           181:        }
        !           182: 
        !           183:        buffer_free(sc->dir_name);
        !           184:        buffer_free(sc->hash_key);
        !           185: 
        !           186: #ifdef HAVE_FAM_H
        !           187:        while (sc->dirs) {
        !           188:                int osize;
        !           189:                splay_tree *node = sc->dirs;
        !           190: 
        !           191:                osize = sc->dirs->size;
        !           192: 
        !           193:                fam_dir_entry_free(node->data);
        !           194:                sc->dirs = splaytree_delete(sc->dirs, node->key);
        !           195: 
        !           196:                if (osize == 1) {
        !           197:                        assert(NULL == sc->dirs);
        !           198:                } else {
        !           199:                        assert(osize == (sc->dirs->size + 1));
        !           200:                }
        !           201:        }
        !           202: 
        !           203:        if (sc->fam) {
        !           204:                FAMClose(sc->fam);
        !           205:                free(sc->fam);
        !           206:        }
        !           207: #endif
        !           208:        free(sc);
        !           209: }
        !           210: 
        !           211: #ifdef HAVE_XATTR
        !           212: static int stat_cache_attr_get(buffer *buf, char *name) {
        !           213:        int attrlen;
        !           214:        int ret;
        !           215: 
        !           216:        attrlen = 1024;
        !           217:        buffer_prepare_copy(buf, attrlen);
        !           218:        attrlen--;
        !           219:        if(0 == (ret = attr_get(name, "Content-Type", buf->ptr, &attrlen, 0))) {
        !           220:                buf->used = attrlen + 1;
        !           221:                buf->ptr[attrlen] = '\0';
        !           222:        }
        !           223:        return ret;
        !           224: }
        !           225: #endif
        !           226: 
        !           227: /* the famous DJB hash function for strings */
        !           228: static uint32_t hashme(buffer *str) {
        !           229:        uint32_t hash = 5381;
        !           230:        const char *s;
        !           231:        for (s = str->ptr; *s; s++) {
        !           232:                hash = ((hash << 5) + hash) + *s;
        !           233:        }
        !           234: 
        !           235:        hash &= ~(1 << 31); /* strip the highest bit */
        !           236: 
        !           237:        return hash;
        !           238: }
        !           239: 
        !           240: #ifdef HAVE_FAM_H
        !           241: handler_t stat_cache_handle_fdevent(server *srv, void *_fce, int revent) {
        !           242:        size_t i;
        !           243:        stat_cache *sc = srv->stat_cache;
        !           244:        size_t events;
        !           245: 
        !           246:        UNUSED(_fce);
        !           247:        /* */
        !           248: 
        !           249:        if ((revent & FDEVENT_IN) &&
        !           250:            sc->fam) {
        !           251: 
        !           252:                events = FAMPending(sc->fam);
        !           253: 
        !           254:                for (i = 0; i < events; i++) {
        !           255:                        FAMEvent fe;
        !           256:                        fam_dir_entry *fam_dir;
        !           257:                        splay_tree *node;
        !           258:                        int ndx, j;
        !           259: 
        !           260:                        FAMNextEvent(sc->fam, &fe);
        !           261: 
        !           262:                        /* handle event */
        !           263: 
        !           264:                        switch(fe.code) {
        !           265:                        case FAMChanged:
        !           266:                        case FAMDeleted:
        !           267:                        case FAMMoved:
        !           268:                                /* if the filename is a directory remove the entry */
        !           269: 
        !           270:                                fam_dir = fe.userdata;
        !           271:                                fam_dir->version++;
        !           272: 
        !           273:                                /* file/dir is still here */
        !           274:                                if (fe.code == FAMChanged) break;
        !           275: 
        !           276:                                /* we have 2 versions, follow and no-follow-symlink */
        !           277: 
        !           278:                                for (j = 0; j < 2; j++) {
        !           279:                                        buffer_copy_string(sc->hash_key, fe.filename);
        !           280:                                        buffer_append_long(sc->hash_key, j);
        !           281: 
        !           282:                                        ndx = hashme(sc->hash_key);
        !           283: 
        !           284:                                        sc->dirs = splaytree_splay(sc->dirs, ndx);
        !           285:                                        node = sc->dirs;
        !           286: 
        !           287:                                        if (node && (node->key == ndx)) {
        !           288:                                                int osize = splaytree_size(sc->dirs);
        !           289: 
        !           290:                                                fam_dir_entry_free(node->data);
        !           291:                                                sc->dirs = splaytree_delete(sc->dirs, ndx);
        !           292: 
        !           293:                                                assert(osize - 1 == splaytree_size(sc->dirs));
        !           294:                                        }
        !           295:                                }
        !           296:                                break;
        !           297:                        default:
        !           298:                                break;
        !           299:                        }
        !           300:                }
        !           301:        }
        !           302: 
        !           303:        if (revent & FDEVENT_HUP) {
        !           304:                /* fam closed the connection */
        !           305:                srv->stat_cache->fam_fcce_ndx = -1;
        !           306: 
        !           307:                fdevent_event_del(srv->ev, &(sc->fam_fcce_ndx), FAMCONNECTION_GETFD(sc->fam));
        !           308:                fdevent_unregister(srv->ev, FAMCONNECTION_GETFD(sc->fam));
        !           309: 
        !           310:                FAMClose(sc->fam);
        !           311:                free(sc->fam);
        !           312: 
        !           313:                sc->fam = NULL;
        !           314:        }
        !           315: 
        !           316:        return HANDLER_GO_ON;
        !           317: }
        !           318: 
        !           319: static int buffer_copy_dirname(buffer *dst, buffer *file) {
        !           320:        size_t i;
        !           321: 
        !           322:        if (buffer_is_empty(file)) return -1;
        !           323: 
        !           324:        for (i = file->used - 1; i+1 > 0; i--) {
        !           325:                if (file->ptr[i] == '/') {
        !           326:                        buffer_copy_string_len(dst, file->ptr, i);
        !           327:                        return 0;
        !           328:                }
        !           329:        }
        !           330: 
        !           331:        return -1;
        !           332: }
        !           333: #endif
        !           334: 
        !           335: #ifdef HAVE_LSTAT
        !           336: static int stat_cache_lstat(server *srv, buffer *dname, struct stat *lst) {
        !           337:        if (lstat(dname->ptr, lst) == 0) {
        !           338:                return S_ISLNK(lst->st_mode) ? 0 : 1;
        !           339:        }
        !           340:        else {
        !           341:                log_error_write(srv, __FILE__, __LINE__, "sbs",
        !           342:                                "lstat failed for:",
        !           343:                                dname, strerror(errno));
        !           344:        };
        !           345:        return -1;
        !           346: }
        !           347: #endif
        !           348: 
        !           349: /***
        !           350:  *
        !           351:  *
        !           352:  *
        !           353:  * returns:
        !           354:  *  - HANDLER_FINISHED on cache-miss (don't forget to reopen the file)
        !           355:  *  - HANDLER_ERROR on stat() failed -> see errno for problem
        !           356:  */
        !           357: 
        !           358: handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_cache_entry **ret_sce) {
        !           359: #ifdef HAVE_FAM_H
        !           360:        fam_dir_entry *fam_dir = NULL;
        !           361:        int dir_ndx = -1;
        !           362:        splay_tree *dir_node = NULL;
        !           363: #endif
        !           364:        stat_cache_entry *sce = NULL;
        !           365:        stat_cache *sc;
        !           366:        struct stat st;
        !           367:        size_t k;
        !           368:        int fd;
        !           369:        struct stat lst;
        !           370: #ifdef DEBUG_STAT_CACHE
        !           371:        size_t i;
        !           372: #endif
        !           373: 
        !           374:        int file_ndx;
        !           375:        splay_tree *file_node = NULL;
        !           376: 
        !           377:        *ret_sce = NULL;
        !           378: 
        !           379:        /*
        !           380:         * check if the directory for this file has changed
        !           381:         */
        !           382: 
        !           383:        sc = srv->stat_cache;
        !           384: 
        !           385:        buffer_copy_string_buffer(sc->hash_key, name);
        !           386:        buffer_append_long(sc->hash_key, con->conf.follow_symlink);
        !           387: 
        !           388:        file_ndx = hashme(sc->hash_key);
        !           389:        sc->files = splaytree_splay(sc->files, file_ndx);
        !           390: 
        !           391: #ifdef DEBUG_STAT_CACHE
        !           392:        for (i = 0; i < ctrl.used; i++) {
        !           393:                if (ctrl.ptr[i] == file_ndx) break;
        !           394:        }
        !           395: #endif
        !           396: 
        !           397:        if (sc->files && (sc->files->key == file_ndx)) {
        !           398: #ifdef DEBUG_STAT_CACHE
        !           399:                /* it was in the cache */
        !           400:                assert(i < ctrl.used);
        !           401: #endif
        !           402: 
        !           403:                /* we have seen this file already and
        !           404:                 * don't stat() it again in the same second */
        !           405: 
        !           406:                file_node = sc->files;
        !           407: 
        !           408:                sce = file_node->data;
        !           409: 
        !           410:                /* check if the name is the same, we might have a collision */
        !           411: 
        !           412:                if (buffer_is_equal(name, sce->name)) {
        !           413:                        if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_SIMPLE) {
        !           414:                                if (sce->stat_ts == srv->cur_ts) {
        !           415:                                        *ret_sce = sce;
        !           416:                                        return HANDLER_GO_ON;
        !           417:                                }
        !           418:                        }
        !           419:                } else {
        !           420:                        /* oops, a collision,
        !           421:                         *
        !           422:                         * file_node is used by the FAM check below to see if we know this file
        !           423:                         * and if we can save a stat().
        !           424:                         *
        !           425:                         * BUT, the sce is not reset here as the entry into the cache is ok, we
        !           426:                         * it is just not pointing to our requested file.
        !           427:                         *
        !           428:                         *  */
        !           429: 
        !           430:                        file_node = NULL;
        !           431:                }
        !           432:        } else {
        !           433: #ifdef DEBUG_STAT_CACHE
        !           434:                if (i != ctrl.used) {
        !           435:                        log_error_write(srv, __FILE__, __LINE__, "xSB",
        !           436:                                file_ndx, "was already inserted but not found in cache, ", name);
        !           437:                }
        !           438:                assert(i == ctrl.used);
        !           439: #endif
        !           440:        }
        !           441: 
        !           442: #ifdef HAVE_FAM_H
        !           443:        /* dir-check */
        !           444:        if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) {
        !           445:                if (0 != buffer_copy_dirname(sc->dir_name, name)) {
        !           446:                        log_error_write(srv, __FILE__, __LINE__, "sb",
        !           447:                                "no '/' found in filename:", name);
        !           448:                        return HANDLER_ERROR;
        !           449:                }
        !           450: 
        !           451:                buffer_copy_string_buffer(sc->hash_key, sc->dir_name);
        !           452:                buffer_append_long(sc->hash_key, con->conf.follow_symlink);
        !           453: 
        !           454:                dir_ndx = hashme(sc->hash_key);
        !           455: 
        !           456:                sc->dirs = splaytree_splay(sc->dirs, dir_ndx);
        !           457: 
        !           458:                if (sc->dirs && (sc->dirs->key == dir_ndx)) {
        !           459:                        dir_node = sc->dirs;
        !           460:                }
        !           461: 
        !           462:                if (dir_node && file_node) {
        !           463:                        /* we found a file */
        !           464: 
        !           465:                        sce = file_node->data;
        !           466:                        fam_dir = dir_node->data;
        !           467: 
        !           468:                        if (fam_dir->version == sce->dir_version) {
        !           469:                                /* the stat()-cache entry is still ok */
        !           470: 
        !           471:                                *ret_sce = sce;
        !           472:                                return HANDLER_GO_ON;
        !           473:                        }
        !           474:                }
        !           475:        }
        !           476: #endif
        !           477: 
        !           478:        /*
        !           479:         * *lol*
        !           480:         * - open() + fstat() on a named-pipe results in a (intended) hang.
        !           481:         * - stat() if regular file + open() to see if we can read from it is better
        !           482:         *
        !           483:         * */
        !           484:        if (-1 == stat(name->ptr, &st)) {
        !           485:                return HANDLER_ERROR;
        !           486:        }
        !           487: 
        !           488: 
        !           489:        if (S_ISREG(st.st_mode)) {
        !           490:                /* fix broken stat/open for symlinks to reg files with appended slash on freebsd,osx */
        !           491:                if (name->ptr[name->used-2] == '/') {
        !           492:                        errno = ENOTDIR;
        !           493:                        return HANDLER_ERROR;
        !           494:                }
        !           495: 
        !           496:                /* try to open the file to check if we can read it */
        !           497:                if (-1 == (fd = open(name->ptr, O_RDONLY))) {
        !           498:                        return HANDLER_ERROR;
        !           499:                }
        !           500:                close(fd);
        !           501:        }
        !           502: 
        !           503:        if (NULL == sce) {
        !           504: #ifdef DEBUG_STAT_CACHE
        !           505:                int osize = splaytree_size(sc->files);
        !           506: #endif
        !           507: 
        !           508:                sce = stat_cache_entry_init();
        !           509:                buffer_copy_string_buffer(sce->name, name);
        !           510: 
        !           511:                sc->files = splaytree_insert(sc->files, file_ndx, sce);
        !           512: #ifdef DEBUG_STAT_CACHE
        !           513:                if (ctrl.size == 0) {
        !           514:                        ctrl.size = 16;
        !           515:                        ctrl.used = 0;
        !           516:                        ctrl.ptr = malloc(ctrl.size * sizeof(*ctrl.ptr));
        !           517:                } else if (ctrl.size == ctrl.used) {
        !           518:                        ctrl.size += 16;
        !           519:                        ctrl.ptr = realloc(ctrl.ptr, ctrl.size * sizeof(*ctrl.ptr));
        !           520:                }
        !           521: 
        !           522:                ctrl.ptr[ctrl.used++] = file_ndx;
        !           523: 
        !           524:                assert(sc->files);
        !           525:                assert(sc->files->data == sce);
        !           526:                assert(osize + 1 == splaytree_size(sc->files));
        !           527: #endif
        !           528:        }
        !           529: 
        !           530:        sce->st = st;
        !           531:        sce->stat_ts = srv->cur_ts;
        !           532: 
        !           533:        /* catch the obvious symlinks
        !           534:         *
        !           535:         * this is not a secure check as we still have a race-condition between
        !           536:         * the stat() and the open. We can only solve this by
        !           537:         * 1. open() the file
        !           538:         * 2. fstat() the fd
        !           539:         *
        !           540:         * and keeping the file open for the rest of the time. But this can
        !           541:         * only be done at network level.
        !           542:         *
        !           543:         * per default it is not a symlink
        !           544:         * */
        !           545: #ifdef HAVE_LSTAT
        !           546:        sce->is_symlink = 0;
        !           547: 
        !           548:        /* we want to only check for symlinks if we should block symlinks.
        !           549:         */
        !           550:        if (!con->conf.follow_symlink) {
        !           551:                if (stat_cache_lstat(srv, name, &lst)  == 0) {
        !           552: #ifdef DEBUG_STAT_CACHE
        !           553:                                log_error_write(srv, __FILE__, __LINE__, "sb",
        !           554:                                                "found symlink", name);
        !           555: #endif
        !           556:                                sce->is_symlink = 1;
        !           557:                }
        !           558: 
        !           559:                /*
        !           560:                 * we assume "/" can not be symlink, so
        !           561:                 * skip the symlink stuff if our path is /
        !           562:                 **/
        !           563:                else if ((name->used > 2)) {
        !           564:                        buffer *dname;
        !           565:                        char *s_cur;
        !           566: 
        !           567:                        dname = buffer_init();
        !           568:                        buffer_copy_string_buffer(dname, name);
        !           569: 
        !           570:                        while ((s_cur = strrchr(dname->ptr,'/'))) {
        !           571:                                *s_cur = '\0';
        !           572:                                dname->used = s_cur - dname->ptr + 1;
        !           573:                                if (dname->ptr == s_cur) {
        !           574: #ifdef DEBUG_STAT_CACHE
        !           575:                                        log_error_write(srv, __FILE__, __LINE__, "s", "reached /");
        !           576: #endif
        !           577:                                        break;
        !           578:                                }
        !           579: #ifdef DEBUG_STAT_CACHE
        !           580:                                log_error_write(srv, __FILE__, __LINE__, "sbs",
        !           581:                                                "checking if", dname, "is a symlink");
        !           582: #endif
        !           583:                                if (stat_cache_lstat(srv, dname, &lst)  == 0) {
        !           584:                                        sce->is_symlink = 1;
        !           585: #ifdef DEBUG_STAT_CACHE
        !           586:                                        log_error_write(srv, __FILE__, __LINE__, "sb",
        !           587:                                                        "found symlink", dname);
        !           588: #endif
        !           589:                                        break;
        !           590:                                };
        !           591:                        };
        !           592:                        buffer_free(dname);
        !           593:                };
        !           594:        };
        !           595: #endif
        !           596: 
        !           597:        if (S_ISREG(st.st_mode)) {
        !           598:                /* determine mimetype */
        !           599:                buffer_reset(sce->content_type);
        !           600: #ifdef HAVE_XATTR
        !           601:                if (con->conf.use_xattr) {
        !           602:                        stat_cache_attr_get(sce->content_type, name->ptr);
        !           603:                }
        !           604: #endif
        !           605:                /* xattr did not set a content-type. ask the config */
        !           606:                if (buffer_is_empty(sce->content_type)) {
        !           607:                        for (k = 0; k < con->conf.mimetypes->used; k++) {
        !           608:                                data_string *ds = (data_string *)con->conf.mimetypes->data[k];
        !           609:                                buffer *type = ds->key;
        !           610: 
        !           611:                                if (type->used == 0) continue;
        !           612: 
        !           613:                                /* check if the right side is the same */
        !           614:                                if (type->used > name->used) continue;
        !           615: 
        !           616:                                if (0 == strncasecmp(name->ptr + name->used - type->used, type->ptr, type->used - 1)) {
        !           617:                                        buffer_copy_string_buffer(sce->content_type, ds->value);
        !           618:                                        break;
        !           619:                                }
        !           620:                        }
        !           621:                }
        !           622:                etag_create(sce->etag, &(sce->st), con->etag_flags);
        !           623:        } else if (S_ISDIR(st.st_mode)) {
        !           624:                etag_create(sce->etag, &(sce->st), con->etag_flags);
        !           625:        }
        !           626: 
        !           627: #ifdef HAVE_FAM_H
        !           628:        if (sc->fam &&
        !           629:            (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM)) {
        !           630:                /* is this directory already registered ? */
        !           631:                if (!dir_node) {
        !           632:                        fam_dir = fam_dir_entry_init();
        !           633:                        fam_dir->fc = sc->fam;
        !           634: 
        !           635:                        buffer_copy_string_buffer(fam_dir->name, sc->dir_name);
        !           636: 
        !           637:                        fam_dir->version = 1;
        !           638: 
        !           639:                        fam_dir->req = calloc(1, sizeof(FAMRequest));
        !           640: 
        !           641:                        if (0 != FAMMonitorDirectory(sc->fam, fam_dir->name->ptr,
        !           642:                                                     fam_dir->req, fam_dir)) {
        !           643: 
        !           644:                                log_error_write(srv, __FILE__, __LINE__, "sbsbs",
        !           645:                                                "monitoring dir failed:",
        !           646:                                                fam_dir->name, 
        !           647:                                                "file:", name,
        !           648:                                                FamErrlist[FAMErrno]);
        !           649: 
        !           650:                                fam_dir_entry_free(fam_dir);
        !           651:                        } else {
        !           652:                                int osize = 0;
        !           653: 
        !           654:                                if (sc->dirs) {
        !           655:                                        osize = sc->dirs->size;
        !           656:                                }
        !           657: 
        !           658:                                sc->dirs = splaytree_insert(sc->dirs, dir_ndx, fam_dir);
        !           659:                                assert(sc->dirs);
        !           660:                                assert(sc->dirs->data == fam_dir);
        !           661:                                assert(osize == (sc->dirs->size - 1));
        !           662:                        }
        !           663:                } else {
        !           664:                        fam_dir = dir_node->data;
        !           665:                }
        !           666: 
        !           667:                /* bind the fam_fc to the stat() cache entry */
        !           668: 
        !           669:                if (fam_dir) {
        !           670:                        sce->dir_version = fam_dir->version;
        !           671:                        sce->dir_ndx     = dir_ndx;
        !           672:                }
        !           673:        }
        !           674: #endif
        !           675: 
        !           676:        *ret_sce = sce;
        !           677: 
        !           678:        return HANDLER_GO_ON;
        !           679: }
        !           680: 
        !           681: /**
        !           682:  * remove stat() from cache which havn't been stat()ed for
        !           683:  * more than 10 seconds
        !           684:  *
        !           685:  *
        !           686:  * walk though the stat-cache, collect the ids which are too old
        !           687:  * and remove them in a second loop
        !           688:  */
        !           689: 
        !           690: static int stat_cache_tag_old_entries(server *srv, splay_tree *t, int *keys, size_t *ndx) {
        !           691:        stat_cache_entry *sce;
        !           692: 
        !           693:        if (!t) return 0;
        !           694: 
        !           695:        stat_cache_tag_old_entries(srv, t->left, keys, ndx);
        !           696:        stat_cache_tag_old_entries(srv, t->right, keys, ndx);
        !           697: 
        !           698:        sce = t->data;
        !           699: 
        !           700:        if (srv->cur_ts - sce->stat_ts > 2) {
        !           701:                keys[(*ndx)++] = t->key;
        !           702:        }
        !           703: 
        !           704:        return 0;
        !           705: }
        !           706: 
        !           707: int stat_cache_trigger_cleanup(server *srv) {
        !           708:        stat_cache *sc;
        !           709:        size_t max_ndx = 0, i;
        !           710:        int *keys;
        !           711: 
        !           712:        sc = srv->stat_cache;
        !           713: 
        !           714:        if (!sc->files) return 0;
        !           715: 
        !           716:        keys = calloc(1, sizeof(size_t) * sc->files->size);
        !           717: 
        !           718:        stat_cache_tag_old_entries(srv, sc->files, keys, &max_ndx);
        !           719: 
        !           720:        for (i = 0; i < max_ndx; i++) {
        !           721:                int ndx = keys[i];
        !           722:                splay_tree *node;
        !           723: 
        !           724:                sc->files = splaytree_splay(sc->files, ndx);
        !           725: 
        !           726:                node = sc->files;
        !           727: 
        !           728:                if (node && (node->key == ndx)) {
        !           729: #ifdef DEBUG_STAT_CACHE
        !           730:                        size_t j;
        !           731:                        int osize = splaytree_size(sc->files);
        !           732:                        stat_cache_entry *sce = node->data;
        !           733: #endif
        !           734:                        stat_cache_entry_free(node->data);
        !           735:                        sc->files = splaytree_delete(sc->files, ndx);
        !           736: 
        !           737: #ifdef DEBUG_STAT_CACHE
        !           738:                        for (j = 0; j < ctrl.used; j++) {
        !           739:                                if (ctrl.ptr[j] == ndx) {
        !           740:                                        ctrl.ptr[j] = ctrl.ptr[--ctrl.used];
        !           741:                                        break;
        !           742:                                }
        !           743:                        }
        !           744: 
        !           745:                        assert(osize - 1 == splaytree_size(sc->files));
        !           746: #endif
        !           747:                }
        !           748:        }
        !           749: 
        !           750:        free(keys);
        !           751: 
        !           752:        return 0;
        !           753: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>