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

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: 
                     73:        buffer *name;
                     74: 
                     75:        int version;
                     76: } fam_dir_entry;
                     77: #endif
                     78: 
                     79: /* the directory name is too long to always compare on it
                     80:  * - we need a hash
                     81:  * - the hash-key is used as sorting criteria for a tree
                     82:  * - a splay-tree is used as we can use the caching effect of it
                     83:  */
                     84: 
                     85: /* we want to cleanup the stat-cache every few seconds, let's say 10
                     86:  *
                     87:  * - remove entries which are outdated since 30s
                     88:  * - remove entries which are fresh but havn't been used since 60s
                     89:  * - if we don't have a stat-cache entry for a directory, release it from the monitor
                     90:  */
                     91: 
                     92: #ifdef DEBUG_STAT_CACHE
                     93: typedef struct {
                     94:        int *ptr;
                     95: 
                     96:        size_t used;
                     97:        size_t size;
                     98: } fake_keys;
                     99: 
                    100: static fake_keys ctrl;
                    101: #endif
                    102: 
                    103: stat_cache *stat_cache_init(void) {
1.1.1.2 ! misho     104:        stat_cache *sc = NULL;
1.1       misho     105: 
1.1.1.2 ! misho     106:        sc = calloc(1, sizeof(*sc));
        !           107: 
        !           108:        sc->dir_name = buffer_init();
        !           109:        sc->hash_key = buffer_init();
1.1       misho     110: 
                    111: #ifdef HAVE_FAM_H
1.1.1.2 ! misho     112:        sc->fam_fcce_ndx = -1;
1.1       misho     113: #endif
                    114: 
                    115: #ifdef DEBUG_STAT_CACHE
                    116:        ctrl.size = 0;
                    117: #endif
                    118: 
1.1.1.2 ! misho     119:        return sc;
1.1       misho     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: 
1.1.1.2 ! misho     156: static void fam_dir_entry_free(FAMConnection *fc, void *data) {
1.1       misho     157:        fam_dir_entry *fam_dir = data;
                    158: 
                    159:        if (!fam_dir) return;
                    160: 
1.1.1.2 ! misho     161:        FAMCancelMonitor(fc, fam_dir->req);
1.1       misho     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: 
1.1.1.2 ! misho     180:                force_assert(osize - 1 == splaytree_size(sc->files));
1.1       misho     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: 
1.1.1.2 ! misho     193:                fam_dir_entry_free(&sc->fam, node->data);
1.1       misho     194:                sc->dirs = splaytree_delete(sc->dirs, node->key);
                    195: 
                    196:                if (osize == 1) {
1.1.1.2 ! misho     197:                        force_assert(NULL == sc->dirs);
1.1       misho     198:                } else {
1.1.1.2 ! misho     199:                        force_assert(osize == (sc->dirs->size + 1));
1.1       misho     200:                }
                    201:        }
                    202: 
1.1.1.2 ! misho     203:        if (-1 != sc->fam_fcce_ndx) {
        !           204:                /* fd events already gone */
        !           205:                sc->fam_fcce_ndx = -1;
        !           206: 
        !           207:                FAMClose(&sc->fam);
1.1       misho     208:        }
                    209: #endif
                    210:        free(sc);
                    211: }
                    212: 
                    213: #ifdef HAVE_XATTR
                    214: static int stat_cache_attr_get(buffer *buf, char *name) {
                    215:        int attrlen;
                    216:        int ret;
                    217: 
                    218:        attrlen = 1024;
                    219:        buffer_prepare_copy(buf, attrlen);
                    220:        attrlen--;
                    221:        if(0 == (ret = attr_get(name, "Content-Type", buf->ptr, &attrlen, 0))) {
                    222:                buf->used = attrlen + 1;
                    223:                buf->ptr[attrlen] = '\0';
                    224:        }
                    225:        return ret;
                    226: }
                    227: #endif
                    228: 
                    229: /* the famous DJB hash function for strings */
                    230: static uint32_t hashme(buffer *str) {
                    231:        uint32_t hash = 5381;
                    232:        const char *s;
                    233:        for (s = str->ptr; *s; s++) {
                    234:                hash = ((hash << 5) + hash) + *s;
                    235:        }
                    236: 
                    237:        hash &= ~(1 << 31); /* strip the highest bit */
                    238: 
                    239:        return hash;
                    240: }
                    241: 
                    242: #ifdef HAVE_FAM_H
                    243: handler_t stat_cache_handle_fdevent(server *srv, void *_fce, int revent) {
                    244:        size_t i;
                    245:        stat_cache *sc = srv->stat_cache;
                    246:        size_t events;
                    247: 
                    248:        UNUSED(_fce);
                    249:        /* */
                    250: 
1.1.1.2 ! misho     251:        if (revent & FDEVENT_IN) {
        !           252:                events = FAMPending(&sc->fam);
1.1       misho     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: 
1.1.1.2 ! misho     260:                        FAMNextEvent(&sc->fam, &fe);
1.1       misho     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: 
1.1.1.2 ! misho     290:                                                fam_dir_entry_free(&sc->fam, node->data);
1.1       misho     291:                                                sc->dirs = splaytree_delete(sc->dirs, ndx);
                    292: 
1.1.1.2 ! misho     293:                                                force_assert(osize - 1 == splaytree_size(sc->dirs));
1.1       misho     294:                                        }
                    295:                                }
                    296:                                break;
                    297:                        default:
                    298:                                break;
                    299:                        }
                    300:                }
                    301:        }
                    302: 
                    303:        if (revent & FDEVENT_HUP) {
                    304:                /* fam closed the connection */
1.1.1.2 ! misho     305:                fdevent_event_del(srv->ev, &(sc->fam_fcce_ndx), FAMCONNECTION_GETFD(&sc->fam));
        !           306:                fdevent_unregister(srv->ev, FAMCONNECTION_GETFD(&sc->fam));
1.1       misho     307: 
1.1.1.2 ! misho     308:                FAMClose(&sc->fam);
1.1       misho     309:        }
                    310: 
                    311:        return HANDLER_GO_ON;
                    312: }
                    313: 
                    314: static int buffer_copy_dirname(buffer *dst, buffer *file) {
                    315:        size_t i;
                    316: 
                    317:        if (buffer_is_empty(file)) return -1;
                    318: 
                    319:        for (i = file->used - 1; i+1 > 0; i--) {
                    320:                if (file->ptr[i] == '/') {
                    321:                        buffer_copy_string_len(dst, file->ptr, i);
                    322:                        return 0;
                    323:                }
                    324:        }
                    325: 
                    326:        return -1;
                    327: }
                    328: #endif
                    329: 
                    330: #ifdef HAVE_LSTAT
                    331: static int stat_cache_lstat(server *srv, buffer *dname, struct stat *lst) {
                    332:        if (lstat(dname->ptr, lst) == 0) {
                    333:                return S_ISLNK(lst->st_mode) ? 0 : 1;
                    334:        }
                    335:        else {
                    336:                log_error_write(srv, __FILE__, __LINE__, "sbs",
                    337:                                "lstat failed for:",
                    338:                                dname, strerror(errno));
                    339:        };
                    340:        return -1;
                    341: }
                    342: #endif
                    343: 
                    344: /***
                    345:  *
                    346:  *
                    347:  *
                    348:  * returns:
                    349:  *  - HANDLER_FINISHED on cache-miss (don't forget to reopen the file)
                    350:  *  - HANDLER_ERROR on stat() failed -> see errno for problem
                    351:  */
                    352: 
                    353: handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_cache_entry **ret_sce) {
                    354: #ifdef HAVE_FAM_H
                    355:        fam_dir_entry *fam_dir = NULL;
                    356:        int dir_ndx = -1;
                    357:        splay_tree *dir_node = NULL;
                    358: #endif
                    359:        stat_cache_entry *sce = NULL;
                    360:        stat_cache *sc;
                    361:        struct stat st;
                    362:        size_t k;
                    363:        int fd;
                    364:        struct stat lst;
                    365: #ifdef DEBUG_STAT_CACHE
                    366:        size_t i;
                    367: #endif
                    368: 
                    369:        int file_ndx;
                    370:        splay_tree *file_node = NULL;
                    371: 
                    372:        *ret_sce = NULL;
                    373: 
                    374:        /*
                    375:         * check if the directory for this file has changed
                    376:         */
                    377: 
                    378:        sc = srv->stat_cache;
                    379: 
                    380:        buffer_copy_string_buffer(sc->hash_key, name);
                    381:        buffer_append_long(sc->hash_key, con->conf.follow_symlink);
                    382: 
                    383:        file_ndx = hashme(sc->hash_key);
                    384:        sc->files = splaytree_splay(sc->files, file_ndx);
                    385: 
                    386: #ifdef DEBUG_STAT_CACHE
                    387:        for (i = 0; i < ctrl.used; i++) {
                    388:                if (ctrl.ptr[i] == file_ndx) break;
                    389:        }
                    390: #endif
                    391: 
                    392:        if (sc->files && (sc->files->key == file_ndx)) {
                    393: #ifdef DEBUG_STAT_CACHE
                    394:                /* it was in the cache */
1.1.1.2 ! misho     395:                force_assert(i < ctrl.used);
1.1       misho     396: #endif
                    397: 
                    398:                /* we have seen this file already and
                    399:                 * don't stat() it again in the same second */
                    400: 
                    401:                file_node = sc->files;
                    402: 
                    403:                sce = file_node->data;
                    404: 
                    405:                /* check if the name is the same, we might have a collision */
                    406: 
                    407:                if (buffer_is_equal(name, sce->name)) {
                    408:                        if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_SIMPLE) {
                    409:                                if (sce->stat_ts == srv->cur_ts) {
                    410:                                        *ret_sce = sce;
                    411:                                        return HANDLER_GO_ON;
                    412:                                }
                    413:                        }
                    414:                } else {
                    415:                        /* oops, a collision,
                    416:                         *
                    417:                         * file_node is used by the FAM check below to see if we know this file
                    418:                         * and if we can save a stat().
                    419:                         *
                    420:                         * BUT, the sce is not reset here as the entry into the cache is ok, we
                    421:                         * it is just not pointing to our requested file.
                    422:                         *
                    423:                         *  */
                    424: 
                    425:                        file_node = NULL;
                    426:                }
                    427:        } else {
                    428: #ifdef DEBUG_STAT_CACHE
                    429:                if (i != ctrl.used) {
                    430:                        log_error_write(srv, __FILE__, __LINE__, "xSB",
                    431:                                file_ndx, "was already inserted but not found in cache, ", name);
                    432:                }
1.1.1.2 ! misho     433:                force_assert(i == ctrl.used);
1.1       misho     434: #endif
                    435:        }
                    436: 
                    437: #ifdef HAVE_FAM_H
                    438:        /* dir-check */
                    439:        if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) {
                    440:                if (0 != buffer_copy_dirname(sc->dir_name, name)) {
                    441:                        log_error_write(srv, __FILE__, __LINE__, "sb",
                    442:                                "no '/' found in filename:", name);
                    443:                        return HANDLER_ERROR;
                    444:                }
                    445: 
                    446:                buffer_copy_string_buffer(sc->hash_key, sc->dir_name);
                    447:                buffer_append_long(sc->hash_key, con->conf.follow_symlink);
                    448: 
                    449:                dir_ndx = hashme(sc->hash_key);
                    450: 
                    451:                sc->dirs = splaytree_splay(sc->dirs, dir_ndx);
                    452: 
                    453:                if (sc->dirs && (sc->dirs->key == dir_ndx)) {
                    454:                        dir_node = sc->dirs;
                    455:                }
                    456: 
                    457:                if (dir_node && file_node) {
                    458:                        /* we found a file */
                    459: 
                    460:                        sce = file_node->data;
                    461:                        fam_dir = dir_node->data;
                    462: 
                    463:                        if (fam_dir->version == sce->dir_version) {
                    464:                                /* the stat()-cache entry is still ok */
                    465: 
                    466:                                *ret_sce = sce;
                    467:                                return HANDLER_GO_ON;
                    468:                        }
                    469:                }
                    470:        }
                    471: #endif
                    472: 
                    473:        /*
                    474:         * *lol*
                    475:         * - open() + fstat() on a named-pipe results in a (intended) hang.
                    476:         * - stat() if regular file + open() to see if we can read from it is better
                    477:         *
                    478:         * */
                    479:        if (-1 == stat(name->ptr, &st)) {
                    480:                return HANDLER_ERROR;
                    481:        }
                    482: 
                    483: 
                    484:        if (S_ISREG(st.st_mode)) {
                    485:                /* fix broken stat/open for symlinks to reg files with appended slash on freebsd,osx */
                    486:                if (name->ptr[name->used-2] == '/') {
                    487:                        errno = ENOTDIR;
                    488:                        return HANDLER_ERROR;
                    489:                }
                    490: 
                    491:                /* try to open the file to check if we can read it */
                    492:                if (-1 == (fd = open(name->ptr, O_RDONLY))) {
                    493:                        return HANDLER_ERROR;
                    494:                }
                    495:                close(fd);
                    496:        }
                    497: 
                    498:        if (NULL == sce) {
                    499: #ifdef DEBUG_STAT_CACHE
                    500:                int osize = splaytree_size(sc->files);
                    501: #endif
                    502: 
                    503:                sce = stat_cache_entry_init();
                    504:                buffer_copy_string_buffer(sce->name, name);
                    505: 
                    506:                sc->files = splaytree_insert(sc->files, file_ndx, sce);
                    507: #ifdef DEBUG_STAT_CACHE
                    508:                if (ctrl.size == 0) {
                    509:                        ctrl.size = 16;
                    510:                        ctrl.used = 0;
                    511:                        ctrl.ptr = malloc(ctrl.size * sizeof(*ctrl.ptr));
                    512:                } else if (ctrl.size == ctrl.used) {
                    513:                        ctrl.size += 16;
                    514:                        ctrl.ptr = realloc(ctrl.ptr, ctrl.size * sizeof(*ctrl.ptr));
                    515:                }
                    516: 
                    517:                ctrl.ptr[ctrl.used++] = file_ndx;
                    518: 
1.1.1.2 ! misho     519:                force_assert(sc->files);
        !           520:                force_assert(sc->files->data == sce);
        !           521:                force_assert(osize + 1 == splaytree_size(sc->files));
1.1       misho     522: #endif
                    523:        }
                    524: 
                    525:        sce->st = st;
                    526:        sce->stat_ts = srv->cur_ts;
                    527: 
                    528:        /* catch the obvious symlinks
                    529:         *
                    530:         * this is not a secure check as we still have a race-condition between
                    531:         * the stat() and the open. We can only solve this by
                    532:         * 1. open() the file
                    533:         * 2. fstat() the fd
                    534:         *
                    535:         * and keeping the file open for the rest of the time. But this can
                    536:         * only be done at network level.
                    537:         *
                    538:         * per default it is not a symlink
                    539:         * */
                    540: #ifdef HAVE_LSTAT
                    541:        sce->is_symlink = 0;
                    542: 
                    543:        /* we want to only check for symlinks if we should block symlinks.
                    544:         */
                    545:        if (!con->conf.follow_symlink) {
                    546:                if (stat_cache_lstat(srv, name, &lst)  == 0) {
                    547: #ifdef DEBUG_STAT_CACHE
                    548:                                log_error_write(srv, __FILE__, __LINE__, "sb",
                    549:                                                "found symlink", name);
                    550: #endif
                    551:                                sce->is_symlink = 1;
                    552:                }
                    553: 
                    554:                /*
                    555:                 * we assume "/" can not be symlink, so
                    556:                 * skip the symlink stuff if our path is /
                    557:                 **/
                    558:                else if ((name->used > 2)) {
                    559:                        buffer *dname;
                    560:                        char *s_cur;
                    561: 
                    562:                        dname = buffer_init();
                    563:                        buffer_copy_string_buffer(dname, name);
                    564: 
                    565:                        while ((s_cur = strrchr(dname->ptr,'/'))) {
                    566:                                *s_cur = '\0';
                    567:                                dname->used = s_cur - dname->ptr + 1;
                    568:                                if (dname->ptr == s_cur) {
                    569: #ifdef DEBUG_STAT_CACHE
                    570:                                        log_error_write(srv, __FILE__, __LINE__, "s", "reached /");
                    571: #endif
                    572:                                        break;
                    573:                                }
                    574: #ifdef DEBUG_STAT_CACHE
                    575:                                log_error_write(srv, __FILE__, __LINE__, "sbs",
                    576:                                                "checking if", dname, "is a symlink");
                    577: #endif
                    578:                                if (stat_cache_lstat(srv, dname, &lst)  == 0) {
                    579:                                        sce->is_symlink = 1;
                    580: #ifdef DEBUG_STAT_CACHE
                    581:                                        log_error_write(srv, __FILE__, __LINE__, "sb",
                    582:                                                        "found symlink", dname);
                    583: #endif
                    584:                                        break;
                    585:                                };
                    586:                        };
                    587:                        buffer_free(dname);
                    588:                };
                    589:        };
                    590: #endif
                    591: 
                    592:        if (S_ISREG(st.st_mode)) {
                    593:                /* determine mimetype */
                    594:                buffer_reset(sce->content_type);
                    595: #ifdef HAVE_XATTR
                    596:                if (con->conf.use_xattr) {
                    597:                        stat_cache_attr_get(sce->content_type, name->ptr);
                    598:                }
                    599: #endif
                    600:                /* xattr did not set a content-type. ask the config */
                    601:                if (buffer_is_empty(sce->content_type)) {
                    602:                        for (k = 0; k < con->conf.mimetypes->used; k++) {
                    603:                                data_string *ds = (data_string *)con->conf.mimetypes->data[k];
                    604:                                buffer *type = ds->key;
                    605: 
                    606:                                if (type->used == 0) continue;
                    607: 
                    608:                                /* check if the right side is the same */
                    609:                                if (type->used > name->used) continue;
                    610: 
                    611:                                if (0 == strncasecmp(name->ptr + name->used - type->used, type->ptr, type->used - 1)) {
                    612:                                        buffer_copy_string_buffer(sce->content_type, ds->value);
                    613:                                        break;
                    614:                                }
                    615:                        }
                    616:                }
                    617:                etag_create(sce->etag, &(sce->st), con->etag_flags);
                    618:        } else if (S_ISDIR(st.st_mode)) {
                    619:                etag_create(sce->etag, &(sce->st), con->etag_flags);
                    620:        }
                    621: 
                    622: #ifdef HAVE_FAM_H
