--- embedaddon/lighttpd/src/stat_cache.c 2013/10/14 10:32:48 1.1.1.1 +++ 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 @@ -69,7 +75,6 @@ #ifdef HAVE_FAM_H typedef struct { FAMRequest *req; - FAMConnection *fc; buffer *name; @@ -102,27 +107,30 @@ static fake_keys ctrl; #endif stat_cache *stat_cache_init(void) { - stat_cache *fc = NULL; + stat_cache *sc = NULL; - fc = calloc(1, sizeof(*fc)); + sc = calloc(1, sizeof(*sc)); + force_assert(NULL != sc); - fc->dir_name = buffer_init(); - fc->hash_key = buffer_init(); + sc->dir_name = buffer_init(); + sc->hash_key = buffer_init(); + #ifdef HAVE_FAM_H - fc->fam = calloc(1, sizeof(*fc->fam)); + sc->fam_fcce_ndx = -1; #endif #ifdef DEBUG_STAT_CACHE ctrl.size = 0; #endif - return fc; + return sc; } 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,18 +155,19 @@ 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(); return fam_dir; } -static void fam_dir_entry_free(void *data) { +static void fam_dir_entry_free(FAMConnection *fc, void *data) { fam_dir_entry *fam_dir = data; if (!fam_dir) return; - FAMCancelMonitor(fam_dir->fc, fam_dir->req); + FAMCancelMonitor(fc, fam_dir->req); buffer_free(fam_dir->name); free(fam_dir->req); @@ -177,7 +186,7 @@ void stat_cache_free(stat_cache *sc) { stat_cache_entry_free(node->data); sc->files = splaytree_delete(sc->files, node->key); - assert(osize - 1 == splaytree_size(sc->files)); + force_assert(osize - 1 == splaytree_size(sc->files)); } buffer_free(sc->dir_name); @@ -190,37 +199,50 @@ void stat_cache_free(stat_cache *sc) { osize = sc->dirs->size; - fam_dir_entry_free(node->data); + fam_dir_entry_free(&sc->fam, node->data); sc->dirs = splaytree_delete(sc->dirs, node->key); if (osize == 1) { - assert(NULL == sc->dirs); + force_assert(NULL == sc->dirs); } else { - assert(osize == (sc->dirs->size + 1)); + force_assert(osize == (sc->dirs->size + 1)); } } - if (sc->fam) { - FAMClose(sc->fam); - free(sc->fam); + if (-1 != sc->fam_fcce_ndx) { + /* fd events already gone */ + sc->fam_fcce_ndx = -1; + + FAMClose(&sc->fam); } #endif 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 @@ -232,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; } @@ -246,18 +268,16 @@ handler_t stat_cache_handle_fdevent(server *srv, void UNUSED(_fce); /* */ - if ((revent & FDEVENT_IN) && - sc->fam) { + if (revent & FDEVENT_IN) { + events = FAMPending(&sc->fam); - events = FAMPending(sc->fam); - for (i = 0; i < events; i++) { FAMEvent fe; fam_dir_entry *fam_dir; splay_tree *node; int ndx, j; - FAMNextEvent(sc->fam, &fe); + FAMNextEvent(&sc->fam, &fe); /* handle event */ @@ -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); @@ -287,10 +307,10 @@ handler_t stat_cache_handle_fdevent(server *srv, void if (node && (node->key == ndx)) { int osize = splaytree_size(sc->dirs); - fam_dir_entry_free(node->data); + fam_dir_entry_free(&sc->fam, node->data); sc->dirs = splaytree_delete(sc->dirs, ndx); - assert(osize - 1 == splaytree_size(sc->dirs)); + force_assert(osize - 1 == splaytree_size(sc->dirs)); } } break; @@ -302,15 +322,10 @@ handler_t stat_cache_handle_fdevent(server *srv, void if (revent & FDEVENT_HUP) { /* fam closed the connection */ - srv->stat_cache->fam_fcce_ndx = -1; + fdevent_event_del(srv->ev, &(sc->fam_fcce_ndx), FAMCONNECTION_GETFD(&sc->fam)); + fdevent_unregister(srv->ev, FAMCONNECTION_GETFD(&sc->fam)); - fdevent_event_del(srv->ev, &(sc->fam_fcce_ndx), FAMCONNECTION_GETFD(sc->fam)); - fdevent_unregister(srv->ev, FAMCONNECTION_GETFD(sc->fam)); - - FAMClose(sc->fam); - free(sc->fam); - - sc->fam = NULL; + FAMClose(&sc->fam); } return HANDLER_GO_ON; @@ -319,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; @@ -359,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; @@ -372,7 +386,6 @@ handler_t stat_cache_get_entry(server *srv, connection #endif int file_ndx; - splay_tree *file_node = NULL; *ret_sce = NULL; @@ -382,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); @@ -397,37 +410,26 @@ handler_t stat_cache_get_entry(server *srv, connection if (sc->files && (sc->files->key == file_ndx)) { #ifdef DEBUG_STAT_CACHE /* it was in the cache */ - assert(i < ctrl.used); + force_assert(i < ctrl.used); #endif /* 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 @@ -435,7 +437,7 @@ handler_t stat_cache_get_entry(server *srv, connection log_error_write(srv, __FILE__, __LINE__, "xSB", file_ndx, "was already inserted but not found in cache, ", name); } - assert(i == ctrl.used); + force_assert(i == ctrl.used); #endif } @@ -448,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; } } } @@ -488,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; } @@ -501,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)); - assert(sc->files); - assert(sc->files->data == sce); - 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); } sce->st = st; @@ -560,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 /"); @@ -597,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; } } @@ -625,20 +637,19 @@ handler_t stat_cache_get_entry(server *srv, connection } #ifdef HAVE_FAM_H - if (sc->fam && - (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM)) { + 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(); - fam_dir->fc = sc->fam; - 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, + if (0 != FAMMonitorDirectory(&sc->fam, fam_dir->name->ptr, fam_dir->req, fam_dir)) { log_error_write(srv, __FILE__, __LINE__, "sbsbs", @@ -647,28 +658,30 @@ handler_t stat_cache_get_entry(server *srv, connection "file:", name, FamErrlist[FAMErrno]); - fam_dir_entry_free(fam_dir); + 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); - assert(sc->dirs); - assert(sc->dirs->data == fam_dir); - assert(osize == (sc->dirs->size - 1)); + force_assert(sc->dirs); + force_assert(sc->dirs->data == fam_dir); } - } else { - fam_dir = dir_node->data; } /* bind the fam_fc to the stat() cache entry */ if (fam_dir) { sce->dir_version = fam_dir->version; - sce->dir_ndx = dir_ndx; } } #endif @@ -678,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 @@ -713,7 +758,8 @@ int stat_cache_trigger_cleanup(server *srv) { if (!sc->files) return 0; - keys = calloc(1, sizeof(size_t) * 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); @@ -742,7 +788,7 @@ int stat_cache_trigger_cleanup(server *srv) { } } - assert(osize - 1 == splaytree_size(sc->files)); + force_assert(osize - 1 == splaytree_size(sc->files)); #endif } }