|
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); |
| |
|