Hi,

revised patch attached, hopefully addressing all concerns.

No more breaking encapsulation. We simply allocate svn_authz_t from the
same pool
that svn_config_t is allocated from and remember this pool in a new
svn_authz_t
member "pool".l Create a function authz_create() for that; it uses
apr_pcalloc
so that we don't have to explicitly zero members "cache" and "cached_user"
any more.

Cheers, Roderich
Index: subversion/libsvn_repos/authz.c
===================================================================
--- subversion/libsvn_repos/authz.c     (revision 1511246)
+++ subversion/libsvn_repos/authz.c     (working copy)
@@ -45,7 +45,7 @@
    lookup. */
 struct authz_lookup_baton {
   /* The authz configuration. */
-  svn_config_t *config;
+  svn_authz_t *authz;
 
   /* The user to authorize. */
   const char *user;
@@ -77,14 +77,26 @@
                            enumerator, if any. */
 };
 
-/* Currently this structure is just a wrapper around a
-   svn_config_t. */
+/* Information needed for authz lookups */
 struct svn_authz_t
 {
-  svn_config_t *cfg;
+  svn_config_t *cfg;    /* The configuration file where to look up 
+                           authorizations. */
+  apr_hash_t *cache;    /* Cache of previous lookup results (w.r.t. user 
+                           CACHED_USER). A hash table whose keys are paths
+                           and whose values are access_cache_t *. */
+  const char *cached_user;  /* User for which lookups are cached. */
+  apr_pool_t *pool;     /* The pool this struct was allocated in. */
 };
 
+/* Cached authz information */
+struct access_cache_t
+{
+  svn_repos_authz_access_t allow;
+  svn_repos_authz_access_t deny;
+};
 
+
 
 /*** Checking access. ***/
 
@@ -242,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);
 }
@@ -296,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 = svn_hash_gets(cache, path);
+  if (access)
+    {
+      /* ... combine baton with the cached allow/deny and we're done. */
+      baton->allow |= access->allow;
+      baton->deny |= access->deny;
+      return;
+    }
+
+  /* Reuse baton: save baton's allow/deny 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->authz->pool instead of pool as the cache must have
+   * the same lifetime as baton->authz.
+   */
+  access = apr_palloc(baton->authz->pool, sizeof(*access));
+  access->allow = baton->allow;
+  access->deny = baton->deny;
+  svn_hash_sets(cache, apr_pstrdup(baton->authz->pool, path), access);
+
+  /* Combine the result with the saved allow/deny. */
+  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.
  */
@@ -311,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,
@@ -339,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,
@@ -347,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);
@@ -365,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);
@@ -383,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;
@@ -399,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;
@@ -421,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);
 
@@ -441,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. */
@@ -461,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. */
@@ -934,12 +999,21 @@
   return SVN_NO_ERROR;
 }
 
+/* Allocate a svn_authz_t in POOL and remember it in svn_authz_t->pool. */
+static svn_authz_t *
+authz_create(apr_pool_t *pool)
+{
+  svn_authz_t *authz = apr_pcalloc(pool, sizeof(*authz));
+  authz->pool = pool;
+  return authz;
+}
+
 svn_error_t *
 svn_repos__authz_read(svn_authz_t **authz_p, const char *path,
                       const char *groups_path, svn_boolean_t must_exist,
                       svn_boolean_t accept_urls, apr_pool_t *pool)
 {
-  svn_authz_t *authz = apr_palloc(pool, sizeof(*authz));
+  svn_authz_t *authz = authz_create(pool);
 
   /* Load the authz file */
   if (accept_urls)
@@ -996,7 +1070,7 @@
 svn_repos_authz_parse(svn_authz_t **authz_p, svn_stream_t *stream,
                       svn_stream_t *groups_stream, apr_pool_t *pool)
 {
-  svn_authz_t *authz = apr_palloc(pool, sizeof(*authz));
+  svn_authz_t *authz = authz_create(pool);
 
   /* Parse the authz stream */
   SVN_ERR(svn_config_parse(&authz->cfg, stream, TRUE, TRUE, pool));
@@ -1028,13 +1102,36 @@
 {
   const char *current_path;
 
+  /* Check whether USER is still the same as authz->cached_user;
+   * otherwise blow away authz->cache and remember the new USER.
+   * Create authz->cache if it doesn't exist yet.
+   * Note: Use authz->pool instead of pool as the cache must have the 
+   * same lifetime as authz.
+   */
+  if (!(user && authz->cached_user 
+        && strcmp(user, authz->cached_user) == 0))
+    {
+      if (user)
+        {
+          authz->cache = NULL;
+          authz->cached_user = apr_pstrdup(authz->pool, user);
+        }
+      else if (authz->cached_user)
+        {
+          authz->cache = NULL;
+          authz->cached_user = NULL;
+        }
+    }
+  if (!authz->cache)
+      authz->cache = apr_hash_make(authz->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;
     }
@@ -1046,11 +1143,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. */
@@ -1069,7 +1164,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;

Reply via email to