diff --git a/src/base.h b/src/base.h index 238d90b3..ead4298d 100644 --- a/src/base.h +++ b/src/base.h @@ -255,8 +255,6 @@ typedef struct { typedef struct { unsigned int max_request_field_size; - int stat_cache_engine; - const char *xattr_name; unsigned int log_state_handling; unsigned char log_request_header_on_error; @@ -321,7 +319,6 @@ typedef struct { struct server { void *plugin_slots; - struct stat_cache *stat_cache; struct fdevents *ev; int (* network_backend_write)(int fd, chunkqueue *cq, off_t max_bytes, log_error_st *errh); diff --git a/src/configfile.c b/src/configfile.c index dc4345d5..b69ef5b2 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -588,9 +588,6 @@ static int config_insert_srvconf(server *srv) { T_CONFIG_SCOPE_UNSET } }; - if (0 != stat_cache_choose_engine(srv, NULL)) /*(set initial default)*/ - return HANDLER_ERROR; - int rc = 0; plugin_data_base srvplug; memset(&srvplug, 0, sizeof(srvplug)); @@ -709,7 +706,7 @@ static int config_insert_srvconf(server *srv) { rc = HANDLER_ERROR; break; case 29:/* mimetype.xattr-name */ - srv->srvconf.xattr_name = cpv->v.b->ptr; + stat_cache_xattrname(cpv->v.b->ptr); break; case 30:/* ssl.engine */ ssl_enabled = (0 != cpv->v.u); @@ -1065,7 +1062,6 @@ void config_init(server *srv) { srv->srvconf.high_precision_timestamps = 0; srv->srvconf.max_request_field_size = 8192; - srv->srvconf.xattr_name = "Content-Type"; srv->srvconf.http_header_strict = 1; srv->srvconf.http_host_strict = 1; /*(implies http_host_normalize)*/ srv->srvconf.http_host_normalize = 0; diff --git a/src/connections.c b/src/connections.c index f16f77e5..530000d1 100644 --- a/src/connections.c +++ b/src/connections.c @@ -284,14 +284,14 @@ static void connection_handle_errdoc(connection *con) { buffer_append_int(con->physical.path, con->http_status); buffer_append_string_len(con->physical.path, CONST_STR_LEN(".html")); if (0 == http_chunk_append_file(con, con->physical.path)) { - stat_cache_entry *sce = NULL; - if (stat_cache_get_entry(con, con->physical.path, &sce) - != HANDLER_ERROR) { - stat_cache_content_type_get(con, con->physical.path, sce); + stat_cache_entry *sce = stat_cache_get_entry(con->physical.path); + const buffer *content_type = (NULL != sce) + ? stat_cache_content_type_get(con, sce) + : NULL; + if (content_type) http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), - CONST_BUF_LEN(sce->content_type)); - } + CONST_BUF_LEN(content_type)); return; } } diff --git a/src/etag.c b/src/etag.c index 0881f48d..69f08485 100644 --- a/src/etag.c +++ b/src/etag.c @@ -172,7 +172,7 @@ int etag_create(buffer *etag, const struct stat *st, int flags) { return 0; } -int etag_mutate(buffer *mut, buffer *etag) { +int etag_mutate(buffer *mut, const buffer *etag) { size_t i, len; uint32_t h; diff --git a/src/etag.h b/src/etag.h index 35dde1c5..7c1dd350 100644 --- a/src/etag.h +++ b/src/etag.h @@ -10,7 +10,7 @@ typedef enum { ETAG_USE_INODE = 1, ETAG_USE_MTIME = 2, ETAG_USE_SIZE = 4 } etag_ int etag_is_equal(const buffer *etag, const char *matches, int weak_ok); int etag_create(buffer *etag, const struct stat *st, int flags); -int etag_mutate(buffer *mut, buffer *etag); +int etag_mutate(buffer *mut, const buffer *etag); #endif diff --git a/src/http-header-glue.c b/src/http-header-glue.c index 184d9d78..606648f0 100644 --- a/src/http-header-glue.c +++ b/src/http-header-glue.c @@ -430,12 +430,12 @@ static int http_response_parse_range(connection *con, buffer *path, stat_cache_e void http_response_send_file (connection *con, buffer *path) { - stat_cache_entry *sce = NULL; + stat_cache_entry * const sce = stat_cache_get_entry(path); const buffer *mtime = NULL; const buffer *vb; int allow_caching = (0 == con->http_status || 200 == con->http_status); - if (HANDLER_ERROR == stat_cache_get_entry(con, path, &sce)) { + if (NULL == sce) { con->http_status = (errno == ENOENT) ? 404 : 403; log_error(con->conf.errh, __FILE__, __LINE__, "not a regular file: %s -> %s", con->uri.path->ptr, path->ptr); @@ -443,7 +443,7 @@ void http_response_send_file (connection *con, buffer *path) { } if (!con->conf.follow_symlink - && 0 != stat_cache_path_contains_symlink(con, path)) { + && 0 != stat_cache_path_contains_symlink(path, con->conf.errh)) { con->http_status = 403; if (con->conf.log_request_handling) { log_error(con->conf.errh, __FILE__, __LINE__, @@ -460,7 +460,7 @@ void http_response_send_file (connection *con, buffer *path) { if (con->conf.log_file_not_found) { log_error(con->conf.errh, __FILE__, __LINE__, "not a regular file: %s -> %s", - con->uri.path->ptr, sce->name->ptr); + con->uri.path->ptr, path->ptr); } return; } @@ -484,8 +484,8 @@ void http_response_send_file (connection *con, buffer *path) { /* set response content-type, if not set already */ if (NULL == http_header_response_get(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"))) { - stat_cache_content_type_get(con, path, sce); - if (buffer_string_is_empty(sce->content_type)) { + const buffer *content_type = stat_cache_content_type_get(con, sce); + if (buffer_string_is_empty(content_type)) { /* we are setting application/octet-stream, but also announce that * this header field might change in the seconds few requests * @@ -496,7 +496,7 @@ void http_response_send_file (connection *con, buffer *path) { allow_caching = 0; } else { - http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(content_type)); } } @@ -505,10 +505,13 @@ void http_response_send_file (connection *con, buffer *path) { } if (allow_caching) { - if (con->conf.etag_flags != 0 && !buffer_string_is_empty(stat_cache_etag_get(sce, con->conf.etag_flags))) { + const buffer *etag = (0 != con->conf.etag_flags) + ? stat_cache_etag_get(sce, con->conf.etag_flags) + : NULL; + if (!buffer_string_is_empty(etag)) { if (NULL == http_header_response_get(con, HTTP_HEADER_ETAG, CONST_STR_LEN("ETag"))) { /* generate e-tag */ - etag_mutate(con->physical.etag, sce->etag); + etag_mutate(con->physical.etag, etag); http_header_response_set(con, HTTP_HEADER_ETAG, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); } @@ -722,7 +725,8 @@ static void http_response_xsendfile2(connection *con, const buffer *value, const } } - if (HANDLER_ERROR == stat_cache_get_entry(con, b, &sce)) { + sce = stat_cache_get_entry(b); + if (NULL == sce) { log_error(con->conf.errh, __FILE__, __LINE__, "send-file error: couldn't get stat_cache entry for " "X-Sendfile2: %s", b->ptr); diff --git a/src/http_chunk.c b/src/http_chunk.c index aa102063..ca2d2b83 100644 --- a/src/http_chunk.c +++ b/src/http_chunk.c @@ -56,7 +56,8 @@ static int http_chunk_len_append_tempfile(chunkqueue * const cq, uintmax_t len, static int http_chunk_append_file_open_fstat(connection * const con, const buffer * const fn, struct stat * const st) { return - (con->conf.follow_symlink || !stat_cache_path_contains_symlink(con, fn)) + (con->conf.follow_symlink + || !stat_cache_path_contains_symlink(fn, con->conf.errh)) ? stat_cache_open_rdonly_fstat(fn, st, con->conf.follow_symlink) : -1; } diff --git a/src/mod_cgi.c b/src/mod_cgi.c index e5c5a093..0d7f98dd 100644 --- a/src/mod_cgi.c +++ b/src/mod_cgi.c @@ -751,12 +751,10 @@ static int cgi_write_request(handler_ctx *hctx, int fd) { return 0; } -static struct stat * cgi_stat(connection *con, buffer *path) { +static const struct stat * cgi_stat(buffer *path) { /* CGI might be executable even if it is not readable */ - stat_cache_entry *sce; - return (HANDLER_ERROR != stat_cache_get_entry(con, path, &sce)) - ? &sce->st - : NULL; + const stat_cache_entry * const sce = stat_cache_get_entry(path); + return sce ? &sce->st : NULL; } static int cgi_create_env(connection *con, plugin_data *p, handler_ctx *hctx, buffer *cgi_handler) { @@ -767,7 +765,7 @@ static int cgi_create_env(connection *con, plugin_data *p, handler_ctx *hctx, bu UNUSED(p); if (!buffer_string_is_empty(cgi_handler)) { - if (NULL == cgi_stat(con, cgi_handler)) { + if (NULL == cgi_stat(cgi_handler)) { log_perror(con->conf.errh, __FILE__, __LINE__, "stat for cgi-handler %s", cgi_handler->ptr); return -1; @@ -894,7 +892,7 @@ static int cgi_create_env(connection *con, plugin_data *p, handler_ctx *hctx, bu URIHANDLER_FUNC(cgi_is_handled) { plugin_data *p = p_d; - struct stat *st; + const struct stat *st; data_string *ds; if (con->mode != DIRECT) return HANDLER_GO_ON; @@ -906,7 +904,7 @@ URIHANDLER_FUNC(cgi_is_handled) { ds = (data_string *)array_match_key_suffix(p->conf.cgi, con->physical.path); if (NULL == ds) return HANDLER_GO_ON; - st = cgi_stat(con, con->physical.path); + st = cgi_stat(con->physical.path); if (NULL == st) return HANDLER_GO_ON; if (!S_ISREG(st->st_mode)) return HANDLER_GO_ON; diff --git a/src/mod_compress.c b/src/mod_compress.c index 99036ded..ba7eadc7 100644 --- a/src/mod_compress.c +++ b/src/mod_compress.c @@ -533,9 +533,11 @@ static int deflate_file_to_file(connection *con, plugin_data *p, int ifd, buffer return -1; } - buffer_append_string_buffer(p->ofn, sce->etag); + const buffer *etag = stat_cache_etag_get(sce, con->conf.etag_flags); + buffer_append_string_buffer(p->ofn, etag); - if (HANDLER_ERROR != stat_cache_get_entry(con, p->ofn, &sce_ofn)) { + sce_ofn = stat_cache_get_entry(p->ofn); + if (sce_ofn) { if (0 == sce->st.st_size) return -1; /* cache file being created */ /* cache-entry exists */ #if 0 @@ -791,7 +793,8 @@ PHYSICALPATH_FUNC(mod_compress_physical) { uint32_t m; stat_cache_entry *sce = NULL; const buffer *mtime = NULL; - buffer *content_type; + buffer *content_type_trunc; + const buffer *content_type; if (con->mode != DIRECT || con->http_status) return HANDLER_GO_ON; @@ -816,7 +819,8 @@ PHYSICALPATH_FUNC(mod_compress_physical) { "-- handling file as static file"); } - if (HANDLER_ERROR == stat_cache_get_entry(con, con->physical.path, &sce)) { + sce = stat_cache_get_entry(con->physical.path); + if (NULL == sce) { con->http_status = 403; log_error(con->conf.errh, __FILE__, __LINE__, "not a regular file: %s -> %s", @@ -840,28 +844,29 @@ PHYSICALPATH_FUNC(mod_compress_physical) { */ if (sce->st.st_size < 128) return HANDLER_GO_ON; - stat_cache_etag_get(sce, con->conf.etag_flags); + const buffer *etag = stat_cache_etag_get(sce, con->conf.etag_flags); + if (buffer_string_is_empty(etag)) etag = NULL; /* check if mimetype is in compress-config */ - content_type = NULL; - stat_cache_content_type_get(con, con->physical.path, sce); - if (!buffer_is_empty(sce->content_type)) { + content_type_trunc = NULL; + content_type = stat_cache_content_type_get(con, sce); + if (!buffer_is_empty(content_type)) { char *c; - if ( (c = strchr(sce->content_type->ptr, ';')) != NULL) { - content_type = con->srv->tmp_buf; - buffer_copy_string_len(content_type, sce->content_type->ptr, c - sce->content_type->ptr); + if ( (c = strchr(content_type->ptr, ';')) != NULL) { + content_type_trunc = con->srv->tmp_buf; + buffer_copy_string_len(content_type_trunc, content_type->ptr, c - content_type->ptr); } } else { - content_type = con->srv->tmp_buf; - buffer_copy_string_len(content_type, CONST_STR_LEN("")); + content_type = content_type_trunc = con->srv->tmp_buf; + buffer_copy_string_len(content_type_trunc, CONST_STR_LEN("")); } for (m = 0; m < p->conf.compress->used; m++) { data_string *compress_ds = (data_string *)p->conf.compress->data[m]; - if (buffer_is_equal(&compress_ds->value, sce->content_type) - || (content_type && buffer_is_equal(&compress_ds->value, content_type))) { + if (buffer_is_equal(&compress_ds->value, content_type) + || (content_type_trunc && buffer_is_equal(&compress_ds->value, content_type_trunc))) { break; } } @@ -884,7 +889,6 @@ PHYSICALPATH_FUNC(mod_compress_physical) { int accept_encoding = 0; char *value = vb->ptr; int matched_encodings = 0; - int use_etag = sce->etag != NULL && sce->etag->ptr != NULL; /* get client side support encodings */ #ifdef USE_ZLIB @@ -913,7 +917,7 @@ PHYSICALPATH_FUNC(mod_compress_physical) { int compression_type = 0; if (!con->conf.follow_symlink - && 0 != stat_cache_path_contains_symlink(con, con->physical.path)) { + && 0 != stat_cache_path_contains_symlink(con->physical.path, con->conf.errh)) { return HANDLER_GO_ON; } @@ -927,10 +931,10 @@ PHYSICALPATH_FUNC(mod_compress_physical) { mtime = strftime_cache_get(con->srv, sce->st.st_mtime); /* try matching original etag of uncompressed version */ - if (use_etag) { - etag_mutate(con->physical.etag, sce->etag); + if (etag) { + etag_mutate(con->physical.etag, etag); if (HANDLER_FINISHED == http_response_handle_cachable(con, mtime)) { - http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(content_type)); http_header_response_set(con, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime)); http_header_response_set(con, HTTP_HEADER_ETAG, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); close(fd); @@ -957,10 +961,10 @@ PHYSICALPATH_FUNC(mod_compress_physical) { compression_name = dflt_deflate; } - if (use_etag) { + if (etag) { /* try matching etag of compressed version */ buffer * const tb = con->srv->tmp_buf; - buffer_copy_buffer(tb, sce->etag); + buffer_copy_buffer(tb, etag); buffer_append_string_len(tb, CONST_STR_LEN("-")); buffer_append_string(tb, compression_name); etag_mutate(con->physical.etag, tb); @@ -968,9 +972,9 @@ PHYSICALPATH_FUNC(mod_compress_physical) { if (HANDLER_FINISHED == http_response_handle_cachable(con, mtime)) { http_header_response_set(con, HTTP_HEADER_CONTENT_ENCODING, CONST_STR_LEN("Content-Encoding"), compression_name, strlen(compression_name)); - http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(content_type)); http_header_response_set(con, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime)); - if (use_etag) { + if (etag) { http_header_response_set(con, HTTP_HEADER_ETAG, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); } close(fd); @@ -978,7 +982,7 @@ PHYSICALPATH_FUNC(mod_compress_physical) { } /* deflate it */ - if (use_etag && !buffer_string_is_empty(p->conf.compress_cache_dir)) { + if (etag && !buffer_string_is_empty(p->conf.compress_cache_dir)) { if (0 != deflate_file_to_file(con, p, fd, con->physical.path, sce, compression_type)) { close(fd); return HANDLER_GO_ON; @@ -992,12 +996,12 @@ PHYSICALPATH_FUNC(mod_compress_physical) { close(fd); http_header_response_set(con, HTTP_HEADER_CONTENT_ENCODING, CONST_STR_LEN("Content-Encoding"), compression_name, strlen(compression_name)); http_header_response_set(con, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime)); - if (use_etag) { + if (etag) { http_header_response_set(con, HTTP_HEADER_ETAG, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); } - http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(content_type)); /* let mod_staticfile handle the cached compressed files, physical path was modified */ - return (use_etag && !buffer_string_is_empty(p->conf.compress_cache_dir)) ? HANDLER_GO_ON : HANDLER_FINISHED; + return (etag && !buffer_string_is_empty(p->conf.compress_cache_dir)) ? HANDLER_GO_ON : HANDLER_FINISHED; } } } diff --git a/src/mod_dirlisting.c b/src/mod_dirlisting.c index 342618d9..b6e49d7a 100644 --- a/src/mod_dirlisting.c +++ b/src/mod_dirlisting.c @@ -25,14 +25,6 @@ * this is a dirlisting for a lighttpd plugin */ -#ifdef HAVE_ATTR_ATTRIBUTES_H -#include -#endif - -#ifdef HAVE_SYS_EXTATTR_H -#include -#endif - typedef struct { char dir_listing; char hide_dot_files; @@ -835,13 +827,9 @@ static int http_list_directory(connection *con, plugin_data *p, buffer *dir) { dirls_entry_t *tmp; char sizebuf[sizeof("999.9K")]; char datebuf[sizeof("2005-Jan-01 22:23:24")]; - const char *content_type; + const buffer *content_type; long name_max; log_error_st * const errh = con->conf.errh; -#if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) - char attrval[128]; - int attrlen; -#endif #ifdef HAVE_LOCALTIME_R struct tm tm; #endif @@ -974,32 +962,24 @@ static int http_list_directory(connection *con, plugin_data *p, buffer *dir) { } /* files */ + const array * const mimetypes = con->conf.mimetypes; for (i = 0; i < files.used; i++) { tmp = files.ent[i]; + #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) /*(pass full path)*/ content_type = NULL; -#if defined(HAVE_XATTR) - if (con->conf.use_xattr) { - memcpy(path_file, DIRLIST_ENT_NAME(tmp), tmp->namelen + 1); - attrlen = sizeof(attrval) - 1; - if (attr_get(path, con->srv->srvconf.xattr_name, attrval, &attrlen, 0) == 0) { - attrval[attrlen] = '\0'; - content_type = attrval; - } - } -#elif defined(HAVE_EXTATTR) if (con->conf.use_xattr) { memcpy(path_file, DIRLIST_ENT_NAME(tmp), tmp->namelen + 1); - if(-1 != (attrlen = extattr_get_file(path, EXTATTR_NAMESPACE_USER, con->srv->srvconf.xattr_name, attrval, sizeof(attrval)-1))) { - attrval[attrlen] = '\0'; - content_type = attrval; - } + content_type = stat_cache_mimetype_by_xattr(path); } -#endif - - if (content_type == NULL) { - const buffer *type = stat_cache_mimetype_by_ext(con, DIRLIST_ENT_NAME(tmp), tmp->namelen); - content_type = NULL != type ? type->ptr : "application/octet-stream"; + if (NULL == content_type) + #endif + content_type = stat_cache_mimetype_by_ext(mimetypes, DIRLIST_ENT_NAME(tmp), tmp->namelen); + if (NULL == content_type) { + static const buffer octet_stream = + { "application/octet-stream", + sizeof("application/octet-stream"), 0 }; + content_type = &octet_stream; } #ifdef HAVE_LOCALTIME_R @@ -1019,7 +999,7 @@ static int http_list_directory(connection *con, plugin_data *p, buffer *dir) { buffer_append_string_len(out, CONST_STR_LEN("")); buffer_append_string(out, sizebuf); buffer_append_string_len(out, CONST_STR_LEN("")); - buffer_append_string(out, content_type); + buffer_append_string_buffer(out, content_type); buffer_append_string_len(out, CONST_STR_LEN("\n")); free(tmp); @@ -1078,7 +1058,8 @@ URIHANDLER_FUNC(mod_dirlisting_subrequest) { "URI : %s", con->uri.path->ptr); } - if (HANDLER_ERROR == stat_cache_get_entry(con, con->physical.path, &sce)) { + sce = stat_cache_get_entry(con->physical.path); + if (NULL == sce) { log_error(con->conf.errh, __FILE__, __LINE__, "stat_cache_get_entry failed: %s", con->physical.path->ptr); con->http_status = 500; diff --git a/src/mod_evhost.c b/src/mod_evhost.c index 13cb8c9e..de993320 100644 --- a/src/mod_evhost.c +++ b/src/mod_evhost.c @@ -338,7 +338,8 @@ static handler_t mod_evhost_uri_handler(connection *con, void *p_d) { buffer * const b = &p->tmp_buf; mod_evhost_build_doc_root_path(b, &p->split_vals, con->uri.authority, p->conf.path_pieces); - if (HANDLER_ERROR == stat_cache_get_entry(con, b, &sce)) { + sce = stat_cache_get_entry(b); + if (NULL == sce) { log_perror(con->conf.errh, __FILE__, __LINE__, "%s", b->ptr); } else if(!S_ISDIR(sce->st.st_mode)) { log_error(con->conf.errh, __FILE__, __LINE__, "not a directory: %s", b->ptr); diff --git a/src/mod_expire.c b/src/mod_expire.c index 59b2288b..61fcb381 100644 --- a/src/mod_expire.c +++ b/src/mod_expire.c @@ -318,8 +318,7 @@ CONNECTION_FUNC(mod_expire_handler) { case 1: /* modification */ - /* if stat fails => sce == NULL, ignore return value */ - (void) stat_cache_get_entry(con, con->physical.path, &sce); + sce = stat_cache_get_entry(con->physical.path); /* can't set modification based expire header if * mtime is not available diff --git a/src/mod_indexfile.c b/src/mod_indexfile.c index 6832de70..01fd9288 100644 --- a/src/mod_indexfile.c +++ b/src/mod_indexfile.c @@ -133,8 +133,8 @@ URIHANDLER_FUNC(mod_indexfile_subrequest) { } buffer_append_string_buffer(b, &ds->value); - stat_cache_entry *sce = NULL; - if (HANDLER_ERROR == stat_cache_get_entry(con, b, &sce)) { + stat_cache_entry * const sce = stat_cache_get_entry(b); + if (NULL == sce) { if (errno == EACCES) { con->http_status = 403; buffer_reset(con->physical.path); diff --git a/src/mod_magnet.c b/src/mod_magnet.c index cb1149c2..1884e80f 100644 --- a/src/mod_magnet.c +++ b/src/mod_magnet.c @@ -288,24 +288,14 @@ static int magnet_print(lua_State *L) { } static int magnet_stat(lua_State *L) { - connection *con = magnet_get_connection(L); - stat_cache_entry *sce = NULL; - { - buffer *sb = magnet_checkbuffer(L, 1); - handler_t res; - - res = stat_cache_get_entry(con, sb, &sce); - - if (HANDLER_GO_ON != res) { - buffer_free(sb); + buffer * const sb = magnet_checkbuffer(L, 1); + stat_cache_entry * const sce = stat_cache_get_entry(sb); + buffer_free(sb); + if (NULL == sce) { lua_pushnil(L); return 1; } - stat_cache_content_type_get(con, sb, sce); - buffer_free(sb); - } - lua_newtable(L); // return value lua_pushboolean(L, S_ISREG(sce->st.st_mode)); @@ -350,18 +340,21 @@ static int magnet_stat(lua_State *L) { lua_pushinteger(L, sce->st.st_ino); lua_setfield(L, -2, "st_ino"); - if (!buffer_string_is_empty(stat_cache_etag_get(sce, con->conf.etag_flags))) { + connection * const con = magnet_get_connection(L); + const buffer *etag = stat_cache_etag_get(sce, con->conf.etag_flags); + if (!buffer_string_is_empty(etag)) { /* we have to mutate the etag */ buffer * const tb = con->srv->tmp_buf; - etag_mutate(tb, sce->etag); + etag_mutate(tb, etag); lua_pushlstring(L, CONST_BUF_LEN(tb)); } else { lua_pushnil(L); } lua_setfield(L, -2, "etag"); - if (!buffer_string_is_empty(sce->content_type)) { - lua_pushlstring(L, CONST_BUF_LEN(sce->content_type)); + const buffer *content_type = stat_cache_content_type_get(con, sce); + if (!buffer_string_is_empty(content_type)) { + lua_pushlstring(L, CONST_BUF_LEN(content_type)); } else { lua_pushnil(L); } @@ -774,7 +767,7 @@ static handler_t magnet_attract(connection *con, plugin_data *p, buffer *name) { const int lighty_table_ndx = 2; /* get the script-context */ - L = script_cache_get_script(con, &p->cache, name); + L = script_cache_get_script(&p->cache, name, con->conf.etag_flags); if (lua_isstring(L, -1)) { log_error(con->conf.errh, __FILE__, __LINE__, diff --git a/src/mod_magnet_cache.c b/src/mod_magnet_cache.c index 2644bacb..3afdf2a8 100644 --- a/src/mod_magnet_cache.c +++ b/src/mod_magnet_cache.c @@ -1,7 +1,7 @@ #include "first.h" #include "mod_magnet_cache.h" -#include "base.h" +#include "log.h" #include "stat_cache.h" #include @@ -53,29 +53,29 @@ void script_cache_free_data(script_cache *p) { free(p->ptr); } -lua_State *script_cache_get_script(connection *con, script_cache *cache, buffer *name) { - size_t i; +lua_State *script_cache_get_script(script_cache *cache, buffer *name, int etag_flags) { script *sc = NULL; stat_cache_entry *sce; - for (i = 0; i < cache->used; i++) { + for (uint32_t i = 0; i < cache->used; ++i, sc = NULL) { sc = cache->ptr[i]; + if (!buffer_is_equal(name, sc->name)) continue; - if (buffer_is_equal(name, sc->name)) { - sc->last_used = time(NULL); + sc->last_used = log_epoch_secs; /* oops, the script failed last time */ if (lua_gettop(sc->L) == 0) break; force_assert(lua_gettop(sc->L) == 1); - if (HANDLER_ERROR == stat_cache_get_entry(con, sc->name, &sce)) { + sce = stat_cache_get_entry(sc->name); + if (NULL == sce) { lua_pop(sc->L, 1); /* pop the old function */ break; } - stat_cache_etag_get(sce, con->conf.etag_flags); - if (!buffer_is_equal(sce->etag, sc->etag)) { + const buffer *etag = stat_cache_etag_get(sce, etag_flags); + if (NULL == etag || !buffer_is_equal(sc->etag, etag)) { /* the etag is outdated, reload the function */ lua_pop(sc->L, 1); break; @@ -84,9 +84,6 @@ lua_State *script_cache_get_script(connection *con, script_cache *cache, buffer force_assert(lua_isfunction(sc->L, -1)); return sc->L; - } - - sc = NULL; } /* if the script was script already loaded but either got changed or @@ -107,15 +104,16 @@ lua_State *script_cache_get_script(connection *con, script_cache *cache, buffer luaL_openlibs(sc->L); } - sc->last_used = time(NULL); + sc->last_used = log_epoch_secs; if (0 != luaL_loadfile(sc->L, name->ptr)) { /* oops, an error, return it */ return sc->L; } - if (HANDLER_GO_ON == stat_cache_get_entry(con, sc->name, &sce)) { - buffer_copy_buffer(sc->etag, stat_cache_etag_get(sce, con->conf.etag_flags)); + sce = stat_cache_get_entry(sc->name); + if (sce) { + buffer_copy_buffer(sc->etag, stat_cache_etag_get(sce, etag_flags)); } force_assert(lua_isfunction(sc->L, -1)); diff --git a/src/mod_magnet_cache.h b/src/mod_magnet_cache.h index bb9a9a5d..eb9e544c 100644 --- a/src/mod_magnet_cache.h +++ b/src/mod_magnet_cache.h @@ -18,14 +18,13 @@ typedef struct { typedef struct { script **ptr; - - size_t used; - size_t size; + uint32_t used; + uint32_t size; } script_cache; script_cache *script_cache_init(void); void script_cache_free_data(script_cache *cache); -lua_State *script_cache_get_script(connection *con, script_cache *cache, buffer *name); +lua_State *script_cache_get_script(script_cache *cache, buffer *name, int etag_flags); #endif diff --git a/src/mod_mysql_vhost.c b/src/mod_mysql_vhost.c index 1d57e256..c06e823a 100644 --- a/src/mod_mysql_vhost.c +++ b/src/mod_mysql_vhost.c @@ -324,7 +324,8 @@ CONNECTION_FUNC(mod_mysql_vhost_handle_docroot) { buffer_copy_string(b, row[0]); buffer_append_slash(b); - if (HANDLER_ERROR == stat_cache_get_entry(con, b, &sce)) { + sce = stat_cache_get_entry(b); + if (NULL == sce) { log_perror(con->conf.errh, __FILE__, __LINE__, "%s", b->ptr); goto ERR500; } diff --git a/src/mod_rewrite.c b/src/mod_rewrite.c index cd98a27a..58f37b84 100644 --- a/src/mod_rewrite.c +++ b/src/mod_rewrite.c @@ -334,10 +334,8 @@ URIHANDLER_FUNC(mod_rewrite_physical) { if (!p->conf.rewrite_NF || !p->conf.rewrite_NF->used) return HANDLER_GO_ON; /* skip if physical.path is a regular file */ - stat_cache_entry *sce; - if (HANDLER_ERROR != stat_cache_get_entry(con, con->physical.path, &sce)) { - if (S_ISREG(sce->st.st_mode)) return HANDLER_GO_ON; - } + stat_cache_entry *sce = stat_cache_get_entry(con->physical.path); + if (sce && S_ISREG(sce->st.st_mode)) return HANDLER_GO_ON; return process_rewrite_rules(con, p, p->conf.rewrite_NF); } diff --git a/src/mod_simple_vhost.c b/src/mod_simple_vhost.c index 57006eca..7b24ebd3 100644 --- a/src/mod_simple_vhost.c +++ b/src/mod_simple_vhost.c @@ -158,7 +158,8 @@ static int build_doc_root(connection *con, plugin_data *p, buffer *out, const bu /* one-element cache (postive cache, not negative cache) */ if (buffer_is_equal(out, &p->last_root)) return 1; - if (HANDLER_ERROR == stat_cache_get_entry(con, out, &sce)) { + sce = stat_cache_get_entry(out); + if (NULL == sce) { if (p->conf.debug) { log_perror(con->conf.errh, __FILE__, __LINE__, "%s", out->ptr); } diff --git a/src/mod_ssi.c b/src/mod_ssi.c index e93ae1ce..b54f1cf6 100644 --- a/src/mod_ssi.c +++ b/src/mod_ssi.c @@ -605,7 +605,7 @@ static int process_ssi_stmt(connection *con, handler_ctx *p, const char **l, siz } if (!con->conf.follow_symlink - && 0 != stat_cache_path_contains_symlink(con, p->stat_fn)) { + && 0 != stat_cache_path_contains_symlink(p->stat_fn, con->conf.errh)) { break; } diff --git a/src/mod_vhostdb.c b/src/mod_vhostdb.c index c9763f65..d60e66fb 100644 --- a/src/mod_vhostdb.c +++ b/src/mod_vhostdb.c @@ -190,7 +190,8 @@ CONNECTION_FUNC(mod_vhostdb_handle_docroot) { /* sanity check that really is a directory */ buffer_append_slash(b); - if (HANDLER_ERROR == stat_cache_get_entry(con, b, &sce)) { + sce = stat_cache_get_entry(b); + if (NULL == sce) { log_perror(con->conf.errh, __FILE__, __LINE__, "%s", b->ptr); return mod_vhostdb_error_500(con); /* HANDLER_FINISHED */ } diff --git a/src/mod_webdav.c b/src/mod_webdav.c index 4449f46f..115da2a7 100644 --- a/src/mod_webdav.c +++ b/src/mod_webdav.c @@ -312,7 +312,6 @@ typedef struct { unsigned short deprecated_unsafe_partial_put_compat; sql_config *sql; - server *srv; buffer *tmpb; buffer *sqlite_db_name; /* not used after worker init */ array *opts; @@ -497,7 +496,6 @@ SETDEFAULTS_FUNC(mod_webdav_set_defaults) { } } - p->defaults.srv = srv; p->defaults.tmpb = srv->tmp_buf; /* initialize p->defaults from global config context */ @@ -2170,27 +2168,25 @@ webdav_if_match_or_unmodified_since (connection * const con, struct stat *st) static void -webdav_response_etag (const plugin_config * const pconf, - connection * const con, struct stat *st) +webdav_response_etag (connection * const con, struct stat *st) { - server *srv = pconf->srv; if (0 != con->conf.etag_flags) { buffer *etagb = con->physical.etag; etag_create(etagb, st, con->conf.etag_flags); - stat_cache_update_entry(srv,CONST_BUF_LEN(con->physical.path),st,etagb); + stat_cache_update_entry(CONST_BUF_LEN(con->physical.path), st, etagb); etag_mutate(etagb, etagb); http_header_response_set(con, HTTP_HEADER_ETAG, CONST_STR_LEN("ETag"), CONST_BUF_LEN(etagb)); } else { - stat_cache_update_entry(srv,CONST_BUF_LEN(con->physical.path),st,NULL); + stat_cache_update_entry(CONST_BUF_LEN(con->physical.path), st, NULL); } } static void -webdav_parent_modified (const plugin_config * const pconf, const buffer *path) +webdav_parent_modified (const buffer *path) { size_t dirlen = buffer_string_length(path); const char *fn = path->ptr; @@ -2199,7 +2195,7 @@ webdav_parent_modified (const plugin_config * const pconf, const buffer *path) if (fn[dirlen-1] == '/') --dirlen; if (0 != dirlen) while (fn[--dirlen] != '/') ; if (0 == dirlen) dirlen = 1; /* root dir ("/") */ - stat_cache_invalidate_entry(pconf->srv, fn, dirlen); + stat_cache_invalidate_entry(fn, dirlen); } @@ -2229,7 +2225,7 @@ webdav_unlinkat (const plugin_config * const pconf, const buffer * const uri, const int dfd, const char * const d_name, size_t len) { if (0 == unlinkat(dfd, d_name, 0)) { - stat_cache_delete_entry(pconf->srv, d_name, len); + stat_cache_delete_entry(d_name, len); return webdav_prop_delete_uri(pconf, uri); } @@ -2246,7 +2242,7 @@ webdav_delete_file (const plugin_config * const pconf, const physical_st * const dst) { if (0 == unlink(dst->path->ptr)) { - stat_cache_delete_entry(pconf->srv, CONST_BUF_LEN(dst->path)); + stat_cache_delete_entry(CONST_BUF_LEN(dst->path)); return webdav_prop_delete_uri(pconf, dst->rel_path); } @@ -2448,7 +2444,7 @@ webdav_copytmp_rename (const plugin_config * const pconf, { /* unconditional stat cache deletion * (not worth extra syscall/race to detect overwritten or not) */ - stat_cache_delete_entry(pconf->srv, CONST_BUF_LEN(dst->path)); + stat_cache_delete_entry(CONST_BUF_LEN(dst->path)); return 0; } else { @@ -2501,8 +2497,8 @@ webdav_copymove_file (const plugin_config * const pconf, if (overwrite) unlink(src->path->ptr); /* unconditional stat cache deletion * (not worth extra syscall/race to detect overwritten or not) */ - stat_cache_delete_entry(pconf->srv, CONST_BUF_LEN(dst->path)); - stat_cache_delete_entry(pconf->srv, CONST_BUF_LEN(src->path)); + stat_cache_delete_entry(CONST_BUF_LEN(dst->path)); + stat_cache_delete_entry(CONST_BUF_LEN(src->path)); webdav_prop_move_uri(pconf, src->rel_path, dst->rel_path); return 0; } @@ -2546,7 +2542,7 @@ webdav_mkdir (const plugin_config * const pconf, const int overwrite) { if (0 == mkdir(dst->path->ptr, WEBDAV_DIR_MODE)) { - webdav_parent_modified(pconf, dst->path); + webdav_parent_modified(dst->path); return 0; } @@ -2594,7 +2590,7 @@ webdav_mkdir (const plugin_config * const pconf, if (0 != status) return status; - webdav_parent_modified(pconf, dst->path); + webdav_parent_modified(dst->path); return (0 == mkdir(dst->path->ptr, WEBDAV_DIR_MODE)) ? 0 : 409; /* Conflict */ @@ -3026,8 +3022,9 @@ webdav_propfind_live_props (const webdav_propfind_bufs * const restrict pb, else { /* provide content type by extension * Note: not currently supporting filesystem xattr */ + const array * const mtypes = pb->con->conf.mimetypes; const buffer *ct = - stat_cache_mimetype_by_ext(pb->con, CONST_BUF_LEN(pb->dst->path)); + stat_cache_mimetype_by_ext(mtypes, CONST_BUF_LEN(pb->dst->path)); if (NULL != ct) { buffer_append_string_len(b, CONST_STR_LEN( "")); @@ -4047,7 +4044,7 @@ mod_webdav_delete (connection * const con, const plugin_config * const pconf) buffer_free(ms); /* invalidate stat cache of src if DELETE, whether or not successful */ - stat_cache_delete_dir(pconf->srv, CONST_BUF_LEN(con->physical.path)); + stat_cache_delete_dir(CONST_BUF_LEN(con->physical.path)); } else if (con->physical.path->ptr[con->physical.path->used - 2] == '/') http_status_set_error(con, 403); @@ -4191,10 +4188,10 @@ mod_webdav_put_0 (connection * const con, const plugin_config * const pconf) if (0 != con->conf.etag_flags) { /*(skip sending etag if fstat() error; not expected)*/ struct stat st; - if (0 == fstat(fd, &st)) webdav_response_etag(pconf, con, &st); + if (0 == fstat(fd, &st)) webdav_response_etag(con, &st); } close(fd); - webdav_parent_modified(pconf, con->physical.path); + webdav_parent_modified(con->physical.path); http_status_set_fin(con, 201); /* Created */ return HANDLER_FINISHED; } @@ -4311,7 +4308,6 @@ mod_webdav_put_prep (connection * const con, const plugin_config * const pconf) #if (defined(__linux__) || defined(__CYGWIN__)) && defined(O_TMPFILE) static int mod_webdav_put_linkat_rename (connection * const con, - const plugin_config * const pconf, const char * const pathtemp) { chunkqueue * const cq = con->request_content_queue; @@ -4336,7 +4332,7 @@ mod_webdav_put_linkat_rename (connection * const con, ? 204 /* No Content */ : 201); /* Created */ if (201 == http_status_get(con)) - webdav_parent_modified(pconf, con->physical.path); + webdav_parent_modified(con->physical.path); if (0 != rename(pathtemp, con->physical.path->ptr)) #endif { @@ -4351,7 +4347,7 @@ mod_webdav_put_linkat_rename (connection * const con, && http_status_get(con) < 300) { /*(201, 204)*/ /*(skip sending etag if fstat() error; not expected)*/ if (0 == fstat(c->file.fd, &st)) - webdav_response_etag(pconf, con, &st); + webdav_response_etag(con, &st); } chunkqueue_mark_written(cq, c->file.length); @@ -4366,7 +4362,6 @@ mod_webdav_put_linkat_rename (connection * const con, __attribute_cold__ static handler_t mod_webdav_put_deprecated_unsafe_partial_put_compat (connection * const con, - const plugin_config *pconf, const buffer * const h) { /* historical code performed very limited range parse (repeated here) */ @@ -4416,7 +4411,7 @@ mod_webdav_put_deprecated_unsafe_partial_put_compat (connection * const con, if (!http_status_is_set(con)) { http_status_set_fin(con, 204); /* No Content */ - if (0 != con->conf.etag_flags) webdav_response_etag(pconf, con, &st); + if (0 != con->conf.etag_flags) webdav_response_etag(con, &st); } return HANDLER_FINISHED; @@ -4450,7 +4445,7 @@ mod_webdav_put (connection * const con, const plugin_config * const pconf) CONST_STR_LEN("Content-Range")); if (NULL != h) return - mod_webdav_put_deprecated_unsafe_partial_put_compat(con,pconf,h); + mod_webdav_put_deprecated_unsafe_partial_put_compat(con, h); } /* construct temporary filename in same directory as target @@ -4499,7 +4494,7 @@ mod_webdav_put (connection * const con, const plugin_config * const pconf) if (!mod_webdav_write_single_file_chunk(con, cq)) return HANDLER_FINISHED; } - if (mod_webdav_put_linkat_rename(con, pconf, pathtemp)) + if (mod_webdav_put_linkat_rename(con, pathtemp)) return HANDLER_FINISHED; /* attempt traditional copy (below) if linkat() failed for any reason */ } @@ -4535,9 +4530,9 @@ mod_webdav_put (connection * const con, const plugin_config * const pconf) ? 204 /* No Content */ : 201); /* Created */ if (201 == http_status_get(con)) - webdav_parent_modified(pconf, con->physical.path); + webdav_parent_modified(con->physical.path); if (0 == rename(pathtemp, con->physical.path->ptr)) { - if (0 != con->conf.etag_flags) webdav_response_etag(pconf,con,&st); + if (0 != con->conf.etag_flags) webdav_response_etag(con, &st); } else { if (errno == EISDIR) @@ -4833,7 +4828,7 @@ mod_webdav_copymove_b (connection * const con, const plugin_config * const pconf buffer_free(ms); /* invalidate stat cache of src if MOVE, whether or not successful */ if (con->request.http_method == HTTP_METHOD_MOVE) - stat_cache_delete_dir(pconf->srv,CONST_BUF_LEN(con->physical.path)); + stat_cache_delete_dir(CONST_BUF_LEN(con->physical.path)); return HANDLER_FINISHED; } else if (con->physical.path->ptr[con->physical.path->used - 2] == '/') { @@ -4891,7 +4886,7 @@ mod_webdav_copymove_b (connection * const con, const plugin_config * const pconf *slash = '/'; /* new entity will be created */ if (!http_status_is_set(con)) { - webdav_parent_modified(pconf, dst_path); + webdav_parent_modified(dst_path); http_status_set_fin(con, 201); /* Created */ } break; @@ -5364,7 +5359,7 @@ mod_webdav_lock (connection * const con, const plugin_config * const pconf) if (0 != fstat(fd, &st)) con->conf.etag_flags = 0; close(fd); created = 1; - webdav_parent_modified(pconf, con->physical.path); + webdav_parent_modified(con->physical.path); } else if (errno != EEXIST) { http_status_set_error(con, 403); /* Forbidden */ @@ -5423,7 +5418,7 @@ mod_webdav_lock (connection * const con, const plugin_config * const pconf) lockstr, sizeof(lockstr)-1); webdav_xml_doc_lock_acquired(con, pconf, &lockdata); if (0 != con->conf.etag_flags && !S_ISDIR(st.st_mode)) - webdav_response_etag(pconf, con, &st); + webdav_response_etag(con, &st); http_status_set_fin(con, created ? 201 : 200); /* Created | OK */ } else /*(database error obtaining lock)*/ diff --git a/src/response.c b/src/response.c index 2b51baaf..3071d4c6 100644 --- a/src/response.c +++ b/src/response.c @@ -133,9 +133,9 @@ int http_response_write_header(connection *con) { } static handler_t http_response_physical_path_check(connection *con) { - stat_cache_entry *sce = NULL; + stat_cache_entry *sce = stat_cache_get_entry(con->physical.path); - if (HANDLER_ERROR != stat_cache_get_entry(con, con->physical.path, &sce)) { + if (sce) { /* file exists */ } else { char *pathinfo = NULL; @@ -204,9 +204,9 @@ static handler_t http_response_physical_path_check(connection *con) { buffer * const tb = con->srv->tmp_buf; for (char *pprev = pathinfo; pathinfo; pprev = pathinfo, pathinfo = strchr(pathinfo+1, '/')) { - stat_cache_entry *nsce = NULL; buffer_copy_string_len(tb, con->physical.path->ptr, pathinfo - con->physical.path->ptr); - if (HANDLER_ERROR == stat_cache_get_entry(con, tb, &nsce)) { + stat_cache_entry *nsce = stat_cache_get_entry(tb); + if (NULL == nsce) { pathinfo = pathinfo != pprev ? pprev : NULL; break; } @@ -254,7 +254,7 @@ static handler_t http_response_physical_path_check(connection *con) { } if (!con->conf.follow_symlink - && 0 != stat_cache_path_contains_symlink(con, con->physical.path)) { + && 0 != stat_cache_path_contains_symlink(con->physical.path, con->conf.errh)) { con->http_status = 403; if (con->conf.log_request_handling) { diff --git a/src/server.c b/src/server.c index a924dada..9fbb1563 100644 --- a/src/server.c +++ b/src/server.c @@ -285,9 +285,7 @@ static void server_free(server *srv) { free(srv->joblist.ptr); free(srv->fdwaitqueue.ptr); - if (srv->stat_cache) { - stat_cache_free(srv->stat_cache); - } + stat_cache_free(); li_rand_cleanup(); chunkqueue_chunk_pool_free(); @@ -1541,7 +1539,7 @@ static int server_main (server * const srv, int argc, char **argv) { } /* might fail if user is using fam (not gamin) and famd isn't running */ - if (NULL == (srv->stat_cache = stat_cache_init(srv))) { + if (!stat_cache_init(srv)) { log_error(srv->errh, __FILE__, __LINE__, "stat-cache could not be setup, dieing."); return -1; @@ -1646,7 +1644,7 @@ static void server_handle_sigalrm (server * const srv, time_t min_ts, time_t las /* free excess chunkqueue buffers every 64 seconds */ if (0 == (min_ts & 0x3f)) chunkqueue_chunk_pool_clear(); /* cleanup stat-cache */ - stat_cache_trigger_cleanup(srv); + stat_cache_trigger_cleanup(); /* reset global/aggregate rate limit counters */ config_reset_config_bytes_sec(srv->config_data_base); /* if graceful_shutdown, accelerate cleanup of recently completed request/responses */ diff --git a/src/stat_cache.c b/src/stat_cache.c index 782388af..716f5b1f 100644 --- a/src/stat_cache.c +++ b/src/stat_cache.c @@ -38,19 +38,22 @@ */ enum { - STAT_CACHE_ENGINE_UNSET, + STAT_CACHE_ENGINE_SIMPLE, /*(default)*/ STAT_CACHE_ENGINE_NONE, - STAT_CACHE_ENGINE_SIMPLE, STAT_CACHE_ENGINE_FAM }; struct stat_cache_fam; /* declaration */ typedef struct stat_cache { + int stat_cache_engine; splay_tree *files; /* nodes of tree are (stat_cache_entry *) */ struct stat_cache_fam *scf; + log_error_st *errh; } stat_cache; +static stat_cache sc; + /* the famous DJB hash function for strings */ __attribute_pure__ @@ -206,10 +209,10 @@ static void fam_dir_tag_refcnt(splay_tree *t, int *keys, int *ndx) } } -static void fam_dir_periodic_cleanup(server *srv) { +static void fam_dir_periodic_cleanup() { int max_ndx, i; int keys[8192]; /* 32k size on stack */ - stat_cache_fam * const scf = srv->stat_cache->scf; + stat_cache_fam * const scf = sc.scf; do { if (!scf->dirs) return; max_ndx = 0; @@ -241,10 +244,10 @@ static void fam_dir_invalidate_tree(splay_tree *t, const char *name, size_t len) } /* declarations */ -static void stat_cache_delete_tree(server *srv, const char *name, size_t len); -static void stat_cache_invalidate_dir_tree(server *srv, const char *name, size_t len); +static void stat_cache_delete_tree(const char *name, size_t len); +static void stat_cache_invalidate_dir_tree(const char *name, size_t len); -static void stat_cache_handle_fdevent_in(server *srv, stat_cache_fam *scf) +static void stat_cache_handle_fdevent_in(stat_cache_fam *scf) { for (int i = 0, ndx; i || (i = FAMPending(&scf->fam)) > 0; --i) { FAMEvent fe; @@ -290,7 +293,7 @@ static void stat_cache_handle_fdevent_in(server *srv, stat_cache_fam *scf) buffer_append_string_len(n, CONST_STR_LEN("/")); buffer_append_string_len(n,fe.filename,strlen(fe.filename)); /* (alternatively, could chose to stat() and update)*/ - stat_cache_invalidate_entry(srv, CONST_BUF_LEN(n)); + stat_cache_invalidate_entry(CONST_BUF_LEN(n)); fam_link = /*(check if might be symlink to monitored dir)*/ stat_cache_sptree_find(&scf->dirs, CONST_BUF_LEN(n)); @@ -301,7 +304,7 @@ static void stat_cache_handle_fdevent_in(server *srv, stat_cache_fam *scf) if (fam_link) { /* replaced symlink changes containing dir */ - stat_cache_invalidate_entry(srv, CONST_BUF_LEN(n)); + stat_cache_invalidate_entry(CONST_BUF_LEN(n)); /* handle symlink to dir as deleted dir below */ fe.code = FAMDeleted; fam_dir = fam_link; @@ -315,15 +318,15 @@ static void stat_cache_handle_fdevent_in(server *srv, stat_cache_fam *scf) switch(fe.code) { case FAMChanged: - stat_cache_invalidate_entry(srv, CONST_BUF_LEN(fam_dir->name)); + stat_cache_invalidate_entry(CONST_BUF_LEN(fam_dir->name)); break; case FAMDeleted: case FAMMoved: - stat_cache_delete_tree(srv, CONST_BUF_LEN(fam_dir->name)); + stat_cache_delete_tree(CONST_BUF_LEN(fam_dir->name)); fam_dir_invalidate_node(fam_dir); if (scf->dirs) fam_dir_invalidate_tree(scf->dirs,CONST_BUF_LEN(fam_dir->name)); - fam_dir_periodic_cleanup(srv); + fam_dir_periodic_cleanup(); break; default: break; @@ -333,11 +336,11 @@ static void stat_cache_handle_fdevent_in(server *srv, stat_cache_fam *scf) static handler_t stat_cache_handle_fdevent(server *srv, void *_fce, int revent) { - stat_cache_fam *scf = srv->stat_cache->scf; + stat_cache_fam * const scf = sc.scf; UNUSED(_fce); if (revent & FDEVENT_IN) { - stat_cache_handle_fdevent_in(srv, scf); + stat_cache_handle_fdevent_in(scf); } if (revent & (FDEVENT_HUP|FDEVENT_RDHUP)) { @@ -346,7 +349,7 @@ static handler_t stat_cache_handle_fdevent(server *srv, void *_fce, int revent) "FAM connection closed; disabling stat_cache."); /* (although effectively STAT_CACHE_ENGINE_NONE, * do not change here so that periodic jobs clean up memory)*/ - /*srv->srvconf.stat_cache_engine = STAT_CACHE_ENGINE_NONE; */ + /*sc.stat_cache_engine = STAT_CACHE_ENGINE_NONE; */ fdevent_fdnode_event_del(srv->ev, scf->fdn); fdevent_unregister(srv->ev, scf->fd); scf->fdn = NULL; @@ -400,7 +403,7 @@ static void stat_cache_free_fam(stat_cache_fam *scf) { free(scf); } -static fam_dir_entry * fam_dir_monitor(server *srv, stat_cache_fam *scf, char *fn, size_t dirlen, struct stat *st) +static fam_dir_entry * fam_dir_monitor(stat_cache_fam *scf, char *fn, size_t dirlen, struct stat *st) { if (NULL == scf->fdn) return NULL; /* FAM connection closed; do nothing */ const int fn_is_dir = S_ISDIR(st->st_mode); @@ -461,9 +464,9 @@ static fam_dir_entry * fam_dir_monitor(server *srv, stat_cache_fam *scf, char *f * fam_dir is not NULL and so splaytree_insert not called below)*/ if (scf->dirs) fam_dir_invalidate_tree(scf->dirs, fn, dirlen); if (!fn_is_dir) /*(if dir, caller is updating stat_cache_entry)*/ - stat_cache_update_entry(srv, fn, dirlen, st, NULL); + stat_cache_update_entry(fn, dirlen, st, NULL); /*(must not delete tree since caller is holding a valid node)*/ - stat_cache_invalidate_dir_tree(srv, fn, dirlen); + stat_cache_invalidate_dir_tree(fn, dirlen); if (0 != FAMCancelMonitor(&scf->fam, &fam_dir->req) || 0 != FAMMonitorDirectory(&scf->fam, fam_dir->name->ptr, &fam_dir->req, @@ -482,9 +485,9 @@ static fam_dir_entry * fam_dir_monitor(server *srv, stat_cache_fam *scf, char *f if (0 != FAMMonitorDirectory(&scf->fam,fam_dir->name->ptr,&fam_dir->req, (void *)(intptr_t)dir_ndx)) { - log_error(srv->errh, __FILE__, __LINE__, - "monitoring dir failed: %s file: %s %s", - fam_dir->name->ptr, fn, FamErrlist[FAMErrno]); + log_error(sc.errh, __FILE__, __LINE__, + "monitoring dir failed: %s file: %s %s", + fam_dir->name->ptr, fn, FamErrlist[FAMErrno]); fam_dir_entry_free(fam_dir); return NULL; } @@ -511,7 +514,7 @@ static fam_dir_entry * fam_dir_monitor(server *srv, stat_cache_fam *scf, char *f fam_dir->fam_parent = NULL; } if (S_ISLNK(lst.st_mode)) { - fam_dir->fam_parent = fam_dir_monitor(srv, scf, fn, dirlen, &lst); + fam_dir->fam_parent = fam_dir_monitor(scf, fn, dirlen, &lst); } } @@ -522,127 +525,126 @@ static fam_dir_entry * fam_dir_monitor(server *srv, stat_cache_fam *scf, char *f #endif -stat_cache *stat_cache_init(server *srv) { - stat_cache *sc = NULL; - UNUSED(srv); +static stat_cache_entry * stat_cache_entry_init(void) { + stat_cache_entry *sce = calloc(1, sizeof(*sce)); + force_assert(NULL != sce); + return sce; +} - sc = calloc(1, sizeof(*sc)); - force_assert(NULL != sc); +static void stat_cache_entry_free(void *data) { + stat_cache_entry *sce = data; + if (!sce) return; -#ifdef HAVE_FAM_H - if (STAT_CACHE_ENGINE_FAM == srv->srvconf.stat_cache_engine) { - sc->scf = stat_cache_init_fam(srv); - if (NULL == sc->scf) { - free(sc); - return NULL; - } - } -#endif + #ifdef HAVE_FAM_H + /*(decrement refcnt only; + * defer cancelling FAM monitor on dir even if refcnt reaches zero)*/ + if (sce->fam_dir) --((fam_dir_entry *)sce->fam_dir)->refcnt; + #endif - return sc; + free(sce->name.ptr); + free(sce->etag.ptr); + if (sce->content_type.size) free(sce->content_type.ptr); + + free(sce); } -static stat_cache_entry * stat_cache_entry_init(void) { - stat_cache_entry *sce = NULL; +#if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) - sce = calloc(1, sizeof(*sce)); - force_assert(NULL != sce); +static const char *attrname = "Content-Type"; +static char attrval[128]; +static buffer attrb = { attrval, 0, 0 }; - sce->name = buffer_init(); - sce->etag = buffer_init(); - sce->content_type = buffer_init(); +static int stat_cache_attr_get(char *name) { + #if defined(HAVE_XATTR) + int attrlen = sizeof(attrval)-1; + if (0 == attr_get(name, attrname, attrval, &attrlen, 0)) + #elif defined(HAVE_EXTATTR) + ssize_t attrlen; + if (0 < (attrlen = extattr_get_file(name, EXTATTR_NAMESPACE_USER, attrname, + attrval, sizeof(attrval)-1))) + #endif + { + attrval[attrlen] = '\0'; + attrb.used = (uint32_t)(attrlen + 1); + return 1; + } + return 0; +} - return sce; +#endif + +int stat_cache_init(server *srv) { + #ifdef HAVE_FAM_H + if (sc.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { + sc.errh = srv->errh; + sc.scf = stat_cache_init_fam(srv); + if (NULL == sc.scf) return 0; + } + #endif + + return 1; } -static void stat_cache_entry_free(void *data) { - stat_cache_entry *sce = data; - if (!sce) return; +void stat_cache_free(void) { + splay_tree *sptree = sc.files; + while (sptree) { + stat_cache_entry_free(sptree->data); + sptree = splaytree_delete(sptree, sptree->key); + } + sc.files = NULL; - #ifdef HAVE_FAM_H - /*(decrement refcnt only; - * defer cancelling FAM monitor on dir even if refcnt reaches zero)*/ - if (sce->fam_dir) --((fam_dir_entry *)sce->fam_dir)->refcnt; - #endif + #ifdef HAVE_FAM_H + stat_cache_free_fam(sc.scf); + sc.scf = NULL; + sc.errh = NULL; + #endif - buffer_free(sce->etag); - buffer_free(sce->name); - buffer_free(sce->content_type); + #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) + attrname = "Content-Type"; + #endif - free(sce); + sc.stat_cache_engine = STAT_CACHE_ENGINE_SIMPLE; /*(default)*/ } -void stat_cache_free(stat_cache *sc) { - while (sc->files) { - splay_tree *node = sc->files; - stat_cache_entry_free(node->data); - sc->files = splaytree_delete(sc->files, node->key); - } - -#ifdef HAVE_FAM_H - stat_cache_free_fam(sc->scf); -#endif - free(sc); +void stat_cache_xattrname (const char *name) { + #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) + attrname = name; + #else + UNUSED(name); + #endif } int stat_cache_choose_engine (server *srv, const buffer *stat_cache_string) { - if (buffer_string_is_empty(stat_cache_string)) { - srv->srvconf.stat_cache_engine = STAT_CACHE_ENGINE_SIMPLE; - } else if (buffer_is_equal_string(stat_cache_string, CONST_STR_LEN("simple"))) { - srv->srvconf.stat_cache_engine = STAT_CACHE_ENGINE_SIMPLE; + if (buffer_string_is_empty(stat_cache_string)) + sc.stat_cache_engine = STAT_CACHE_ENGINE_SIMPLE; + else if (buffer_eq_slen(stat_cache_string, CONST_STR_LEN("simple"))) + sc.stat_cache_engine = STAT_CACHE_ENGINE_SIMPLE; #ifdef HAVE_FAM_H - } else if (buffer_is_equal_string(stat_cache_string, CONST_STR_LEN("fam"))) { - srv->srvconf.stat_cache_engine = STAT_CACHE_ENGINE_FAM; + else if (buffer_eq_slen(stat_cache_string, CONST_STR_LEN("fam"))) + sc.stat_cache_engine = STAT_CACHE_ENGINE_FAM; #endif - } else if (buffer_is_equal_string(stat_cache_string, CONST_STR_LEN("disable"))) { - srv->srvconf.stat_cache_engine = STAT_CACHE_ENGINE_NONE; - } else { - log_error(srv->errh, __FILE__, __LINE__, - "server.stat-cache-engine can be one of \"disable\", \"simple\"," + else if (buffer_eq_slen(stat_cache_string, CONST_STR_LEN("disable"))) + sc.stat_cache_engine = STAT_CACHE_ENGINE_NONE; + else { + log_error(srv->errh, __FILE__, __LINE__, + "server.stat-cache-engine can be one of \"disable\", \"simple\"," #ifdef HAVE_FAM_H - " \"fam\"," + " \"fam\"," #endif - " but not: %s", stat_cache_string->ptr); - return -1; - } - return 0; -} - -#if defined(HAVE_XATTR) -static int stat_cache_attr_get(buffer *buf, char *name, const char *xattrname) { - int attrlen; - int ret; - - 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, const 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 -1; + " but not: %s", stat_cache_string->ptr); + return -1; + } + return 0; } -#endif -const buffer * stat_cache_mimetype_by_ext(const connection *con, const char *name, size_t nlen) +const buffer * stat_cache_mimetype_by_ext(const array * const mimetypes, const char * const name, const size_t nlen) { - const char *end = name + nlen; /*(end of string)*/ - const size_t used = con->conf.mimetypes->used; + const char * const end = name + nlen; /*(end of string)*/ + const uint32_t used = mimetypes->used; if (used < 16) { - for (size_t i = 0; i < used; ++i) { + for (uint32_t i = 0; i < used; ++i) { /* suffix match */ - const data_string *ds = (data_string *)con->conf.mimetypes->data[i]; + const data_string *ds = (data_string *)mimetypes->data[i]; const size_t klen = buffer_string_length(&ds->key); if (klen <= nlen && buffer_eq_icase_ssn(end-klen, ds->key.ptr, klen)) return &ds->value; @@ -659,107 +661,149 @@ const buffer * stat_cache_mimetype_by_ext(const connection *con, const char *nam s = name; } /* search for basename, then longest .ext2.ext1, then .ext1, then "" */ - ds = (const data_string *)array_get_element_klen(con->conf.mimetypes, s, end - s); + ds = (const data_string *)array_get_element_klen(mimetypes, s, end - s); if (NULL != ds) return &ds->value; while (++s < end) { while (*s != '.' && ++s != end) ; if (s == end) break; /* search ".ext" then "ext" */ - ds = (const data_string *)array_get_element_klen(con->conf.mimetypes, s, end - s); + ds = (const data_string *)array_get_element_klen(mimetypes, s, end - s); if (NULL != ds) return &ds->value; /* repeat search without leading '.' to handle situation where * admin configured mimetype.assign keys without leading '.' */ if (++s < end) { if (*s == '.') { --s; continue; } - ds = (const data_string *)array_get_element_klen(con->conf.mimetypes, s, end - s); + ds = (const data_string *)array_get_element_klen(mimetypes, s, end - s); if (NULL != ds) return &ds->value; } } /* search for ""; catchall */ - ds = (const data_string *)array_get_element_klen(con->conf.mimetypes, CONST_STR_LEN("")); + ds = (const data_string *)array_get_element_klen(mimetypes, CONST_STR_LEN("")); if (NULL != ds) return &ds->value; } return NULL; } -const buffer * stat_cache_content_type_get(const connection *con, const buffer *name, stat_cache_entry *sce) +#if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) + +const buffer * stat_cache_mimetype_by_xattr(const char * const name) +{ + return stat_cache_attr_get(name) ? &attrb : NULL; +} + +const buffer * stat_cache_content_type_get_by_xattr(stat_cache_entry *sce, const array *mimetypes, int use_xattr) { /*(invalid caching if user config has multiple, different * con->conf.mimetypes for same extension (not expected))*/ - if (!buffer_string_is_empty(sce->content_type)) return sce->content_type; - - if (S_ISREG(sce->st.st_mode)) { - /* determine mimetype */ - buffer_clear(sce->content_type); - #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) - if (con->conf.use_xattr) { - stat_cache_attr_get(sce->content_type, name->ptr, con->srv->srvconf.xattr_name); + if (!buffer_string_is_empty(&sce->content_type)) return &sce->content_type; + + if (!S_ISREG(sce->st.st_mode)) return NULL; + + /* cache mimetype */ + const buffer *mtype = + (use_xattr) ? stat_cache_mimetype_by_xattr(sce->name.ptr) : NULL; + if (NULL == mtype) + mtype = stat_cache_mimetype_by_ext(mimetypes,CONST_BUF_LEN(&sce->name)); + if (NULL != mtype) { + if (sce->content_type.size) { + buffer_copy_buffer(&sce->content_type, mtype); } - #endif - /* xattr did not set a content-type. ask the config */ - if (buffer_string_is_empty(sce->content_type)) { - const buffer *type = stat_cache_mimetype_by_ext(con, CONST_BUF_LEN(name)); - if (NULL != type) { - buffer_copy_buffer(sce->content_type, type); - } + else if (mtype == &attrb) { + sce->content_type.ptr = NULL; + buffer_copy_buffer(&sce->content_type, mtype); + } + else { + /*(copy pointers from mimetypes array; avoid allocation)*/ + sce->content_type.ptr = mtype->ptr; + sce->content_type.used = mtype->used; + /*(leave sce->content_type.size = 0 to flag not-allocated)*/ } - return sce->content_type; } + else + buffer_clear(&sce->content_type); - return NULL; + return &sce->content_type; } +#else + +const buffer * stat_cache_content_type_get_by_ext(stat_cache_entry *sce, const array *mimetypes) +{ + /*(invalid caching if user config has multiple, different + * con->conf.mimetypes for same extension (not expected))*/ + if (!buffer_string_is_empty(&sce->content_type)) return &sce->content_type; + + if (!S_ISREG(sce->st.st_mode)) return NULL; + + /* cache mimetype */ + const buffer * const mtype = + stat_cache_mimetype_by_ext(mimetypes, CONST_BUF_LEN(&sce->name)); + if (NULL != mtype) { + /*(copy pointers from mimetypes array; avoid allocation)*/ + sce->content_type.ptr = mtype->ptr; + sce->content_type.used = mtype->used; + /*(leave sce->content_type.size = 0 to flag not-allocated)*/ + } + else + buffer_clear(&sce->content_type); + + return &sce->content_type; +} + +#endif + const buffer * stat_cache_etag_get(stat_cache_entry *sce, int flags) { /*(invalid caching if user cfg has multiple, different con->conf.etag_flags * for same path (not expected, since etag flags should be by filesystem))*/ - if (!buffer_string_is_empty(sce->etag)) return sce->etag; + if (!buffer_string_is_empty(&sce->etag)) return &sce->etag; if (S_ISREG(sce->st.st_mode) || S_ISDIR(sce->st.st_mode)) { - etag_create(sce->etag, &sce->st, flags); - return sce->etag; + if (0 == flags) return NULL; + etag_create(&sce->etag, &sce->st, flags); + return &sce->etag; } return NULL; } -void stat_cache_update_entry(server *srv, const char *name, size_t len, +void stat_cache_update_entry(const char *name, size_t len, struct stat *st, buffer *etagb) { - if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_NONE) return; + if (sc.stat_cache_engine == STAT_CACHE_ENGINE_NONE) return; force_assert(0 != len); if (name[len-1] == '/') { if (0 == --len) len = 1; } - splay_tree **sptree = &srv->stat_cache->files; + splay_tree **sptree = &sc.files; stat_cache_entry *sce = stat_cache_sptree_find(sptree, name, len); - if (sce && buffer_is_equal_string(sce->name, name, len)) { + if (sce && buffer_is_equal_string(&sce->name, name, len)) { sce->stat_ts = log_epoch_secs; sce->st = *st; /* etagb might be NULL to clear etag (invalidate) */ - buffer_copy_string_len(sce->etag, CONST_BUF_LEN(etagb)); + buffer_copy_string_len(&sce->etag, CONST_BUF_LEN(etagb)); #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) - buffer_clear(sce->content_type); + buffer_clear(&sce->content_type); #endif } } -void stat_cache_delete_entry(server *srv, const char *name, size_t len) +void stat_cache_delete_entry(const char *name, size_t len) { - if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_NONE) return; + if (sc.stat_cache_engine == STAT_CACHE_ENGINE_NONE) return; force_assert(0 != len); if (name[len-1] == '/') { if (0 == --len) len = 1; } - splay_tree **sptree = &srv->stat_cache->files; + splay_tree **sptree = &sc.files; stat_cache_entry *sce = stat_cache_sptree_find(sptree, name, len); - if (sce && buffer_is_equal_string(sce->name, name, len)) { + if (sce && buffer_is_equal_string(&sce->name, name, len)) { stat_cache_entry_free(sce); *sptree = splaytree_delete(*sptree, (*sptree)->key); } } -void stat_cache_invalidate_entry(server *srv, const char *name, size_t len) +void stat_cache_invalidate_entry(const char *name, size_t len) { - splay_tree **sptree = &srv->stat_cache->files; + splay_tree **sptree = &sc.files; stat_cache_entry *sce = stat_cache_sptree_find(sptree, name, len); - if (sce && buffer_is_equal_string(sce->name, name, len)) { + if (sce && buffer_is_equal_string(&sce->name, name, len)) { sce->stat_ts = 0; #ifdef HAVE_FAM_H if (sce->fam_dir != NULL) { @@ -778,7 +822,7 @@ static void stat_cache_invalidate_dir_tree_walk(splay_tree *t, if (t->left) stat_cache_invalidate_dir_tree_walk(t->left, name, len); if (t->right) stat_cache_invalidate_dir_tree_walk(t->right, name, len); - buffer *b = ((stat_cache_entry *)t->data)->name; + buffer *b = &((stat_cache_entry *)t->data)->name; size_t blen = buffer_string_length(b); if (blen > len && b->ptr[len] == '/' && 0 == memcmp(b->ptr, name, len)) { stat_cache_entry *sce = t->data; @@ -790,10 +834,9 @@ static void stat_cache_invalidate_dir_tree_walk(splay_tree *t, } } -static void stat_cache_invalidate_dir_tree(server *srv, - const char *name, size_t len) +static void stat_cache_invalidate_dir_tree(const char *name, size_t len) { - splay_tree *sptree = srv->stat_cache->files; + splay_tree * const sptree = sc.files; if (sptree) stat_cache_invalidate_dir_tree_walk(sptree, name, len); } @@ -812,51 +855,52 @@ static void stat_cache_tag_dir_tree(splay_tree *t, const char *name, size_t len, if (t->right) stat_cache_tag_dir_tree(t->right, name, len, keys, ndx); if (*ndx == 8192) return; /*(must match num array entries in keys[])*/ - buffer *b = ((stat_cache_entry *)t->data)->name; + buffer *b = &((stat_cache_entry *)t->data)->name; size_t blen = buffer_string_length(b); if (blen > len && b->ptr[len] == '/' && 0 == memcmp(b->ptr, name, len)) keys[(*ndx)++] = t->key; } -static void stat_cache_prune_dir_tree(stat_cache * const sc, - const char *name, size_t len) +static void stat_cache_prune_dir_tree(const char *name, size_t len) { int max_ndx, i; int keys[8192]; /* 32k size on stack */ + splay_tree *sptree = sc.files; do { - if (!sc->files) return; + if (!sptree) return; max_ndx = 0; - stat_cache_tag_dir_tree(sc->files, name, len, keys, &max_ndx); + stat_cache_tag_dir_tree(sptree, name, len, keys, &max_ndx); for (i = 0; i < max_ndx; ++i) { const int ndx = keys[i]; - splay_tree *node = sc->files = splaytree_splay(sc->files, ndx); + splay_tree *node = sptree = splaytree_splay(sptree, ndx); if (node && node->key == ndx) { stat_cache_entry_free(node->data); - sc->files = splaytree_delete(sc->files, ndx); + sptree = splaytree_delete(sptree, ndx); } } } while (max_ndx == sizeof(keys)/sizeof(int)); + sc.files = sptree; } -static void stat_cache_delete_tree(server *srv, const char *name, size_t len) +static void stat_cache_delete_tree(const char *name, size_t len) { - stat_cache_delete_entry(srv, name, len); - stat_cache_prune_dir_tree(srv->stat_cache, name, len); + stat_cache_delete_entry(name, len); + stat_cache_prune_dir_tree(name, len); } -void stat_cache_delete_dir(server *srv, const char *name, size_t len) +void stat_cache_delete_dir(const char *name, size_t len) { force_assert(0 != len); if (name[len-1] == '/') { if (0 == --len) len = 1; } - stat_cache_delete_tree(srv, name, len); + stat_cache_delete_tree(name, len); #ifdef HAVE_FAM_H - if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { - splay_tree **sptree = &srv->stat_cache->scf->dirs; + if (sc.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { + splay_tree **sptree = &sc.scf->dirs; fam_dir_entry *fam_dir = stat_cache_sptree_find(sptree, name, len); if (fam_dir && buffer_is_equal_string(fam_dir->name, name, len)) fam_dir_invalidate_node(fam_dir); if (*sptree) fam_dir_invalidate_tree(*sptree, name, len); - fam_dir_periodic_cleanup(srv); + fam_dir_periodic_cleanup(); } #endif } @@ -870,14 +914,11 @@ void stat_cache_delete_dir(server *srv, const char *name, size_t len) * - HANDLER_ERROR on stat() failed -> see errno for problem */ -handler_t stat_cache_get_entry(connection *con, buffer *name, stat_cache_entry **ret_sce) { +stat_cache_entry * stat_cache_get_entry(const buffer *name) { stat_cache_entry *sce = NULL; - stat_cache *sc; struct stat st; int file_ndx; - *ret_sce = NULL; - /* consistency: ensure lookup name does not end in '/' unless root "/" * (but use full path given with stat(), even with trailing '/') */ int final_slash = 0; @@ -887,41 +928,37 @@ handler_t stat_cache_get_entry(connection *con, buffer *name, stat_cache_entry * /* Note: paths are expected to be normalized before calling stat_cache, * e.g. without repeated '/' */ - if (name->ptr[0] != '/') return HANDLER_ERROR; + if (name->ptr[0] != '/') return NULL; /* * check if the directory for this file has changed */ - server * const srv = con->srv; const time_t cur_ts = log_epoch_secs; - sc = srv->stat_cache; - file_ndx = hashme(name->ptr, len); - sc->files = splaytree_splay(sc->files, file_ndx); + splay_tree * const sptree = sc.files = splaytree_splay(sc.files, file_ndx); - if (sc->files && (sc->files->key == file_ndx)) { + if (sptree && (sptree->key == file_ndx)) { /* we have seen this file already and * don't stat() it again in the same second */ - sce = sc->files->data; + sce = sptree->data; /* check if the name is the same, we might have a collision */ - if (buffer_is_equal_string(sce->name, name->ptr, len)) { - if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_SIMPLE) { + if (buffer_is_equal_string(&sce->name, name->ptr, len)) { + if (sc.stat_cache_engine == STAT_CACHE_ENGINE_SIMPLE) { if (sce->stat_ts == cur_ts) { if (final_slash && !S_ISDIR(sce->st.st_mode)) { errno = ENOTDIR; - return HANDLER_ERROR; + return NULL; } - *ret_sce = sce; - return HANDLER_GO_ON; + return sce; } } #ifdef HAVE_FAM_H - else if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM + else if (sc.stat_cache_engine == STAT_CACHE_ENGINE_FAM && sce->fam_dir) { /* entry is in monitored dir */ /* re-stat() periodically, even if monitoring for changes * (due to limitations in stat_cache.c use of FAM) @@ -929,10 +966,9 @@ handler_t stat_cache_get_entry(connection *con, buffer *name, stat_cache_entry * if (cur_ts - sce->stat_ts < 16) { if (final_slash && !S_ISDIR(sce->st.st_mode)) { errno = ENOTDIR; - return HANDLER_ERROR; + return NULL; } - *ret_sce = sce; - return HANDLER_GO_ON; + return sce; } } #endif @@ -943,36 +979,36 @@ handler_t stat_cache_get_entry(connection *con, buffer *name, stat_cache_entry * } if (-1 == stat(name->ptr, &st)) { - return HANDLER_ERROR; + return NULL; } if (S_ISREG(st.st_mode)) { /* fix broken stat/open for symlinks to reg files with appended slash on freebsd,osx */ if (name->ptr[buffer_string_length(name) - 1] == '/') { errno = ENOTDIR; - return HANDLER_ERROR; + return NULL; } } if (NULL == sce) { sce = stat_cache_entry_init(); - buffer_copy_string_len(sce->name, name->ptr, len); + buffer_copy_string_len(&sce->name, name->ptr, len); /* already splayed file_ndx */ - if ((NULL != sc->files) && (sc->files->key == file_ndx)) { + if (NULL != sptree && sptree->key == file_ndx) { /* hash collision: replace old entry */ - stat_cache_entry_free(sc->files->data); - sc->files->data = sce; + stat_cache_entry_free(sptree->data); + sptree->data = sce; } else { - sc->files = splaytree_insert(sc->files, file_ndx, sce); + sc.files = splaytree_insert(sptree, file_ndx, sce); } } else { - buffer_clear(sce->etag); + buffer_clear(&sce->etag); #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) - buffer_clear(sce->content_type); + buffer_clear(&sce->content_type); #endif } @@ -980,10 +1016,10 @@ handler_t stat_cache_get_entry(connection *con, buffer *name, stat_cache_entry * sce->st = st; /*(copy prior to calling fam_dir_monitor())*/ #ifdef HAVE_FAM_H - if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { + if (sc.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { if (sce->fam_dir) --((fam_dir_entry *)sce->fam_dir)->refcnt; sce->fam_dir = - fam_dir_monitor(srv, sc->scf, CONST_BUF_LEN(name), &st); + fam_dir_monitor(sc.scf, CONST_BUF_LEN(name), &st); #if 0 /*(performed below)*/ if (NULL != sce->fam_dir) { /*(may have been invalidated by dir change)*/ @@ -994,12 +1030,10 @@ handler_t stat_cache_get_entry(connection *con, buffer *name, stat_cache_entry * #endif sce->stat_ts = cur_ts; - *ret_sce = sce; - - return HANDLER_GO_ON; + return sce; } -int stat_cache_path_contains_symlink(connection *con, const buffer *name) { +int stat_cache_path_contains_symlink(const buffer *name, log_error_st *errh) { /* caller should check for symlinks only if we should block symlinks. */ /* catch the obvious symlinks @@ -1035,8 +1069,7 @@ int stat_cache_path_contains_symlink(connection *con, const buffer *name) { if (S_ISLNK(st.st_mode)) return 1; } else { - log_perror(con->conf.errh, __FILE__, __LINE__, - "lstat failed for: %s", buf); + log_perror(errh, __FILE__, __LINE__, "lstat failed for: %s", buf); return -1; } } while ((s_cur = strrchr(buf, '/')) != buf); @@ -1068,7 +1101,7 @@ int stat_cache_open_rdonly_fstat (const buffer *name, struct stat *st, int symli * and remove them in a second loop */ -static void stat_cache_tag_old_entries(splay_tree * const t, int * const keys, size_t * const ndx, const time_t max_age, const time_t cur_ts) { +static void stat_cache_tag_old_entries(splay_tree * const t, int * const keys, uint32_t * const ndx, const time_t max_age, const time_t cur_ts) { if (!t) return; stat_cache_tag_old_entries(t->left, keys, ndx, max_age, cur_ts); @@ -1081,43 +1114,39 @@ static void stat_cache_tag_old_entries(splay_tree * const t, int * const keys, s } } -static void stat_cache_periodic_cleanup(stat_cache * const sc, const time_t max_age, const time_t cur_ts) { - size_t max_ndx = 0, i; - int *keys; - - if (!sc->files) return; +static void stat_cache_periodic_cleanup(const time_t max_age, const time_t cur_ts) { + splay_tree *sptree = sc.files; + if (!sptree) return; - keys = calloc(1, sizeof(int) * sc->files->size); + int * const keys = calloc(1, sizeof(int) * sptree->size); force_assert(NULL != keys); - stat_cache_tag_old_entries(sc->files, keys, &max_ndx, max_age, cur_ts); + uint32_t max_ndx = 0; + stat_cache_tag_old_entries(sptree, keys, &max_ndx, max_age, cur_ts); - for (i = 0; i < max_ndx; i++) { + for (uint32_t i = 0; i < max_ndx; ++i) { int ndx = keys[i]; - splay_tree *node; - - sc->files = splaytree_splay(sc->files, ndx); - - node = sc->files; - - if (node && (node->key == ndx)) { - stat_cache_entry_free(node->data); - sc->files = splaytree_delete(sc->files, ndx); + sptree = splaytree_splay(sptree, ndx); + if (sptree && sptree->key == ndx) { + stat_cache_entry_free(sptree->data); + sptree = splaytree_delete(sptree, ndx); } } + sc.files = sptree; + free(keys); } -int stat_cache_trigger_cleanup(server *srv) { +void stat_cache_trigger_cleanup(void) { time_t max_age = 2; #ifdef HAVE_FAM_H - if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { - if (log_epoch_secs & 0x1F) return 0; + if (STAT_CACHE_ENGINE_FAM == sc.stat_cache_engine) { + if (log_epoch_secs & 0x1F) return; /* once every 32 seconds (0x1F == 31) */ max_age = 32; - fam_dir_periodic_cleanup(srv); + fam_dir_periodic_cleanup(); /* By doing this before stat_cache_periodic_cleanup(), * entries used within the next max_age secs will remain * monitored, instead of effectively flushing and @@ -1125,7 +1154,5 @@ int stat_cache_trigger_cleanup(server *srv) { } #endif - stat_cache_periodic_cleanup(srv->stat_cache, max_age, log_epoch_secs); - - return 0; + stat_cache_periodic_cleanup(max_age, log_epoch_secs); } diff --git a/src/stat_cache.h b/src/stat_cache.h index aad27e7c..46c0bf12 100644 --- a/src/stat_cache.h +++ b/src/stat_cache.h @@ -4,48 +4,53 @@ #include "base_decls.h" #include "buffer.h" +#include "array.h" #include "etag.h" #include #include #include -struct stat_cache; /* declaration */ - typedef struct { - buffer *name; - buffer *etag; - - struct stat st; - - time_t stat_ts; - + buffer name; + time_t stat_ts; #ifdef HAVE_FAM_H - void *fam_dir; + void *fam_dir; #endif - - buffer *content_type; + buffer etag; + buffer content_type; + struct stat st; } stat_cache_entry; __attribute_cold__ int stat_cache_choose_engine (server *srv, const buffer *stat_cache_string); __attribute_cold__ -struct stat_cache *stat_cache_init(server *srv); +int stat_cache_init(server *srv); __attribute_cold__ -void stat_cache_free(struct stat_cache *fc); +void stat_cache_free(void); -const buffer * stat_cache_mimetype_by_ext(const connection *con, const char *name, size_t nlen); -const buffer * stat_cache_content_type_get(const connection *con, const buffer *name, stat_cache_entry *sce); +__attribute_cold__ +void stat_cache_xattrname (const char *name); + +const buffer * stat_cache_mimetype_by_ext(const array *mimetypes, const char *name, size_t nlen); +#if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) +const buffer * stat_cache_mimetype_by_xattr(const char *name); +const buffer * stat_cache_content_type_get_by_xattr(stat_cache_entry *sce, const array *mimetypes, int use_xattr); +#define stat_cache_content_type_get(con, sce) stat_cache_content_type_get_by_xattr((sce), (con)->conf.mimetypes, (con)->conf.use_xattr) +#else +const buffer * stat_cache_content_type_get_by_ext(stat_cache_entry *sce, const array *mimetypes); +#define stat_cache_content_type_get(con, sce) stat_cache_content_type_get_by_ext((sce), (con)->conf.mimetypes) +#endif const buffer * stat_cache_etag_get(stat_cache_entry *sce, int flags); -void stat_cache_update_entry(server *srv, const char *name, size_t len, struct stat *st, buffer *etagb); -void stat_cache_delete_entry(server *srv, const char *name, size_t len); -void stat_cache_delete_dir(server *srv, const char *name, size_t len); -void stat_cache_invalidate_entry(server *srv, const char *name, size_t len); -handler_t stat_cache_get_entry(connection *con, buffer *name, stat_cache_entry **sce); -int stat_cache_path_contains_symlink(connection *con, const buffer *name); +void stat_cache_update_entry(const char *name, size_t len, struct stat *st, buffer *etagb); +void stat_cache_delete_entry(const char *name, size_t len); +void stat_cache_delete_dir(const char *name, size_t len); +void stat_cache_invalidate_entry(const char *name, size_t len); +stat_cache_entry * stat_cache_get_entry(const buffer *name); +int stat_cache_path_contains_symlink(const buffer *name, log_error_st *errh); int stat_cache_open_rdonly_fstat (const buffer *name, struct stat *st, int symlinks); -int stat_cache_trigger_cleanup(server *srv); +void stat_cache_trigger_cleanup(void); #endif diff --git a/src/t/test_mod_evhost.c b/src/t/test_mod_evhost.c index b79df785..67f592c9 100644 --- a/src/t/test_mod_evhost.c +++ b/src/t/test_mod_evhost.c @@ -75,9 +75,7 @@ int main (void) { * stub functions */ -handler_t stat_cache_get_entry(connection *con, buffer *name, stat_cache_entry **sce) { - UNUSED(con); +stat_cache_entry * stat_cache_get_entry(const buffer *name) { UNUSED(name); - UNUSED(sce); - return HANDLER_GO_ON; + return NULL; } diff --git a/src/t/test_mod_simple_vhost.c b/src/t/test_mod_simple_vhost.c index 14bc318e..65e4b3c4 100644 --- a/src/t/test_mod_simple_vhost.c +++ b/src/t/test_mod_simple_vhost.c @@ -43,10 +43,8 @@ int main (void) { * stub functions */ -handler_t stat_cache_get_entry(connection *con, buffer *name, stat_cache_entry **sce) { - UNUSED(con); +stat_cache_entry * stat_cache_get_entry(const buffer *name) { UNUSED(name); - UNUSED(sce); - return HANDLER_GO_ON; + return NULL; }