Diff for /embedaddon/lighttpd/src/stat_cache.c between versions 1.1.1.2 and 1.1.1.3

version 1.1.1.2, 2014/06/15 20:20:06 version 1.1.1.3, 2016/11/02 10:35:00
Line 1 Line 1
   #include "first.h"
   
 #include "log.h"  #include "log.h"
 #include "stat_cache.h"  #include "stat_cache.h"
 #include "fdevent.h"  #include "fdevent.h"
Line 18 Line 20
 # include <attr/attributes.h>  # include <attr/attributes.h>
 #endif  #endif
   
   #ifdef HAVE_SYS_EXTATTR_H
   # include <sys/extattr.h>
   #endif
   
 #ifdef HAVE_FAM_H  #ifdef HAVE_FAM_H
 # include <fam.h>  # include <fam.h>
 #endif  #endif
Line 104  stat_cache *stat_cache_init(void) { Line 110  stat_cache *stat_cache_init(void) {
         stat_cache *sc = NULL;          stat_cache *sc = NULL;
   
         sc = calloc(1, sizeof(*sc));          sc = calloc(1, sizeof(*sc));
           force_assert(NULL != sc);
   
         sc->dir_name = buffer_init();          sc->dir_name = buffer_init();
         sc->hash_key = buffer_init();          sc->hash_key = buffer_init();
Line 123  static stat_cache_entry * stat_cache_entry_init(void)  Line 130  static stat_cache_entry * stat_cache_entry_init(void) 
         stat_cache_entry *sce = NULL;          stat_cache_entry *sce = NULL;
   
         sce = calloc(1, sizeof(*sce));          sce = calloc(1, sizeof(*sce));
           force_assert(NULL != sce);
   
         sce->name = buffer_init();          sce->name = buffer_init();
         sce->etag = buffer_init();          sce->etag = buffer_init();
Line 147  static fam_dir_entry * fam_dir_entry_init(void) { Line 155  static fam_dir_entry * fam_dir_entry_init(void) {
         fam_dir_entry *fam_dir = NULL;          fam_dir_entry *fam_dir = NULL;
   
         fam_dir = calloc(1, sizeof(*fam_dir));          fam_dir = calloc(1, sizeof(*fam_dir));
           force_assert(NULL != fam_dir);
   
         fam_dir->name = buffer_init();          fam_dir->name = buffer_init();
   
Line 210  void stat_cache_free(stat_cache *sc) { Line 219  void stat_cache_free(stat_cache *sc) {
         free(sc);          free(sc);
 }  }
   
#ifdef HAVE_XATTR#if defined(HAVE_XATTR)
static int stat_cache_attr_get(buffer *buf, char *name) {static int stat_cache_attr_get(buffer *buf, char *name, char *xattrname) {
         int attrlen;          int attrlen;
         int ret;          int ret;
   
        attrlen = 1024;        buffer_string_prepare_copy(buf, 1023);
        buffer_prepare_copy(buf, attrlen);        attrlen = buf->size - 1;
        attrlen--;        if(0 == (ret = attr_get(name, xattrname, buf->ptr, &attrlen, 0))) {
        if(0 == (ret = attr_get(name, "Content-Type", buf->ptr, &attrlen, 0))) {                buffer_commit(buf, attrlen);
         }
         return ret;
 }
 #elif defined(HAVE_EXTATTR)
 static int stat_cache_attr_get(buffer *buf, char *name, char *xattrname) {
         ssize_t attrlen;
 
         buffer_string_prepare_copy(buf, 1023);
 
         if (-1 != (attrlen = extattr_get_file(name, EXTATTR_NAMESPACE_USER, xattrname, buf->ptr, buf->size - 1))) {
                 buf->used = attrlen + 1;                  buf->used = attrlen + 1;
                 buf->ptr[attrlen] = '\0';                  buf->ptr[attrlen] = '\0';
                   return 0;
         }          }
        return ret;        return -1;
 }  }
 #endif  #endif
   
Line 234  static uint32_t hashme(buffer *str) { Line 254  static uint32_t hashme(buffer *str) {
                 hash = ((hash << 5) + hash) + *s;                  hash = ((hash << 5) + hash) + *s;
         }          }
   
        hash &= ~(1 << 31); /* strip the highest bit */        hash &= ~(((uint32_t)1) << 31); /* strip the highest bit */
   
         return hash;          return hash;
 }  }
Line 277  handler_t stat_cache_handle_fdevent(server *srv, void  Line 297  handler_t stat_cache_handle_fdevent(server *srv, void 
   
                                 for (j = 0; j < 2; j++) {                                  for (j = 0; j < 2; j++) {
                                         buffer_copy_string(sc->hash_key, fe.filename);                                          buffer_copy_string(sc->hash_key, fe.filename);
                                        buffer_append_long(sc->hash_key, j);                                        buffer_append_int(sc->hash_key, j);
   
                                         ndx = hashme(sc->hash_key);                                          ndx = hashme(sc->hash_key);
   
Line 314  handler_t stat_cache_handle_fdevent(server *srv, void  Line 334  handler_t stat_cache_handle_fdevent(server *srv, void 
 static int buffer_copy_dirname(buffer *dst, buffer *file) {  static int buffer_copy_dirname(buffer *dst, buffer *file) {
         size_t i;          size_t i;
   
        if (buffer_is_empty(file)) return -1;        if (buffer_string_is_empty(file)) return -1;
   
        for (i = file->used - 1; i+1 > 0; i--) {        for (i = buffer_string_length(file); i > 0; i--) {
                 if (file->ptr[i] == '/') {                  if (file->ptr[i] == '/') {
                         buffer_copy_string_len(dst, file->ptr, i);                          buffer_copy_string_len(dst, file->ptr, i);
                         return 0;                          return 0;
Line 354  handler_t stat_cache_get_entry(server *srv, connection Line 374  handler_t stat_cache_get_entry(server *srv, connection
 #ifdef HAVE_FAM_H  #ifdef HAVE_FAM_H
         fam_dir_entry *fam_dir = NULL;          fam_dir_entry *fam_dir = NULL;
         int dir_ndx = -1;          int dir_ndx = -1;
         splay_tree *dir_node = NULL;  
 #endif  #endif
         stat_cache_entry *sce = NULL;          stat_cache_entry *sce = NULL;
         stat_cache *sc;          stat_cache *sc;
Line 367  handler_t stat_cache_get_entry(server *srv, connection Line 386  handler_t stat_cache_get_entry(server *srv, connection
 #endif  #endif
   
         int file_ndx;          int file_ndx;
         splay_tree *file_node = NULL;  
   
         *ret_sce = NULL;          *ret_sce = NULL;
   
Line 377  handler_t stat_cache_get_entry(server *srv, connection Line 395  handler_t stat_cache_get_entry(server *srv, connection
   
         sc = srv->stat_cache;          sc = srv->stat_cache;
   
        buffer_copy_string_buffer(sc->hash_key, name);        buffer_copy_buffer(sc->hash_key, name);
        buffer_append_long(sc->hash_key, con->conf.follow_symlink);        buffer_append_int(sc->hash_key, con->conf.follow_symlink);
   
         file_ndx = hashme(sc->hash_key);          file_ndx = hashme(sc->hash_key);
         sc->files = splaytree_splay(sc->files, file_ndx);          sc->files = splaytree_splay(sc->files, file_ndx);
Line 398  handler_t stat_cache_get_entry(server *srv, connection Line 416  handler_t stat_cache_get_entry(server *srv, connection
                 /* we have seen this file already and                  /* we have seen this file already and
                  * don't stat() it again in the same second */                   * don't stat() it again in the same second */
   
                file_node = sc->files;                sce = sc->files->data;
   
                 sce = file_node->data;  
   
                 /* check if the name is the same, we might have a collision */                  /* check if the name is the same, we might have a collision */
   
                 if (buffer_is_equal(name, sce->name)) {                  if (buffer_is_equal(name, sce->name)) {
                         if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_SIMPLE) {                          if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_SIMPLE) {
                                if (sce->stat_ts == srv->cur_ts) {                                if (sce->stat_ts == srv->cur_ts && con->conf.follow_symlink) {
                                         *ret_sce = sce;                                          *ret_sce = sce;
                                         return HANDLER_GO_ON;                                          return HANDLER_GO_ON;
                                 }                                  }
                         }                          }
                 } else {                  } else {
                        /* oops, a collision,                        /* collision, forget about the entry */
                         *                        sce = NULL;
                         * file_node is used by the FAM check below to see if we know this file 
                         * and if we can save a stat(). 
                         * 
                         * BUT, the sce is not reset here as the entry into the cache is ok, we 
                         * it is just not pointing to our requested file. 
                         * 
                         *  */ 
 
                        file_node = NULL; 
                 }                  }
         } else {          } else {
 #ifdef DEBUG_STAT_CACHE  #ifdef DEBUG_STAT_CACHE
Line 443  handler_t stat_cache_get_entry(server *srv, connection Line 450  handler_t stat_cache_get_entry(server *srv, connection
                         return HANDLER_ERROR;                          return HANDLER_ERROR;
                 }                  }
   
                buffer_copy_string_buffer(sc->hash_key, sc->dir_name);                buffer_copy_buffer(sc->hash_key, sc->dir_name);
                buffer_append_long(sc->hash_key, con->conf.follow_symlink);                buffer_append_int(sc->hash_key, con->conf.follow_symlink);
   
                 dir_ndx = hashme(sc->hash_key);                  dir_ndx = hashme(sc->hash_key);
   
                 sc->dirs = splaytree_splay(sc->dirs, dir_ndx);                  sc->dirs = splaytree_splay(sc->dirs, dir_ndx);
   
                if (sc->dirs && (sc->dirs->key == dir_ndx)) {                if ((NULL != sc->dirs) && (sc->dirs->key == dir_ndx)) {
                        dir_node = sc->dirs;                        fam_dir = sc->dirs->data;
                } 
   
                if (dir_node && file_node) {                        /* check whether we got a collision */
                        /* we found a file */                        if (buffer_is_equal(sc->dir_name, fam_dir->name)) {
                                 /* test whether a found file cache entry is still ok */
                                 if ((NULL != sce) && (fam_dir->version == sce->dir_version)) {
                                         /* the stat()-cache entry is still ok */
   
                        sce = file_node->data;                                        *ret_sce = sce;
                        fam_dir = dir_node->data;                                        return HANDLER_GO_ON;
                                }
                        if (fam_dir->version == sce->dir_version) {                        } else {
                                /* the stat()-cache entry is still ok */                                /* hash collision, forget about the entry */
                                fam_dir = NULL;
                                *ret_sce = sce; 
                                return HANDLER_GO_ON; 
                         }                          }
                 }                  }
         }          }
Line 483  handler_t stat_cache_get_entry(server *srv, connection Line 490  handler_t stat_cache_get_entry(server *srv, connection
   
         if (S_ISREG(st.st_mode)) {          if (S_ISREG(st.st_mode)) {
                 /* fix broken stat/open for symlinks to reg files with appended slash on freebsd,osx */                  /* fix broken stat/open for symlinks to reg files with appended slash on freebsd,osx */
                if (name->ptr[name->used-2] == '/') {                if (name->ptr[buffer_string_length(name) - 1] == '/') {
                         errno = ENOTDIR;                          errno = ENOTDIR;
                         return HANDLER_ERROR;                          return HANDLER_ERROR;
                 }                  }
Line 496  handler_t stat_cache_get_entry(server *srv, connection Line 503  handler_t stat_cache_get_entry(server *srv, connection
         }          }
   
         if (NULL == sce) {          if (NULL == sce) {
 #ifdef DEBUG_STAT_CACHE  
                 int osize = splaytree_size(sc->files);  
 #endif  
   
                 sce = stat_cache_entry_init();                  sce = stat_cache_entry_init();
                buffer_copy_string_buffer(sce->name, name);                buffer_copy_buffer(sce->name, name);
   
                sc->files = splaytree_insert(sc->files, file_ndx, sce);                /* already splayed file_ndx */
#ifdef DEBUG_STAT_CACHE                if ((NULL != sc->files) && (sc->files->key == file_ndx)) {
                if (ctrl.size == 0) {                        /* hash collision: replace old entry */
                        ctrl.size = 16;                        stat_cache_entry_free(sc->files->data);
                        ctrl.used = 0;                        sc->files->data = sce;
                        ctrl.ptr = malloc(ctrl.size * sizeof(*ctrl.ptr));                } else {
                } else if (ctrl.size == ctrl.used) {                        int osize = splaytree_size(sc->files);
                        ctrl.size += 16; 
                        ctrl.ptr = realloc(ctrl.ptr, ctrl.size * sizeof(*ctrl.ptr)); 
                } 
   
                ctrl.ptr[ctrl.used++] = file_ndx;                        sc->files = splaytree_insert(sc->files, file_ndx, sce);
                         force_assert(osize + 1 == splaytree_size(sc->files));
   
   #ifdef DEBUG_STAT_CACHE
                           if (ctrl.size == 0) {
                                   ctrl.size = 16;
                                   ctrl.used = 0;
                                   ctrl.ptr = malloc(ctrl.size * sizeof(*ctrl.ptr));
                                   force_assert(NULL != ctrl.ptr);
                           } else if (ctrl.size == ctrl.used) {
                                   ctrl.size += 16;
                                   ctrl.ptr = realloc(ctrl.ptr, ctrl.size * sizeof(*ctrl.ptr));
                                   force_assert(NULL != ctrl.ptr);
                           }
   
                           ctrl.ptr[ctrl.used++] = file_ndx;
   #endif
                   }
                 force_assert(sc->files);                  force_assert(sc->files);
                 force_assert(sc->files->data == sce);                  force_assert(sc->files->data == sce);
                 force_assert(osize + 1 == splaytree_size(sc->files));  
 #endif  
         }          }
   
         sce->st = st;          sce->st = st;
Line 555  handler_t stat_cache_get_entry(server *srv, connection Line 570  handler_t stat_cache_get_entry(server *srv, connection
                  * we assume "/" can not be symlink, so                   * we assume "/" can not be symlink, so
                  * skip the symlink stuff if our path is /                   * skip the symlink stuff if our path is /
                  **/                   **/
                else if ((name->used > 2)) {                else if (buffer_string_length(name) > 1) {
                         buffer *dname;                          buffer *dname;
                         char *s_cur;                          char *s_cur;
   
                         dname = buffer_init();                          dname = buffer_init();
                        buffer_copy_string_buffer(dname, name);                        buffer_copy_buffer(dname, name);
   
                        while ((s_cur = strrchr(dname->ptr,'/'))) {                        while ((s_cur = strrchr(dname->ptr, '/'))) {
                                *s_cur = '\0';                                buffer_string_set_length(dname, s_cur - dname->ptr);
                                dname->used = s_cur - dname->ptr + 1; 
                                 if (dname->ptr == s_cur) {                                  if (dname->ptr == s_cur) {
 #ifdef DEBUG_STAT_CACHE  #ifdef DEBUG_STAT_CACHE
                                         log_error_write(srv, __FILE__, __LINE__, "s", "reached /");                                          log_error_write(srv, __FILE__, __LINE__, "s", "reached /");
Line 592  handler_t stat_cache_get_entry(server *srv, connection Line 606  handler_t stat_cache_get_entry(server *srv, connection
         if (S_ISREG(st.st_mode)) {          if (S_ISREG(st.st_mode)) {
                 /* determine mimetype */                  /* determine mimetype */
                 buffer_reset(sce->content_type);                  buffer_reset(sce->content_type);
#ifdef HAVE_XATTR#if defined(HAVE_XATTR) || defined(HAVE_EXTATTR)
                 if (con->conf.use_xattr) {                  if (con->conf.use_xattr) {
                        stat_cache_attr_get(sce->content_type, name->ptr);                        stat_cache_attr_get(sce->content_type, name->ptr, srv->srvconf.xattr_name->ptr);
                 }                  }
 #endif  #endif
                 /* xattr did not set a content-type. ask the config */                  /* xattr did not set a content-type. ask the config */
                if (buffer_is_empty(sce->content_type)) {                if (buffer_string_is_empty(sce->content_type)) {
                         size_t namelen = buffer_string_length(name);
 
                         for (k = 0; k < con->conf.mimetypes->used; k++) {                          for (k = 0; k < con->conf.mimetypes->used; k++) {
                                 data_string *ds = (data_string *)con->conf.mimetypes->data[k];                                  data_string *ds = (data_string *)con->conf.mimetypes->data[k];
                                 buffer *type = ds->key;                                  buffer *type = ds->key;
                                   size_t typelen = buffer_string_length(type);
   
                                if (type->used == 0) continue;                                if (buffer_is_empty(type)) continue;
   
                                 /* check if the right side is the same */                                  /* check if the right side is the same */
                                if (type->used > name->used) continue;                                if (typelen > namelen) continue;
   
                                if (0 == strncasecmp(name->ptr + name->used - type->used, type->ptr, type->used - 1)) {                                if (0 == strncasecmp(name->ptr + namelen - typelen, type->ptr, typelen)) {
                                        buffer_copy_string_buffer(sce->content_type, ds->value);                                        buffer_copy_buffer(sce->content_type, ds->value);
                                         break;                                          break;
                                 }                                  }
                         }                          }
Line 622  handler_t stat_cache_get_entry(server *srv, connection Line 639  handler_t stat_cache_get_entry(server *srv, connection
 #ifdef HAVE_FAM_H  #ifdef HAVE_FAM_H
         if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) {          if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) {
                 /* is this directory already registered ? */                  /* is this directory already registered ? */
                if (!dir_node) {                if (NULL == fam_dir) {
                         fam_dir = fam_dir_entry_init();                          fam_dir = fam_dir_entry_init();
   
                        buffer_copy_string_buffer(fam_dir->name, sc->dir_name);                        buffer_copy_buffer(fam_dir->name, sc->dir_name);
   
                         fam_dir->version = 1;                          fam_dir->version = 1;
   
                         fam_dir->req = calloc(1, sizeof(FAMRequest));                          fam_dir->req = calloc(1, sizeof(FAMRequest));
                           force_assert(NULL != fam_dir);
   
                         if (0 != FAMMonitorDirectory(&sc->fam, fam_dir->name->ptr,                          if (0 != FAMMonitorDirectory(&sc->fam, fam_dir->name->ptr,
                                                      fam_dir->req, fam_dir)) {                                                       fam_dir->req, fam_dir)) {
Line 643  handler_t stat_cache_get_entry(server *srv, connection Line 661  handler_t stat_cache_get_entry(server *srv, connection
                                 fam_dir_entry_free(&sc->fam, fam_dir);                                  fam_dir_entry_free(&sc->fam, fam_dir);
                                 fam_dir = NULL;                                  fam_dir = NULL;
                         } else {                          } else {
                                int osize = 0;                                int osize = splaytree_size(sc->dirs);
   
                                if (sc->dirs) {                                /* already splayed dir_ndx */
                                        osize = sc->dirs->size;                                if ((NULL != sc->dirs) && (sc->dirs->key == dir_ndx)) {
                                         /* hash collision: replace old entry */
                                         fam_dir_entry_free(&sc->fam, sc->dirs->data);
                                         sc->dirs->data = fam_dir;
                                 } else {
                                         sc->dirs = splaytree_insert(sc->dirs, dir_ndx, fam_dir);
                                         force_assert(osize == (splaytree_size(sc->dirs) - 1));
                                 }                                  }
   
                                 sc->dirs = splaytree_insert(sc->dirs, dir_ndx, fam_dir);  
                                 force_assert(sc->dirs);                                  force_assert(sc->dirs);
                                 force_assert(sc->dirs->data == fam_dir);                                  force_assert(sc->dirs->data == fam_dir);
                                 force_assert(osize == (sc->dirs->size - 1));  
                         }                          }
                 } else {  
                         fam_dir = dir_node->data;  
                 }                  }
   
                 /* bind the fam_fc to the stat() cache entry */                  /* bind the fam_fc to the stat() cache entry */
Line 671  handler_t stat_cache_get_entry(server *srv, connection Line 691  handler_t stat_cache_get_entry(server *srv, connection
         return HANDLER_GO_ON;          return HANDLER_GO_ON;
 }  }
   
   int stat_cache_open_rdonly_fstat (server *srv, connection *con, buffer *name, struct stat *st) {
           /*(Note: O_NOFOLLOW affects only the final path segment, the target file,
            * not any intermediate symlinks along the path)*/
           #ifndef O_BINARY
           #define O_BINARY 0
           #endif
           #ifndef O_LARGEFILE
           #define O_LARGEFILE 0
           #endif
           #ifndef O_NOCTTY
           #define O_NOCTTY 0
           #endif
           #ifndef O_NONBLOCK
           #define O_NONBLOCK 0
           #endif
           #ifndef O_NOFOLLOW
           #define O_NOFOLLOW 0
           #endif
           const int oflags = O_BINARY | O_LARGEFILE | O_NOCTTY | O_NONBLOCK
                            | (con->conf.follow_symlink ? 0 : O_NOFOLLOW);
           const int fd = open(name->ptr, O_RDONLY | oflags);
           if (fd >= 0) {
                   if (0 == fstat(fd, st)) {
                           return fd;
                   } else {
                           close(fd);
                   }
           }
           UNUSED(srv); /*(might log_error_write(srv, ...) in the future)*/
           return -1;
   }
   
 /**  /**
  * remove stat() from cache which havn't been stat()ed for   * remove stat() from cache which havn't been stat()ed for
  * more than 10 seconds   * more than 10 seconds
Line 707  int stat_cache_trigger_cleanup(server *srv) { Line 759  int stat_cache_trigger_cleanup(server *srv) {
         if (!sc->files) return 0;          if (!sc->files) return 0;
   
         keys = calloc(1, sizeof(int) * sc->files->size);          keys = calloc(1, sizeof(int) * sc->files->size);
           force_assert(NULL != keys);
   
         stat_cache_tag_old_entries(srv, sc->files, keys, &max_ndx);          stat_cache_tag_old_entries(srv, sc->files, keys, &max_ndx);
   

Removed from v.1.1.1.2  
changed lines
  Added in v.1.1.1.3


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