--- embedaddon/lighttpd/src/stat_cache.c 2014/06/15 20:20:06 1.1.1.2 +++ embedaddon/lighttpd/src/stat_cache.c 2016/11/02 10:35:00 1.1.1.3 @@ -1,3 +1,5 @@ +#include "first.h" + #include "log.h" #include "stat_cache.h" #include "fdevent.h" @@ -18,6 +20,10 @@ # include #endif +#ifdef HAVE_SYS_EXTATTR_H +# include +#endif + #ifdef HAVE_FAM_H # include #endif @@ -104,6 +110,7 @@ stat_cache *stat_cache_init(void) { stat_cache *sc = NULL; sc = calloc(1, sizeof(*sc)); + force_assert(NULL != sc); sc->dir_name = buffer_init(); sc->hash_key = buffer_init(); @@ -123,6 +130,7 @@ static stat_cache_entry * stat_cache_entry_init(void) stat_cache_entry *sce = NULL; sce = calloc(1, sizeof(*sce)); + force_assert(NULL != sce); sce->name = buffer_init(); sce->etag = buffer_init(); @@ -147,6 +155,7 @@ static fam_dir_entry * fam_dir_entry_init(void) { fam_dir_entry *fam_dir = NULL; fam_dir = calloc(1, sizeof(*fam_dir)); + force_assert(NULL != fam_dir); fam_dir->name = buffer_init(); @@ -210,19 +219,30 @@ void stat_cache_free(stat_cache *sc) { free(sc); } -#ifdef HAVE_XATTR -static int stat_cache_attr_get(buffer *buf, char *name) { +#if defined(HAVE_XATTR) +static int stat_cache_attr_get(buffer *buf, char *name, char *xattrname) { int attrlen; int ret; - attrlen = 1024; - buffer_prepare_copy(buf, attrlen); - attrlen--; - if(0 == (ret = attr_get(name, "Content-Type", buf->ptr, &attrlen, 0))) { + buffer_string_prepare_copy(buf, 1023); + attrlen = buf->size - 1; + if(0 == (ret = attr_get(name, xattrname, 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->ptr[attrlen] = '\0'; + return 0; } - return ret; + return -1; } #endif @@ -234,7 +254,7 @@ static uint32_t hashme(buffer *str) { hash = ((hash << 5) + hash) + *s; } - hash &= ~(1 << 31); /* strip the highest bit */ + hash &= ~(((uint32_t)1) << 31); /* strip the highest bit */ return hash; } @@ -277,7 +297,7 @@ handler_t stat_cache_handle_fdevent(server *srv, void for (j = 0; j < 2; j++) { 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); @@ -314,9 +334,9 @@ handler_t stat_cache_handle_fdevent(server *srv, void static int buffer_copy_dirname(buffer *dst, buffer *file) { 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] == '/') { buffer_copy_string_len(dst, file->ptr, i); return 0; @@ -354,7 +374,6 @@ handler_t stat_cache_get_entry(server *srv, connection #ifdef HAVE_FAM_H fam_dir_entry *fam_dir = NULL; int dir_ndx = -1; - splay_tree *dir_node = NULL; #endif stat_cache_entry *sce = NULL; stat_cache *sc; @@ -367,7 +386,6 @@ handler_t stat_cache_get_entry(server *srv, connection #endif int file_ndx; - splay_tree *file_node = NULL; *ret_sce = NULL; @@ -377,8 +395,8 @@ handler_t stat_cache_get_entry(server *srv, connection sc = srv->stat_cache; - buffer_copy_string_buffer(sc->hash_key, name); - buffer_append_long(sc->hash_key, con->conf.follow_symlink); + buffer_copy_buffer(sc->hash_key, name); + buffer_append_int(sc->hash_key, con->conf.follow_symlink); file_ndx = hashme(sc->hash_key); sc->files = splaytree_splay(sc->files, file_ndx); @@ -398,31 +416,20 @@ handler_t stat_cache_get_entry(server *srv, connection /* we have seen this file already and * 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 */ if (buffer_is_equal(name, sce->name)) { 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; return HANDLER_GO_ON; } } } else { - /* oops, a collision, - * - * 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; + /* collision, forget about the entry */ + sce = NULL; } } else { #ifdef DEBUG_STAT_CACHE @@ -443,28 +450,28 @@ handler_t stat_cache_get_entry(server *srv, connection return HANDLER_ERROR; } - buffer_copy_string_buffer(sc->hash_key, sc->dir_name); - buffer_append_long(sc->hash_key, con->conf.follow_symlink); + buffer_copy_buffer(sc->hash_key, sc->dir_name); + buffer_append_int(sc->hash_key, con->conf.follow_symlink); dir_ndx = hashme(sc->hash_key); sc->dirs = splaytree_splay(sc->dirs, dir_ndx); - if (sc->dirs && (sc->dirs->key == dir_ndx)) { - dir_node = sc->dirs; - } + if ((NULL != sc->dirs) && (sc->dirs->key == dir_ndx)) { + fam_dir = sc->dirs->data; - if (dir_node && file_node) { - /* we found a file */ + /* check whether we got a collision */ + 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; - fam_dir = dir_node->data; - - if (fam_dir->version == sce->dir_version) { - /* the stat()-cache entry is still ok */ - - *ret_sce = sce; - return HANDLER_GO_ON; + *ret_sce = sce; + return HANDLER_GO_ON; + } + } else { + /* hash collision, forget about the entry */ + fam_dir = NULL; } } } @@ -483,7 +490,7 @@ handler_t stat_cache_get_entry(server *srv, connection if (S_ISREG(st.st_mode)) { /* 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; return HANDLER_ERROR; } @@ -496,30 +503,38 @@ handler_t stat_cache_get_entry(server *srv, connection } if (NULL == sce) { -#ifdef DEBUG_STAT_CACHE - int osize = splaytree_size(sc->files); -#endif 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); -#ifdef DEBUG_STAT_CACHE - if (ctrl.size == 0) { - ctrl.size = 16; - ctrl.used = 0; - ctrl.ptr = malloc(ctrl.size * sizeof(*ctrl.ptr)); - } else if (ctrl.size == ctrl.used) { - ctrl.size += 16; - ctrl.ptr = realloc(ctrl.ptr, ctrl.size * sizeof(*ctrl.ptr)); - } + /* already splayed file_ndx */ + if ((NULL != sc->files) && (sc->files->key == file_ndx)) { + /* hash collision: replace old entry */ + stat_cache_entry_free(sc->files->data); + sc->files->data = sce; + } else { + int osize = splaytree_size(sc->files); - 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->data == sce); - force_assert(osize + 1 == splaytree_size(sc->files)); -#endif } sce->st = st; @@ -555,16 +570,15 @@ handler_t stat_cache_get_entry(server *srv, connection * we assume "/" can not be symlink, so * skip the symlink stuff if our path is / **/ - else if ((name->used > 2)) { + else if (buffer_string_length(name) > 1) { buffer *dname; char *s_cur; dname = buffer_init(); - buffer_copy_string_buffer(dname, name); + buffer_copy_buffer(dname, name); - while ((s_cur = strrchr(dname->ptr,'/'))) { - *s_cur = '\0'; - dname->used = s_cur - dname->ptr + 1; + while ((s_cur = strrchr(dname->ptr, '/'))) { + buffer_string_set_length(dname, s_cur - dname->ptr); if (dname->ptr == s_cur) { #ifdef DEBUG_STAT_CACHE log_error_write(srv, __FILE__, __LINE__, "s", "reached /"); @@ -592,24 +606,27 @@ handler_t stat_cache_get_entry(server *srv, connection if (S_ISREG(st.st_mode)) { /* determine mimetype */ buffer_reset(sce->content_type); -#ifdef HAVE_XATTR +#if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) 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 /* 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++) { data_string *ds = (data_string *)con->conf.mimetypes->data[k]; 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 */ - if (type->used > name->used) continue; + if (typelen > namelen) continue; - if (0 == strncasecmp(name->ptr + name->used - type->used, type->ptr, type->used - 1)) { - buffer_copy_string_buffer(sce->content_type, ds->value); + if (0 == strncasecmp(name->ptr + namelen - typelen, type->ptr, typelen)) { + buffer_copy_buffer(sce->content_type, ds->value); break; } } @@ -622,14 +639,15 @@ handler_t stat_cache_get_entry(server *srv, connection #ifdef HAVE_FAM_H if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { /* is this directory already registered ? */ - if (!dir_node) { + if (NULL == fam_dir) { 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->req = calloc(1, sizeof(FAMRequest)); + force_assert(NULL != fam_dir); if (0 != FAMMonitorDirectory(&sc->fam, fam_dir->name->ptr, fam_dir->req, fam_dir)) { @@ -643,19 +661,21 @@ handler_t stat_cache_get_entry(server *srv, connection fam_dir_entry_free(&sc->fam, fam_dir); fam_dir = NULL; } else { - int osize = 0; + int osize = splaytree_size(sc->dirs); - if (sc->dirs) { - osize = sc->dirs->size; + /* already splayed dir_ndx */ + 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->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 */ @@ -671,6 +691,38 @@ handler_t stat_cache_get_entry(server *srv, connection 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 * more than 10 seconds @@ -707,6 +759,7 @@ int stat_cache_trigger_cleanup(server *srv) { if (!sc->files) return 0; keys = calloc(1, sizeof(int) * sc->files->size); + force_assert(NULL != keys); stat_cache_tag_old_entries(srv, sc->files, keys, &max_ndx);