Roderich Schupp <roderich.sch...@gmail.com> writes: > The patch needs to #include libsvn_subr/config_impl.h > in order to gain access to svn_config_t.pool: > the cache (apr_hash_t itself, keys and values) must be allocated from > the same pool as svn_config_t so that they have the same lifespan.
It would probably better to introduce a new API something like: apr_pool_t *svn_config__pool(svn_config_t *); declared in subversion/include/private/. However I don't fully understand the logic governing the lifetime. The cache is part of svn_authz_t so I would expect it's lifetime to be that of svn_authz_t. If that's the case you add a pool member to svn_authz_t and use that. > Index: subversion/libsvn_repos/authz.c > =================================================================== > --- subversion/libsvn_repos/authz.c (revision 1508093) > +++ subversion/libsvn_repos/authz.c (working copy) > @@ -37,6 +37,9 @@ > #include "private/svn_fspath.h" > #include "repos.h" > > +/* we need just svn_config_t.pool */ > +#include "../libsvn_subr/config_impl.h" > + > > /*** Structures. ***/ > > @@ -44,7 +47,7 @@ > lookup. */ > struct authz_lookup_baton { > /* The authz configuration. */ > - svn_config_t *config; > + svn_authz_t *authz; > > /* The user to authorize. */ > const char *user; > @@ -76,14 +79,24 @@ > enumerator, if any. */ > }; > > +svn_boolean_t authz_use_cache = TRUE; /* FIXME devel only */ > + I assume that is temporary, what is the long-term plan? Remove it completely? > /* Currently this structure is just a wrapper around a > svn_config_t. */ That comment is out-of-date. > struct svn_authz_t > { > svn_config_t *cfg; > + const char *cached_user; > + apr_hash_t *cache; Add a comment describing what types are used as keys and values in the hash. > }; > > +struct access_cache_t > +{ > + svn_repos_authz_access_t allow; > + svn_repos_authz_access_t deny; > +}; > > + > > /*** Checking access. ***/ > > @@ -241,10 +254,10 @@ > */ > if (rule_match_string[0] == '@') > return authz_group_contains_user( > - b->config, &rule_match_string[1], b->user, pool); > + b->authz->cfg, &rule_match_string[1], b->user, pool); > else if (rule_match_string[0] == '&') > return authz_alias_is_user( > - b->config, &rule_match_string[1], b->user, pool); > + b->authz->cfg, &rule_match_string[1], b->user, pool); > else > return (strcmp(b->user, rule_match_string) == 0); > } > @@ -295,6 +308,62 @@ > } > > > +/* Wrapper for svn_config_enumerate2(..., authz_parse_line, ...) > + * that manages the allow/deny per-section cache. > + */ > +static void > +authz_lookup_cached(const char *path, > + struct authz_lookup_baton *baton, > + apr_pool_t *pool) > +{ > + struct svn_config_t *cfg = baton->authz->cfg; > + struct apr_hash_t *cache = baton->authz->cache; > + struct access_cache_t *access; > + svn_repos_authz_access_t saved_allow, saved_deny; > + > + if (!cache) > + { > + svn_config_enumerate2(cfg, path, > + authz_parse_line, baton, pool); > + return; > + } > + > + /* if section [path] doesn't exist, then we're done */ > + if (!svn_config_has_section(cfg, path)) > + return; > + > + /* if we already know allow/deny for section [path] ... */ > + access = apr_hash_get(cache, path, APR_HASH_KEY_STRING); svn_hash_gets > + if (access) > + { > + /* ... combine baton with cached allow/deny and we're done */ > + baton->allow |= access->allow; > + baton->deny |= access->deny; > + return; > + } > + > + /* reuse baton: save baton's allow/deny information and reset it */ > + saved_allow = baton->allow; > + saved_deny = baton->deny; > + baton->allow = baton->deny = svn_authz_none; > + > + svn_config_enumerate2(cfg, path, authz_parse_line, baton, pool); > + > + /* cache the result > + * Note: Use baton->config->pool instead of pool as the cache has > + * the same lifetime as baton->config. > + */ > + access = apr_palloc(cfg->pool, sizeof(*access)); > + access->allow = baton->allow; > + access->deny = baton->deny; > + apr_hash_set(cache, apr_pstrdup(cfg->pool, path), APR_HASH_KEY_STRING, > access); svn_hash_sets > + > + /* combine the result with saved values */ > + baton->allow |= saved_allow; > + baton->deny |= saved_deny; > +} > + > + > /* Callback to parse a section and update the authz_baton if the > * section denies access to the subtree the baton describes. > */ > @@ -310,9 +379,8 @@ > return TRUE; > > /* Work out what this section grants. */ > - b->allow = b->deny = 0; > - svn_config_enumerate2(b->config, section_name, > - authz_parse_line, b, pool); > + b->allow = b->deny = svn_authz_none; > + authz_lookup_cached(section_name, b, pool); > > /* Has the section explicitly determined an access? */ > conclusive = authz_access_is_determined(b->allow, b->deny, > @@ -338,7 +406,7 @@ > * successfully determined. > */ > static svn_boolean_t > -authz_get_path_access(svn_config_t *cfg, const char *repos_name, > +authz_get_path_access(svn_authz_t *authz, const char *repos_name, > const char *path, const char *user, > svn_repos_authz_access_t required_access, > svn_boolean_t *access_granted, > @@ -346,14 +414,13 @@ > { > const char *qualified_path; > struct authz_lookup_baton baton = { 0 }; > - > - baton.config = cfg; > + > + baton.authz = authz; > baton.user = user; > > /* Try to locate a repository-specific block first. */ > qualified_path = apr_pstrcat(pool, repos_name, ":", path, (char *)NULL); > - svn_config_enumerate2(cfg, qualified_path, > - authz_parse_line, &baton, pool); > + authz_lookup_cached(qualified_path, &baton, pool); > > *access_granted = authz_access_is_granted(baton.allow, baton.deny, > required_access); > @@ -364,7 +431,7 @@ > return TRUE; > > /* No repository specific rule, try pan-repository rules. */ > - svn_config_enumerate2(cfg, path, authz_parse_line, &baton, pool); > + authz_lookup_cached(path, &baton, pool); > > *access_granted = authz_access_is_granted(baton.allow, baton.deny, > required_access); > @@ -382,14 +449,14 @@ > * searched, return the updated authorization status. > */ > static svn_boolean_t > -authz_get_tree_access(svn_config_t *cfg, const char *repos_name, > +authz_get_tree_access(svn_authz_t *authz, const char *repos_name, > const char *path, const char *user, > svn_repos_authz_access_t required_access, > apr_pool_t *pool) > { > struct authz_lookup_baton baton = { 0 }; > > - baton.config = cfg; > + baton.authz = authz; > baton.user = user; > baton.required_access = required_access; > baton.repos_path = path; > @@ -398,7 +465,7 @@ > /* Default to access granted if no rules say otherwise. */ > baton.access = TRUE; > > - svn_config_enumerate_sections2(cfg, authz_parse_section, > + svn_config_enumerate_sections2(authz->cfg, authz_parse_section, > &baton, pool); > > return baton.access; > @@ -420,9 +487,8 @@ > strlen(b->qualified_repos_path)) == 0) > { > b->allow = b->deny = svn_authz_none; > - > - svn_config_enumerate2(b->config, section_name, > - authz_parse_line, baton, pool); > + > + authz_lookup_cached(section_name, baton, pool); > b->access = authz_access_is_granted(b->allow, b->deny, > b->required_access); > > @@ -440,14 +506,14 @@ > * to any path within the REPOSITORY. Return TRUE if so. Use POOL > * for temporary allocations. */ > static svn_boolean_t > -authz_get_any_access(svn_config_t *cfg, const char *repos_name, > +authz_get_any_access(svn_authz_t *authz, const char *repos_name, > const char *user, > svn_repos_authz_access_t required_access, > apr_pool_t *pool) > { > struct authz_lookup_baton baton = { 0 }; > > - baton.config = cfg; > + baton.authz = authz; > baton.user = user; > baton.required_access = required_access; > baton.access = FALSE; /* Deny access by default. */ > @@ -460,7 +526,7 @@ > * may not always have). So we end up enumerating the sections in > * the authz CFG and stop on the first match with some access for > * this user. */ > - svn_config_enumerate_sections2(cfg, authz_get_any_access_parser_cb, > + svn_config_enumerate_sections2(authz->cfg, authz_get_any_access_parser_cb, > &baton, pool); > > /* If walking the configuration was inconclusive, deny access. */ > @@ -754,6 +820,9 @@ > { > struct authz_validate_baton baton = { 0 }; > > + authz->cached_user = NULL; /* FIXME does apr_palloc zero the alloced > stuff? */ > + authz->cache = NULL; /* FIXME does apr_palloc zero the alloced > stuff? */ apr_palloc doesn't zero, apr_pcalloc does. > + > baton.err = SVN_NO_ERROR; > baton.config = authz->cfg; > > @@ -1027,13 +1096,40 @@ > { > const char *current_path; > > + /* Note: Below we use authz->cfg->pool instead of pool as the cache must > + * have the same lifetime as authz->cfg. > + */ > + if (authz_use_cache) /* FIXME devel only */ > + { > + /* Check whether user is still the same as authz->cached_user; > + * otherwise blow away authz->cache and remember the new user. > + */ > + if (!( user && authz->cached_user > + && strcmp(user, authz->cached_user) == 0 )) We don't usually put whitespace there if (!(user && authz->cached_user && strcmp(user, authz->cached_user) == 0)) > + { > + if (user) > + { > + authz->cache = NULL; > + authz->cached_user = apr_pstrdup(authz->cfg->pool, user); > + } > + else if (authz->cached_user) > + { > + authz->cache = NULL; > + authz->cached_user = NULL; > + } > + } > + > + if (!authz->cache) > + authz->cache = apr_hash_make(authz->cfg->pool); > + } > + > if (!repos_name) > repos_name = ""; > > /* If PATH is NULL, check if the user has *any* access. */ > if (!path) > { > - *access_granted = authz_get_any_access(authz->cfg, repos_name, > + *access_granted = authz_get_any_access(authz, repos_name, > user, required_access, pool); > return SVN_NO_ERROR; > } > @@ -1045,11 +1141,9 @@ > path = svn_fspath__canonicalize(path, pool); > current_path = path; > > - while (!authz_get_path_access(authz->cfg, repos_name, > + while (!authz_get_path_access(authz, repos_name, > current_path, user, > - required_access, > - access_granted, > - pool)) > + required_access, access_granted, pool)) > { > /* Stop if the loop hits the repository root with no > results. */ > @@ -1068,7 +1162,7 @@ > the entire authz config to see whether any child paths are denied > to the requested user. */ > if (*access_granted && (required_access & svn_authz_recursive)) > - *access_granted = authz_get_tree_access(authz->cfg, repos_name, path, > + *access_granted = authz_get_tree_access(authz, repos_name, path, > user, required_access, pool); > > return SVN_NO_ERROR; > -- Philip Martin | Subversion Committer WANdisco | Non-Stop Data