1.1.1.2 ! misho     623:        if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) {
1.1       misho     624:                /* is this directory already registered ? */
                    625:                if (!dir_node) {
                    626:                        fam_dir = fam_dir_entry_init();
                    627: 
                    628:                        buffer_copy_string_buffer(fam_dir->name, sc->dir_name);
                    629: 
                    630:                        fam_dir->version = 1;
                    631: 
                    632:                        fam_dir->req = calloc(1, sizeof(FAMRequest));
                    633: 
1.1.1.2 ! misho     634:                        if (0 != FAMMonitorDirectory(&sc->fam, fam_dir->name->ptr,
1.1       misho     635:                                                     fam_dir->req, fam_dir)) {
                    636: 
                    637:                                log_error_write(srv, __FILE__, __LINE__, "sbsbs",
                    638:                                                "monitoring dir failed:",
                    639:                                                fam_dir->name, 
                    640:                                                "file:", name,
                    641:                                                FamErrlist[FAMErrno]);
                    642: 
1.1.1.2 ! misho     643:                                fam_dir_entry_free(&sc->fam, fam_dir);
        !           644:                                fam_dir = NULL;
1.1       misho     645:                        } else {
                    646:                                int osize = 0;
                    647: 
1.1.1.2 ! misho     648:                                if (sc->dirs) {
1.1       misho     649:                                        osize = sc->dirs->size;
                    650:                                }
                    651: 
                    652:                                sc->dirs = splaytree_insert(sc->dirs, dir_ndx, fam_dir);
1.1.1.2 ! misho     653:                                force_assert(sc->dirs);
        !           654:                                force_assert(sc->dirs->data == fam_dir);
        !           655:                                force_assert(osize == (sc->dirs->size - 1));
1.1       misho     656:                        }
                    657:                } else {
                    658:                        fam_dir = dir_node->data;
                    659:                }
                    660: 
                    661:                /* bind the fam_fc to the stat() cache entry */
                    662: 
                    663:                if (fam_dir) {
                    664:                        sce->dir_version = fam_dir->version;
                    665:                }
                    666:        }
                    667: #endif
                    668: 
                    669:        *ret_sce = sce;
                    670: 
                    671:        return HANDLER_GO_ON;
                    672: }
                    673: 
                    674: /**
                    675:  * remove stat() from cache which havn't been stat()ed for
                    676:  * more than 10 seconds
                    677:  *
                    678:  *
                    679:  * walk though the stat-cache, collect the ids which are too old
                    680:  * and remove them in a second loop
                    681:  */
                    682: 
                    683: static int stat_cache_tag_old_entries(server *srv, splay_tree *t, int *keys, size_t *ndx) {
                    684:        stat_cache_entry *sce;
                    685: 
                    686:        if (!t) return 0;
                    687: 
                    688:        stat_cache_tag_old_entries(srv, t->left, keys, ndx);
                    689:        stat_cache_tag_old_entries(srv, t->right, keys, ndx);
                    690: 
                    691:        sce = t->data;
                    692: 
                    693:        if (srv->cur_ts - sce->stat_ts > 2) {
                    694:                keys[(*ndx)++] = t->key;
                    695:        }
                    696: 
                    697:        return 0;
                    698: }
                    699: 
                    700: int stat_cache_trigger_cleanup(server *srv) {
                    701:        stat_cache *sc;
                    702:        size_t max_ndx = 0, i;
                    703:        int *keys;
                    704: 
                    705:        sc = srv->stat_cache;
                    706: 
                    707:        if (!sc->files) return 0;
                    708: 
1.1.1.2 ! misho     709:        keys = calloc(1, sizeof(int) * sc->files->size);
1.1       misho     710: 
                    711:        stat_cache_tag_old_entries(srv, sc->files, keys, &max_ndx);
                    712: 
                    713:        for (i = 0; i < max_ndx; i++) {
                    714:                int ndx = keys[i];
                    715:                splay_tree *node;
                    716: 
                    717:                sc->files = splaytree_splay(sc->files, ndx);
                    718: 
                    719:                node = sc->files;
                    720: 
                    721:                if (node && (node->key == ndx)) {
                    722: #ifdef DEBUG_STAT_CACHE
                    723:                        size_t j;
                    724:                        int osize = splaytree_size(sc->files);
                    725:                        stat_cache_entry *sce = node->data;
                    726: #endif
                    727:                        stat_cache_entry_free(node->data);
                    728:                        sc->files = splaytree_delete(sc->files, ndx);
                    729: 
                    730: #ifdef DEBUG_STAT_CACHE
                    731:                        for (j = 0; j < ctrl.used; j++) {
                    732:                                if (ctrl.ptr[j] == ndx) {
                    733:                                        ctrl.ptr[j] = ctrl.ptr[--ctrl.used];
                    734:                                        break;
                    735:                                }
                    736:                        }
                    737: 
1.1.1.2 ! misho     738:                        force_assert(osize - 1 == splaytree_size(sc->files));
1.1       misho     739: #endif
                    740:                }
                    741:        }
                    742: 
                    743:        free(keys);
                    744: 
                    745:        return 0;
                    746: }

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