You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2013/10/16 00:57:09 UTC

svn commit: r1532583 [8/10] - in /subversion/branches/fsfs-improvements: ./ build/ac-macros/ build/generator/ build/generator/templates/ build/win32/ contrib/client-side/emacs/ notes/ subversion/bindings/javahl/native/ subversion/bindings/javahl/src/or...

Modified: subversion/branches/fsfs-improvements/subversion/svnserve/serve.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/svnserve/serve.c?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/svnserve/serve.c (original)
+++ subversion/branches/fsfs-improvements/subversion/svnserve/serve.c Tue Oct 15 22:57:03 2013
@@ -61,6 +61,7 @@
 #endif
 
 #include "server.h"
+#include "logger.h"
 
 typedef struct commit_callback_baton_t {
   apr_pool_t *pool;
@@ -104,81 +105,23 @@ typedef struct authz_baton_t {
   svn_ra_svn_conn_t *conn;
 } authz_baton_t;
 
-/* Write LEN bytes of ERRSTR to LOG_FILE with svn_io_file_write(). */
-static svn_error_t *
-log_write(apr_file_t *log_file, const char *errstr, apr_size_t len,
-          apr_pool_t *pool)
-{
-  return svn_io_file_write(log_file, errstr, &len, pool);
-}
-
-void
-log_error(svn_error_t *err, apr_file_t *log_file, const char *remote_host,
-          const char *user, const char *repos, apr_pool_t *pool)
-{
-  const char *timestr, *continuation;
-  char errbuf[256];
-  /* 8192 from MAX_STRING_LEN in from httpd-2.2.4/include/httpd.h */
-  char errstr[8192];
-
-  if (err == SVN_NO_ERROR)
-    return;
-
-  if (log_file == NULL)
-    return;
-
-  timestr = svn_time_to_cstring(apr_time_now(), pool);
-  remote_host = (remote_host ? remote_host : "-");
-  user = (user ? user : "-");
-  repos = (repos ? repos : "-");
-
-  continuation = "";
-  while (err != NULL)
-    {
-      const char *message = svn_err_best_message(err, errbuf, sizeof(errbuf));
-      /* based on httpd-2.2.4/server/log.c:log_error_core */
-      apr_size_t len = apr_snprintf(errstr, sizeof(errstr),
-                                    "%" APR_PID_T_FMT
-                                    " %s %s %s %s ERR%s %s %ld %d ",
-                                    getpid(), timestr, remote_host, user,
-                                    repos, continuation,
-                                    err->file ? err->file : "-", err->line,
-                                    err->apr_err);
-
-      len += escape_errorlog_item(errstr + len, message,
-                                  sizeof(errstr) - len);
-      /* Truncate for the terminator (as apr_snprintf does) */
-      if (len > sizeof(errstr) - sizeof(APR_EOL_STR)) {
-        len = sizeof(errstr) - sizeof(APR_EOL_STR);
-      }
-      strcpy(errstr + len, APR_EOL_STR);
-      len += strlen(APR_EOL_STR);
-      svn_error_clear(log_write(log_file, errstr, len, pool));
-
-      continuation = "-";
-      err = err->child;
-    }
-}
-
-/* Call log_error with log_file, remote_host, user, and repos
-   arguments from SERVER and CONN. */
+/* svn_error_create() a new error, log_server_error() it, and
+   return it. */
 static void
-log_server_error(svn_error_t *err, server_baton_t *server,
-                 svn_ra_svn_conn_t *conn, apr_pool_t *pool)
+log_error(svn_error_t *err, server_baton_t *server)
 {
-  log_error(err, server->log_file, svn_ra_svn_conn_remote_host(conn),
-            server->user, server->repos_name, pool);
+  logger__log_error(server->logger, err, server->repository,
+                    server->client_info);
 }
 
 /* svn_error_create() a new error, log_server_error() it, and
    return it. */
 static svn_error_t *
 error_create_and_log(apr_status_t apr_err, svn_error_t *child,
-                     const char *message, server_baton_t *server,
-                     svn_ra_svn_conn_t *conn, apr_pool_t *pool)
+                     const char *message, server_baton_t *server)
 {
   svn_error_t *err = svn_error_create(apr_err, child, message);
-  log_server_error(err, server, conn, pool);
+  log_error(err, server);
   return err;
 }
 
@@ -190,7 +133,7 @@ log_fail_and_flush(svn_error_t *err, ser
 {
   svn_error_t *io_err;
 
-  log_server_error(err, server, conn, pool);
+  log_error(err, server);
   io_err = svn_ra_svn__write_cmd_failure(conn, pool, err);
   svn_error_clear(err);
   SVN_ERR(io_err);
@@ -207,7 +150,7 @@ static svn_error_t *log_command(server_b
   va_list ap;
   apr_size_t nbytes;
 
-  if (b->log_file == NULL)
+  if (b->logger == NULL)
     return SVN_NO_ERROR;
 
   remote_host = svn_ra_svn_conn_remote_host(conn);
@@ -221,10 +164,11 @@ static svn_error_t *log_command(server_b
                       " %s %s %s %s %s" APR_EOL_STR,
                       getpid(), timestr,
                       (remote_host ? remote_host : "-"),
-                      (b->user ? b->user : "-"), b->repos_name, log);
+                      (b->client_info->user ? b->client_info->user : "-"),
+                      b->repository->repos_name, log);
   nbytes = strlen(line);
 
-  return log_write(b->log_file, line, nbytes, pool);
+  return logger__write(b->logger, line, nbytes);
 }
 
 /* Log an authz failure */
@@ -232,56 +176,57 @@ static svn_error_t *
 log_authz_denied(const char *path,
                  svn_repos_authz_access_t required,
                  server_baton_t *b,
-                 svn_ra_svn_conn_t *conn,
                  apr_pool_t *pool)
 {
   const char *timestr, *remote_host, *line;
 
-  if (b->log_file == NULL)
+  if (!b->logger)
     return SVN_NO_ERROR;
 
-  if (!b->user)
+  if (!b->client_info || !b->client_info->user)
     return SVN_NO_ERROR;
 
   timestr = svn_time_to_cstring(apr_time_now(), pool);
-  remote_host = svn_ra_svn_conn_remote_host(conn);
+  remote_host = b->client_info->remote_host;
 
   line = apr_psprintf(pool, "%" APR_PID_T_FMT
                       " %s %s %s %s Authorization Failed %s%s %s" APR_EOL_STR,
                       getpid(), timestr,
                       (remote_host ? remote_host : "-"),
-                      (b->user ? b->user : "-"),
-                      b->repos_name,
+                      b->client_info->user,
+                      b->repository->repos_name,
                       (required & svn_authz_recursive ? "recursive " : ""),
                       (required & svn_authz_write ? "write" : "read"),
                       (path && path[0] ? path : "/"));
 
-  return log_write(b->log_file, line, strlen(line), pool);
+  return logger__write(b->logger, line, strlen(line));
 }
 
-
-svn_error_t *load_pwdb_config(server_baton_t *server,
-                              svn_ra_svn_conn_t *conn,
-                              apr_pool_t *pool)
+/* If CFG specifies a path to the password DB, read that DB and store it
+ * in REPOSITORY->PWDB.
+ */
+static svn_error_t *
+load_pwdb_config(repository_t *repository,
+                 svn_config_t *cfg,
+                 apr_pool_t *pool)
 {
   const char *pwdb_path;
   svn_error_t *err;
 
-  svn_config_get(server->cfg, &pwdb_path, SVN_CONFIG_SECTION_GENERAL,
+  svn_config_get(cfg, &pwdb_path,
+                 SVN_CONFIG_SECTION_GENERAL,
                  SVN_CONFIG_OPTION_PASSWORD_DB, NULL);
 
-  server->pwdb = NULL;
+  repository->pwdb = NULL;
   if (pwdb_path)
     {
       pwdb_path = svn_dirent_internal_style(pwdb_path, pool);
-      pwdb_path = svn_dirent_join(server->base, pwdb_path, pool);
+      pwdb_path = svn_dirent_join(repository->base, pwdb_path, pool);
 
-      err = svn_config_read3(&server->pwdb, pwdb_path, TRUE,
+      err = svn_config_read3(&repository->pwdb, pwdb_path, TRUE,
                              FALSE, FALSE, pool);
       if (err)
         {
-          log_server_error(err, server, conn, pool);
-
           /* Because it may be possible to read the pwdb file with some
              access methods and not others, ignore errors reading the pwdb
              file and just don't present password authentication as an
@@ -294,11 +239,7 @@ svn_error_t *load_pwdb_config(server_bat
           if (err->apr_err != SVN_ERR_BAD_FILENAME
               && ! APR_STATUS_IS_EACCES(err->apr_err))
             {
-                /* Now that we've logged the error, clear it and return a
-                 * nice, generic error to the user:
-                 * http://subversion.tigris.org/issues/show_bug.cgi?id=2271 */
-                svn_error_clear(err);
-                return svn_error_create(SVN_ERR_AUTHN_FAILED, NULL, NULL);
+              return svn_error_create(SVN_ERR_AUTHN_FAILED, err, NULL);
             }
           else
             /* Ignore SVN_ERR_BAD_FILENAME and APR_EACCES and proceed. */
@@ -310,11 +251,11 @@ svn_error_t *load_pwdb_config(server_bat
 }
 
 /* Canonicalize *ACCESS_FILE based on the type of argument.  Results are
- * placed in *ACCESS_FILE.  SERVER baton is used to convert relative paths to
+ * placed in *ACCESS_FILE.  REPOSITORY is used to convert relative paths to
  * absolute paths rooted at the server root.  REPOS_ROOT is used to calculate
  * an absolute URL for repos-relative URLs. */
 static svn_error_t *
-canonicalize_access_file(const char **access_file, server_baton_t *server,
+canonicalize_access_file(const char **access_file, repository_t *repository,
                          const char *repos_root, apr_pool_t *pool)
 {
   if (svn_path_is_url(*access_file))
@@ -334,7 +275,7 @@ canonicalize_access_file(const char **ac
   else
     {
       *access_file = svn_dirent_internal_style(*access_file, pool);
-      *access_file = svn_dirent_join(server->base, *access_file, pool);
+      *access_file = svn_dirent_join(repository->base, *access_file, pool);
     }
 
   return SVN_NO_ERROR;
@@ -345,21 +286,21 @@ canonicalize_access_file(const char **ac
 
    SERVER and CONN must not be NULL. The real errors will be logged with
    SERVER and CONN but return generic errors to the client. */
-static 
-svn_error_t *load_authz_config(server_baton_t *server,
-                               svn_ra_svn_conn_t *conn,
-                               const char *repos_root,
-                               apr_pool_t *pool)
+static svn_error_t *
+load_authz_config(repository_t *repository,
+                  const char *repos_root,
+                  svn_config_t *cfg,
+                  apr_pool_t *pool)
 {
   const char *authzdb_path;
   const char *groupsdb_path;
   svn_error_t *err;
 
   /* Read authz configuration. */
-  svn_config_get(server->cfg, &authzdb_path, SVN_CONFIG_SECTION_GENERAL,
+  svn_config_get(cfg, &authzdb_path, SVN_CONFIG_SECTION_GENERAL,
                  SVN_CONFIG_OPTION_AUTHZ_DB, NULL);
 
-  svn_config_get(server->cfg, &groupsdb_path, SVN_CONFIG_SECTION_GENERAL,
+  svn_config_get(cfg, &groupsdb_path, SVN_CONFIG_SECTION_GENERAL,
                  SVN_CONFIG_OPTION_GROUPS_DB, NULL);
 
   if (authzdb_path)
@@ -367,48 +308,70 @@ svn_error_t *load_authz_config(server_ba
       const char *case_force_val;
 
       /* Canonicalize and add the base onto the authzdb_path (if needed). */
-      err = canonicalize_access_file(&authzdb_path, server,
+      err = canonicalize_access_file(&authzdb_path, repository,
                                      repos_root, pool);
 
       /* Same for the groupsdb_path if it is present. */
       if (groupsdb_path && !err)
-        err = canonicalize_access_file(&groupsdb_path, server,
+        err = canonicalize_access_file(&groupsdb_path, repository,
                                        repos_root, pool);
 
       if (!err)
-        err = svn_repos_authz_read2(&server->authzdb, authzdb_path,
+        err = svn_repos_authz_read2(&repository->authzdb, authzdb_path,
                                     groupsdb_path, TRUE, pool);
 
       if (err)
-        {
-          log_server_error(err, server, conn, pool);
-          svn_error_clear(err);
-          return svn_error_create(SVN_ERR_AUTHZ_INVALID_CONFIG, NULL, NULL);
-        }
+        return svn_error_create(SVN_ERR_AUTHZ_INVALID_CONFIG, err, NULL);
 
       /* Are we going to be case-normalizing usernames when we consult
        * this authz file? */
-      svn_config_get(server->cfg, &case_force_val, SVN_CONFIG_SECTION_GENERAL,
+      svn_config_get(cfg, &case_force_val,
+                     SVN_CONFIG_SECTION_GENERAL,
                      SVN_CONFIG_OPTION_FORCE_USERNAME_CASE, NULL);
       if (case_force_val)
         {
           if (strcmp(case_force_val, "upper") == 0)
-            server->username_case = CASE_FORCE_UPPER;
+            repository->username_case = CASE_FORCE_UPPER;
           else if (strcmp(case_force_val, "lower") == 0)
-            server->username_case = CASE_FORCE_LOWER;
+            repository->username_case = CASE_FORCE_LOWER;
           else
-            server->username_case = CASE_ASIS;
+            repository->username_case = CASE_ASIS;
         }
     }
   else
     {
-      server->authzdb = NULL;
-      server->username_case = CASE_ASIS;
+      repository->authzdb = NULL;
+      repository->username_case = CASE_ASIS;
     }
 
   return SVN_NO_ERROR;
 }
 
+/* If ERROR is a AUTH* error as returned by load_pwdb_config or
+ * load_authz_config, write it to SERVER's log file.
+ * Return a sanitized version of ERROR.
+ */
+static svn_error_t *
+handle_config_error(svn_error_t *error,
+                    server_baton_t *server)
+{
+  if (   error
+      && (   error->apr_err == SVN_ERR_AUTHZ_INVALID_CONFIG
+          || error->apr_err == SVN_ERR_AUTHN_FAILED))
+    {
+      apr_status_t apr_err = error->apr_err;
+      log_error(error, server);
+
+      /* Now that we've logged the error, clear it and return a
+       * nice, generic error to the user:
+       * http://subversion.tigris.org/issues/show_bug.cgi?id=2271 */
+      svn_error_clear(error);
+      return svn_error_create(apr_err, NULL, NULL);
+    }
+
+  return error;
+}
+
 /* Set *FS_PATH to the portion of URL that is the path within the
    repository, if URL is inside REPOS_URL (if URL is not inside
    REPOS_URL, then error, with the effect on *FS_PATH undefined).
@@ -456,14 +419,16 @@ static svn_error_t *authz_check_access(s
                                        const char *path,
                                        svn_repos_authz_access_t required,
                                        server_baton_t *b,
-                                       svn_ra_svn_conn_t *conn,
                                        apr_pool_t *pool)
 {
+  repository_t *repository = b->repository;
+  client_info_t *client_info = b->client_info;
+
   /* If authz cannot be performed, grant access.  This is NOT the same
      as the default policy when authz is performed on a path with no
      rules.  In the latter case, the default is to deny access, and is
      set by svn_repos_authz_check_access. */
-  if (!b->authzdb)
+  if (!repository->authzdb)
     {
       *allowed = TRUE;
       return SVN_NO_ERROR;
@@ -481,21 +446,23 @@ static svn_error_t *authz_check_access(s
   /* If we have a username, and we've not yet used it + any username
      case normalization that might be requested to determine "the
      username we used for authz purposes", do so now. */
-  if (b->user && (! b->authz_user))
+  if (client_info->user && (! client_info->authz_user))
     {
-      char *authz_user = apr_pstrdup(b->pool, b->user);
-      if (b->username_case == CASE_FORCE_UPPER)
+      char *authz_user = apr_pstrdup(b->pool, client_info->user);
+      if (repository->username_case == CASE_FORCE_UPPER)
         convert_case(authz_user, TRUE);
-      else if (b->username_case == CASE_FORCE_LOWER)
+      else if (repository->username_case == CASE_FORCE_LOWER)
         convert_case(authz_user, FALSE);
-      b->authz_user = authz_user;
+
+      client_info->authz_user = authz_user;
     }
 
-  SVN_ERR(svn_repos_authz_check_access(b->authzdb, b->authz_repos_name,
-                                       path, b->authz_user, required,
-                                       allowed, pool));
+  SVN_ERR(svn_repos_authz_check_access(repository->authzdb,
+                                       repository->authz_repos_name,
+                                       path, client_info->authz_user,
+                                       required, allowed, pool));
   if (!*allowed)
-    SVN_ERR(log_authz_denied(path, required, b, conn, pool));
+    SVN_ERR(log_authz_denied(path, required, b, pool));
 
   return SVN_NO_ERROR;
 }
@@ -513,14 +480,14 @@ static svn_error_t *authz_check_access_c
   authz_baton_t *sb = baton;
 
   return authz_check_access(allowed, path, svn_authz_read,
-                            sb->server, sb->conn, pool);
+                            sb->server, pool);
 }
 
 /* If authz is enabled in the specified BATON, return a read authorization
    function. Otherwise, return NULL. */
 static svn_repos_authz_func_t authz_check_access_cb_func(server_baton_t *baton)
 {
-  if (baton->authzdb)
+  if (baton->repository->authzdb)
      return authz_check_access_cb;
   return NULL;
 }
@@ -539,27 +506,50 @@ static svn_error_t *authz_commit_cb(svn_
 {
   authz_baton_t *sb = baton;
 
-  return authz_check_access(allowed, path, required,
-                            sb->server, sb->conn, pool);
+  return authz_check_access(allowed, path, required, sb->server, pool);
 }
 
-
-enum access_type get_access(server_baton_t *b, enum authn_type auth)
+/* Return the access level specified for OPTION in CFG.  If no such
+ * setting exists, use DEF.  If READ_ONLY is set, unconditionally disable
+ * write access.
+ */
+static enum access_type
+get_access(svn_config_t *cfg,
+           const char *option,
+           const char *def,
+           svn_boolean_t read_only)
 {
-  const char *var = (auth == AUTHENTICATED) ? SVN_CONFIG_OPTION_AUTH_ACCESS :
-    SVN_CONFIG_OPTION_ANON_ACCESS;
-  const char *val, *def = (auth == AUTHENTICATED) ? "write" : "read";
   enum access_type result;
+  const char *val;
 
-  svn_config_get(b->cfg, &val, SVN_CONFIG_SECTION_GENERAL, var, def);
+  svn_config_get(cfg, &val, SVN_CONFIG_SECTION_GENERAL, option, def);
   result = (strcmp(val, "write") == 0 ? WRITE_ACCESS :
             strcmp(val, "read") == 0 ? READ_ACCESS : NO_ACCESS);
-  return (result == WRITE_ACCESS && b->read_only) ? READ_ACCESS : result;
+
+  return result == WRITE_ACCESS && read_only ? READ_ACCESS : result;
+}
+
+/* Set the *_ACCESS members in REPOSITORY according to the settings in
+ * CFG.  If READ_ONLY is set, unconditionally disable write access.
+ */
+static void
+set_access(repository_t *repository,
+           svn_config_t *cfg,
+           svn_boolean_t read_only)
+{
+  repository->auth_access = get_access(cfg, SVN_CONFIG_OPTION_AUTH_ACCESS,
+                                       "write", read_only);
+  repository->anon_access = get_access(cfg, SVN_CONFIG_OPTION_ANON_ACCESS,
+                                       "read", read_only);
 }
 
-static enum access_type current_access(server_baton_t *b)
+/* Return the access level for the user in B.
+ */
+static enum access_type
+current_access(server_baton_t *b)
 {
-  return get_access(b, (b->user) ? AUTHENTICATED : UNAUTHENTICATED);
+  return b->client_info->user ? b->repository->auth_access
+                              : b->repository->anon_access;
 }
 
 /* Send authentication mechs for ACCESS_TYPE to the client.  If NEEDS_USERNAME
@@ -569,11 +559,11 @@ static svn_error_t *send_mechs(svn_ra_sv
                                server_baton_t *b, enum access_type required,
                                svn_boolean_t needs_username)
 {
-  if (!needs_username && get_access(b, UNAUTHENTICATED) >= required)
+  if (!needs_username && b->repository->anon_access >= required)
     SVN_ERR(svn_ra_svn__write_word(conn, pool, "ANONYMOUS"));
-  if (b->tunnel_user && get_access(b, AUTHENTICATED) >= required)
+  if (b->client_info->tunnel_user && b->repository->auth_access >= required)
     SVN_ERR(svn_ra_svn__write_word(conn, pool, "EXTERNAL"));
-  if (b->pwdb && get_access(b, AUTHENTICATED) >= required)
+  if (b->repository->pwdb && b->repository->auth_access >= required)
     SVN_ERR(svn_ra_svn__write_word(conn, pool, "CRAM-MD5"));
   return SVN_NO_ERROR;
 }
@@ -613,15 +603,15 @@ create_fs_access(server_baton_t *b, apr_
   svn_fs_access_t *fs_access;
   struct cleanup_fs_access_baton *cleanup_baton;
 
-  if (!b->user)
+  if (!b->client_info->user)
     return SVN_NO_ERROR;
 
-  SVN_ERR(svn_fs_create_access(&fs_access, b->user, pool));
-  SVN_ERR(svn_fs_set_access(b->fs, fs_access));
+  SVN_ERR(svn_fs_create_access(&fs_access, b->client_info->user, pool));
+  SVN_ERR(svn_fs_set_access(b->repository->fs, fs_access));
 
   cleanup_baton = apr_pcalloc(pool, sizeof(*cleanup_baton));
   cleanup_baton->pool = pool;
-  cleanup_baton->fs = b->fs;
+  cleanup_baton->fs = b->repository->fs;
   apr_pool_cleanup_register(pool, cleanup_baton, cleanup_fs_access,
                             apr_pool_cleanup_null);
 
@@ -643,19 +633,19 @@ static svn_error_t *auth(svn_ra_svn_conn
   const char *user;
   *success = FALSE;
 
-  if (get_access(b, AUTHENTICATED) >= required
-      && b->tunnel_user && strcmp(mech, "EXTERNAL") == 0)
+  if (b->repository->auth_access >= required
+      && b->client_info->tunnel_user && strcmp(mech, "EXTERNAL") == 0)
     {
-      if (*mecharg && strcmp(mecharg, b->tunnel_user) != 0)
+      if (*mecharg && strcmp(mecharg, b->client_info->tunnel_user) != 0)
         return svn_ra_svn__write_tuple(conn, pool, "w(c)", "failure",
                                        "Requested username does not match");
-      b->user = b->tunnel_user;
+      b->client_info->user = b->client_info->tunnel_user;
       SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w()", "success"));
       *success = TRUE;
       return SVN_NO_ERROR;
     }
 
-  if (get_access(b, UNAUTHENTICATED) >= required
+  if (b->repository->anon_access >= required
       && strcmp(mech, "ANONYMOUS") == 0 && ! needs_username)
     {
       SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w()", "success"));
@@ -663,11 +653,12 @@ static svn_error_t *auth(svn_ra_svn_conn
       return SVN_NO_ERROR;
     }
 
-  if (get_access(b, AUTHENTICATED) >= required
-      && b->pwdb && strcmp(mech, "CRAM-MD5") == 0)
+  if (b->repository->auth_access >= required
+      && b->repository->pwdb && strcmp(mech, "CRAM-MD5") == 0)
     {
-      SVN_ERR(svn_ra_svn_cram_server(conn, pool, b->pwdb, &user, success));
-      b->user = apr_pstrdup(b->pool, user);
+      SVN_ERR(svn_ra_svn_cram_server(conn, pool, b->repository->pwdb,
+                                     &user, success));
+      b->client_info->user = apr_pstrdup(b->pool, user);
       return SVN_NO_ERROR;
     }
 
@@ -686,7 +677,7 @@ internal_auth_request(svn_ra_svn_conn_t 
 
   SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success"));
   SVN_ERR(send_mechs(conn, pool, b, required, needs_username));
-  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)c)", b->realm));
+  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)c)", b->repository->realm));
   do
     {
       SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "w(?c)", &mech, &mecharg));
@@ -708,7 +699,7 @@ static svn_error_t *auth_request(svn_ra_
                                  svn_boolean_t needs_username)
 {
 #ifdef SVN_HAVE_SASL
-  if (b->use_sasl)
+  if (b->repository->use_sasl)
     return cyrus_auth_request(conn, pool, b, required, needs_username);
 #endif
 
@@ -738,7 +729,6 @@ static svn_error_t *trivial_auth_request
  */
 static svn_boolean_t lookup_access(apr_pool_t *pool,
                                    server_baton_t *baton,
-                                   svn_ra_svn_conn_t *conn,
                                    svn_repos_authz_access_t required,
                                    const char *path,
                                    svn_boolean_t needs_username)
@@ -749,12 +739,12 @@ static svn_boolean_t lookup_access(apr_p
   svn_error_t *err;
 
   /* Get authz's opinion on the access. */
-  err = authz_check_access(&authorized, path, required, baton, conn, pool);
+  err = authz_check_access(&authorized, path, required, baton, pool);
 
   /* If an error made lookup fail, deny access. */
   if (err)
     {
-      log_server_error(err, baton, conn, pool);
+      log_error(err, baton);
       svn_error_clear(err);
       return FALSE;
     }
@@ -764,7 +754,7 @@ static svn_boolean_t lookup_access(apr_p
      lookup has succeeded. */
   if (current_access(baton) >= req
       && authorized
-      && (! needs_username || baton->user))
+      && (! needs_username || baton->client_info->user))
     return TRUE;
 
   return FALSE;
@@ -797,7 +787,7 @@ static svn_error_t *must_have_access(svn
   /* See whether the user already has the required access.  If so,
      nothing needs to be done.  Create the FS access and send a
      trivial auth request. */
-  if (lookup_access(pool, b, conn, required, path, needs_username))
+  if (lookup_access(pool, b, required, path, needs_username))
     {
       SVN_ERR(create_fs_access(b, pool));
       return trivial_auth_request(conn, pool, b);
@@ -809,17 +799,18 @@ static svn_error_t *must_have_access(svn
      requiring a username because we need one to be able to check
      authz configuration again with a different user credentials than
      the first time round. */
-  if (b->user == NULL
-      && get_access(b, AUTHENTICATED) >= req
-      && (b->tunnel_user || b->pwdb || b->use_sasl))
+  if (b->client_info->user == NULL
+      && b->repository->auth_access >= req
+      && (b->client_info->tunnel_user || b->repository->pwdb
+          || b->repository->use_sasl))
     SVN_ERR(auth_request(conn, pool, b, req, TRUE));
 
   /* Now that an authentication has been done get the new take of
      authz on the request. */
-  if (! lookup_access(pool, b, conn, required, path, needs_username))
+  if (! lookup_access(pool, b, required, path, needs_username))
     return svn_error_create(SVN_ERR_RA_SVN_CMD_ERR,
                             error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED,
-                                                 NULL, NULL, b, conn, pool),
+                                                 NULL, NULL, b),
                             NULL);
 
   /* Else, access is granted, and there is much rejoicing. */
@@ -972,17 +963,18 @@ static svn_error_t *accept_report(svn_bo
   /* Make an svn_repos report baton.  Tell it to drive the network editor
    * when the report is complete. */
   svn_ra_svn_get_editor(&editor, &edit_baton, conn, pool, NULL, NULL);
-  SVN_CMD_ERR(svn_repos_begin_report3(&report_baton, rev, b->repos,
-                                      b->fs_path->data, target, tgt_path,
-                                      text_deltas, depth, ignore_ancestry,
-                                      send_copyfrom_args,
+  SVN_CMD_ERR(svn_repos_begin_report3(&report_baton, rev,
+                                      b->repository->repos,
+                                      b->repository->fs_path->data, target,
+                                      tgt_path, text_deltas, depth,
+                                      ignore_ancestry, send_copyfrom_args,
                                       editor, edit_baton,
                                       authz_check_access_cb_func(b),
                                       &ab, svn_ra_svn_zero_copy_limit(conn),
                                       pool));
 
   rb.sb = b;
-  rb.repos_url = svn_path_uri_decode(b->repos_url, pool);
+  rb.repos_url = svn_path_uri_decode(b->repository->repos_url, pool);
   rb.report_baton = report_baton;
   rb.err = NULL;
   rb.entry_counter = 0;
@@ -1108,11 +1100,11 @@ static svn_error_t *reparent(svn_ra_svn_
   SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &url));
   url = svn_uri_canonicalize(url, pool);
   SVN_ERR(trivial_auth_request(conn, pool, b));
-  SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repos_url, pool),
+  SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repository->repos_url, pool),
                           svn_path_uri_decode(url, pool),
                           &fs_path));
   SVN_ERR(log_command(b, conn, pool, "%s", svn_log__reparent(fs_path, pool)));
-  svn_stringbuf_set(b->fs_path, fs_path);
+  svn_stringbuf_set(b->repository->fs_path, fs_path);
   SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
   return SVN_NO_ERROR;
 }
@@ -1126,7 +1118,7 @@ static svn_error_t *get_latest_rev(svn_r
   SVN_ERR(log_command(b, conn, pool, "get-latest-rev"));
 
   SVN_ERR(trivial_auth_request(conn, pool, b));
-  SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
+  SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
   SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", rev));
   return SVN_NO_ERROR;
 }
@@ -1144,7 +1136,7 @@ static svn_error_t *get_dated_rev(svn_ra
 
   SVN_ERR(trivial_auth_request(conn, pool, b));
   SVN_CMD_ERR(svn_time_from_cstring(&tm, timestr, pool));
-  SVN_CMD_ERR(svn_repos_dated_revision(&rev, b->repos, tm, pool));
+  SVN_CMD_ERR(svn_repos_dated_revision(&rev, b->repository->repos, tm, pool));
   SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", rev));
   return SVN_NO_ERROR;
 }
@@ -1166,7 +1158,8 @@ static svn_error_t *do_change_rev_prop(s
   SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, FALSE));
   SVN_ERR(log_command(b, conn, pool, "%s",
                       svn_log__change_rev_prop(rev, name, pool)));
-  SVN_CMD_ERR(svn_repos_fs_change_rev_prop4(b->repos, rev, b->user,
+  SVN_CMD_ERR(svn_repos_fs_change_rev_prop4(b->repository->repos, rev,
+                                            b->client_info->user,
                                             name, old_value_p, value,
                                             TRUE, TRUE,
                                             authz_check_access_cb_func(b), &ab,
@@ -1245,9 +1238,10 @@ static svn_error_t *rev_proplist(svn_ra_
   SVN_ERR(log_command(b, conn, pool, "%s", svn_log__rev_proplist(rev, pool)));
 
   SVN_ERR(trivial_auth_request(conn, pool, b));
-  SVN_CMD_ERR(svn_repos_fs_revision_proplist(&props, b->repos, rev,
-                                             authz_check_access_cb_func(b), &ab,
-                                             pool));
+  SVN_CMD_ERR(svn_repos_fs_revision_proplist(&props, b->repository->repos,
+                                             rev,
+                                             authz_check_access_cb_func(b),
+                                             &ab, pool));
   SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success"));
   SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props));
   SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))"));
@@ -1271,9 +1265,9 @@ static svn_error_t *rev_prop(svn_ra_svn_
                       svn_log__rev_prop(rev, name, pool)));
 
   SVN_ERR(trivial_auth_request(conn, pool, b));
-  SVN_CMD_ERR(svn_repos_fs_revision_prop(&value, b->repos, rev, name,
-                                         authz_check_access_cb_func(b), &ab,
-                                         pool));
+  SVN_CMD_ERR(svn_repos_fs_revision_prop(&value, b->repository->repos, rev,
+                                         name, authz_check_access_cb_func(b),
+                                         &ab, pool));
   SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "(?s)", value));
   return SVN_NO_ERROR;
 }
@@ -1300,15 +1294,14 @@ static svn_error_t *commit_done(const sv
  * violates the authz configuration, return SVN_ERR_RA_NOT_AUTHORIZED
  * to the client.  Use POOL for temporary allocations only.
  */
-static svn_error_t *add_lock_tokens(svn_ra_svn_conn_t *conn,
-                                    const apr_array_header_t *lock_tokens,
+static svn_error_t *add_lock_tokens(const apr_array_header_t *lock_tokens,
                                     server_baton_t *sb,
                                     apr_pool_t *pool)
 {
   int i;
   svn_fs_access_t *fs_access;
 
-  SVN_ERR(svn_fs_get_access(&fs_access, sb->fs));
+  SVN_ERR(svn_fs_get_access(&fs_access, sb->repository->fs));
 
   /* If there is no access context, nowhere to add the tokens. */
   if (! fs_access)
@@ -1335,14 +1328,13 @@ static svn_error_t *add_lock_tokens(svn_
                                 "Lock token isn't a string");
 
       path = path_item->u.string->data;
-      full_path = svn_fspath__join(sb->fs_path->data,
+      full_path = svn_fspath__join(sb->repository->fs_path->data,
                                    svn_relpath_canonicalize(path, pool),
                                    pool);
 
-      if (! lookup_access(pool, sb, conn, svn_authz_write,
-                          full_path, TRUE))
+      if (! lookup_access(pool, sb, svn_authz_write, full_path, TRUE))
         return error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, NULL,
-                                    sb, conn, pool);
+                                    sb);
 
       token = token_item->u.string->data;
       SVN_ERR(svn_fs_access_add_lock_token2(fs_access, path, token));
@@ -1355,7 +1347,6 @@ static svn_error_t *add_lock_tokens(svn_
    LOCK_TOKENS contains svn_ra_svn_item_t elements, assumed to be lists. */
 static svn_error_t *unlock_paths(const apr_array_header_t *lock_tokens,
                                  server_baton_t *sb,
-                                 svn_ra_svn_conn_t *conn,
                                  apr_pool_t *pool)
 {
   int i;
@@ -1377,15 +1368,15 @@ static svn_error_t *unlock_paths(const a
       path = path_item->u.string->data;
       token = token_item->u.string->data;
 
-      full_path = svn_fspath__join(sb->fs_path->data,
+      full_path = svn_fspath__join(sb->repository->fs_path->data,
                                    svn_relpath_canonicalize(path, iterpool),
                                    iterpool);
 
       /* The lock may have become defunct after the commit, so ignore such
          errors. */
-      err = svn_repos_fs_unlock(sb->repos, full_path, token,
+      err = svn_repos_fs_unlock(sb->repository->repos, full_path, token,
                                 FALSE, iterpool);
-      log_server_error(err, sb, conn, iterpool);
+      log_error(err, sb);
       svn_error_clear(err);
     }
 
@@ -1445,7 +1436,7 @@ static svn_error_t *commit(svn_ra_svn_co
   /* Authorize the lock tokens and give them to the FS if we got
      any. */
   if (lock_tokens && lock_tokens->nelts)
-    SVN_CMD_ERR(add_lock_tokens(conn, lock_tokens, b, pool));
+    SVN_CMD_ERR(add_lock_tokens(lock_tokens, b, pool));
 
   /* Ignore LOG_MSG, per the protocol.  See ra_svn_commit(). */
   if (revprop_list)
@@ -1460,7 +1451,9 @@ static svn_error_t *commit(svn_ra_svn_co
   /* Get author from the baton, making sure clients can't circumvent
      the authentication via the revision props. */
   svn_hash_sets(revprop_table, SVN_PROP_REVISION_AUTHOR,
-                b->user ? svn_string_create(b->user, pool) : NULL);
+                b->client_info->user
+                   ? svn_string_create(b->client_info->user, pool)
+                   : NULL);
 
   ccb.pool = pool;
   ccb.new_rev = &new_rev;
@@ -1469,9 +1462,9 @@ static svn_error_t *commit(svn_ra_svn_co
   ccb.post_commit_err = &post_commit_err;
   /* ### Note that svn_repos_get_commit_editor5 actually wants a decoded URL. */
   SVN_CMD_ERR(svn_repos_get_commit_editor5
-              (&editor, &edit_baton, b->repos, NULL,
-               svn_path_uri_decode(b->repos_url, pool),
-               b->fs_path->data, revprop_table,
+              (&editor, &edit_baton, b->repository->repos, NULL,
+               svn_path_uri_decode(b->repository->repos_url, pool),
+               b->repository->fs_path->data, revprop_table,
                commit_done, &ccb,
                authz_commit_cb, &ab, pool));
   SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
@@ -1488,18 +1481,18 @@ static svn_error_t *commit(svn_ra_svn_co
          and thus kill the server.  But otherwise, deltify after
          answering the client, to avoid user-visible delay. */
 
-      if (b->tunnel)
-        SVN_ERR(svn_fs_deltify_revision(b->fs, new_rev, pool));
+      if (b->client_info->tunnel)
+        SVN_ERR(svn_fs_deltify_revision(b->repository->fs, new_rev, pool));
 
       /* Unlock the paths. */
       if (! keep_locks && lock_tokens && lock_tokens->nelts)
-        SVN_ERR(unlock_paths(lock_tokens, b, conn, pool));
+        SVN_ERR(unlock_paths(lock_tokens, b, pool));
 
       SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "r(?c)(?c)(?c)",
                                       new_rev, date, author, post_commit_err));
 
-      if (! b->tunnel)
-        SVN_ERR(svn_fs_deltify_revision(b->fs, new_rev, pool));
+      if (! b->client_info->tunnel)
+        SVN_ERR(svn_fs_deltify_revision(b->repository->fs, new_rev, pool));
     }
   return SVN_NO_ERROR;
 }
@@ -1532,7 +1525,7 @@ static svn_error_t *get_file(svn_ra_svn_
                                   &want_props, &want_contents,
                                   &wants_inherited_props));
 
-  full_path = svn_fspath__join(b->fs_path->data,
+  full_path = svn_fspath__join(b->repository->fs_path->data,
                                svn_relpath_canonicalize(path, pool), pool);
 
   /* Check authorizations */
@@ -1540,14 +1533,14 @@ static svn_error_t *get_file(svn_ra_svn_
                            full_path, FALSE));
 
   if (!SVN_IS_VALID_REVNUM(rev))
-    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
+    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
 
   SVN_ERR(log_command(b, conn, pool, "%s",
                       svn_log__get_file(full_path, rev,
                                         want_contents, want_props, pool)));
 
   /* Fetch the properties and a stream for the contents. */
-  SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool));
+  SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, pool));
   SVN_CMD_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, root,
                                    full_path, TRUE, pool));
   hex_digest = svn_checksum_to_cstring_display(checksum, pool);
@@ -1677,7 +1670,7 @@ static svn_error_t *get_dir(svn_ra_svn_c
         }
     }
 
-  full_path = svn_fspath__join(b->fs_path->data,
+  full_path = svn_fspath__join(b->repository->fs_path->data,
                                svn_relpath_canonicalize(path, pool), pool);
 
   /* Check authorizations */
@@ -1685,7 +1678,7 @@ static svn_error_t *get_dir(svn_ra_svn_c
                            full_path, FALSE));
 
   if (!SVN_IS_VALID_REVNUM(rev))
-    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
+    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
 
   SVN_ERR(log_command(b, conn, pool, "%s",
                       svn_log__get_dir(full_path, rev,
@@ -1693,7 +1686,7 @@ static svn_error_t *get_dir(svn_ra_svn_c
                                        dirent_fields, pool)));
 
   /* Fetch the root of the appropriate revision. */
-  SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool));
+  SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, pool));
 
   /* Fetch the directory's explicit and/or inherited properties
      if requested. */
@@ -1739,8 +1732,7 @@ static svn_error_t *get_dir(svn_ra_svn_c
           svn_pool_clear(subpool);
 
           file_path = svn_fspath__join(full_path, name, subpool);
-          if (! lookup_access(subpool, b, conn, svn_authz_read,
-                              file_path, FALSE))
+          if (! lookup_access(subpool, b, svn_authz_read, file_path, FALSE))
             continue;
 
           if (dirent_fields & SVN_DIRENT_KIND)
@@ -1840,12 +1832,12 @@ static svn_error_t *update(svn_ra_svn_co
   else
     depth = SVN_DEPTH_INFINITY_OR_FILES(recurse);
 
-  full_path = svn_fspath__join(b->fs_path->data, target, pool);
+  full_path = svn_fspath__join(b->repository->fs_path->data, target, pool);
   /* Check authorization and authenticate the user if necessary. */
   SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, full_path, FALSE));
 
   if (!SVN_IS_VALID_REVNUM(rev))
-    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
+    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
 
   SVN_ERR(accept_report(&is_checkout, NULL,
                         conn, pool, b, rev, target, NULL, TRUE,
@@ -1898,14 +1890,16 @@ static svn_error_t *switch_cmd(svn_ra_sv
 
   SVN_ERR(trivial_auth_request(conn, pool, b));
   if (!SVN_IS_VALID_REVNUM(rev))
-    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
+    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
 
-  SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repos_url, pool),
+  SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repository->repos_url,
+                                              pool),
                           svn_path_uri_decode(switch_url, pool),
                           &switch_path));
 
   {
-    const char *full_path = svn_fspath__join(b->fs_path->data, target, pool);
+    const char *full_path = svn_fspath__join(b->repository->fs_path->data,
+                                             target, pool);
     SVN_ERR(log_command(b, conn, pool, "%s",
                         svn_log__switch(full_path, switch_path, rev,
                                         depth, pool)));
@@ -1941,10 +1935,11 @@ static svn_error_t *status(svn_ra_svn_co
 
   SVN_ERR(trivial_auth_request(conn, pool, b));
   if (!SVN_IS_VALID_REVNUM(rev))
-    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
+    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
 
   {
-    const char *full_path = svn_fspath__join(b->fs_path->data, target, pool);
+    const char *full_path = svn_fspath__join(b->repository->fs_path->data,
+                                             target, pool);
     SVN_ERR(log_command(b, conn, pool, "%s",
                         svn_log__status(full_path, rev, depth, pool)));
   }
@@ -1992,13 +1987,15 @@ static svn_error_t *diff(svn_ra_svn_conn
   SVN_ERR(trivial_auth_request(conn, pool, b));
 
   if (!SVN_IS_VALID_REVNUM(rev))
-    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
-  SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repos_url, pool),
+    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
+  SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repository->repos_url,
+                                              pool),
                           svn_path_uri_decode(versus_url, pool),
                           &versus_path));
 
   {
-    const char *full_path = svn_fspath__join(b->fs_path->data, target, pool);
+    const char *full_path = svn_fspath__join(b->repository->fs_path->data,
+                                             target, pool);
     svn_revnum_t from_rev;
     SVN_ERR(accept_report(NULL, &from_rev,
                           conn, pool, b, rev, target, versus_path,
@@ -2050,7 +2047,7 @@ static svn_error_t *get_mergeinfo(svn_ra
           return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
                                   _("Path is not a string"));
         full_path = svn_relpath_canonicalize(item->u.string->data, pool);
-        full_path = svn_fspath__join(b->fs_path->data, full_path, pool);
+        full_path = svn_fspath__join(b->repository->fs_path->data, full_path, pool);
         APR_ARRAY_PUSH(canonical_paths, const char *) = full_path;
      }
 
@@ -2060,14 +2057,14 @@ static svn_error_t *get_mergeinfo(svn_ra
                                              pool)));
 
   SVN_ERR(trivial_auth_request(conn, pool, b));
-  SVN_CMD_ERR(svn_repos_fs_get_mergeinfo(&mergeinfo, b->repos,
+  SVN_CMD_ERR(svn_repos_fs_get_mergeinfo(&mergeinfo, b->repository->repos,
                                          canonical_paths, rev,
                                          inherit,
                                          include_descendants,
                                          authz_check_access_cb_func(b), &ab,
                                          pool));
   SVN_ERR(svn_mergeinfo__remove_prefix_from_catalog(&mergeinfo, mergeinfo,
-                                                    b->fs_path->data, pool));
+                                      b->repository->fs_path->data, pool));
   SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success"));
   iterpool = svn_pool_create(pool);
   for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
@@ -2184,18 +2181,20 @@ static svn_error_t *log_cmd(svn_ra_svn_c
   char *revprop_word;
   svn_ra_svn_item_t *elt;
   int i;
-  apr_uint64_t limit, include_merged_revs_param;
+  apr_uint64_t limit, include_merged_revs_param, move_behavior_param;
+  svn_move_behavior_t move_behavior;
   log_baton_t lb;
   authz_baton_t ab;
 
   ab.server = b;
   ab.conn = conn;
 
-  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "l(?r)(?r)bb?n?Bwl", &paths,
+  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "l(?r)(?r)bb?n?Bwl?n", &paths,
                                   &start_rev, &end_rev, &send_changed_paths,
                                   &strict_node, &limit,
                                   &include_merged_revs_param,
-                                  &revprop_word, &revprop_items));
+                                  &revprop_word, &revprop_items,
+                                  &move_behavior_param));
 
   if (include_merged_revs_param == SVN_RA_SVN_UNSPECIFIED_NUMBER)
     include_merged_revisions = FALSE;
@@ -2227,6 +2226,16 @@ static svn_error_t *log_cmd(svn_ra_svn_c
                              _("Unknown revprop word '%s' in log command"),
                              revprop_word);
 
+  if (move_behavior_param == SVN_RA_SVN_UNSPECIFIED_NUMBER)
+    move_behavior = svn_move_behavior_no_moves;
+  else if (move_behavior_param <= svn_move_behavior_auto_moves)
+    move_behavior = (svn_move_behavior_t) move_behavior_param;
+  else
+    return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
+                             _("Invalid move_behavior value %"
+                               APR_UINT64_T_FMT " in log command"),
+                             move_behavior_param);
+
   /* If we got an unspecified number then the user didn't send us anything,
      so we assume no limit.  If it's larger than INT_MAX then someone is
      messing with us, since we know the svn client libraries will never send
@@ -2242,7 +2251,8 @@ static svn_error_t *log_cmd(svn_ra_svn_c
         return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
                                 _("Log path entry not a string"));
       full_path = svn_relpath_canonicalize(elt->u.string->data, pool),
-      full_path = svn_fspath__join(b->fs_path->data, full_path, pool);
+      full_path = svn_fspath__join(b->repository->fs_path->data, full_path,
+                                   pool);
       APR_ARRAY_PUSH(full_paths, const char *) = full_path;
     }
   SVN_ERR(trivial_auth_request(conn, pool, b));
@@ -2254,14 +2264,15 @@ static svn_error_t *log_cmd(svn_ra_svn_c
                                    revprops, pool)));
 
   /* Get logs.  (Can't report errors back to the client at this point.) */
-  lb.fs_path = b->fs_path->data;
+  lb.fs_path = b->repository->fs_path->data;
   lb.conn = conn;
   lb.stack_depth = 0;
-  err = svn_repos_get_logs4(b->repos, full_paths, start_rev, end_rev,
-                            (int) limit, send_changed_paths, strict_node,
-                            include_merged_revisions, revprops,
-                            authz_check_access_cb_func(b), &ab, log_receiver,
-                            &lb, pool);
+  err = svn_repos_get_logs5(b->repository->repos, full_paths, start_rev,
+                            end_rev, (int) limit, send_changed_paths,
+                            strict_node, include_merged_revisions,
+                            move_behavior, revprops,
+                            authz_check_access_cb_func(b),
+                            &ab, log_receiver, &lb, pool);
 
   write_err = svn_ra_svn__write_word(conn, pool, "done");
   if (write_err)
@@ -2284,7 +2295,7 @@ static svn_error_t *check_path(svn_ra_sv
   svn_node_kind_t kind;
 
   SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)", &path, &rev));
-  full_path = svn_fspath__join(b->fs_path->data,
+  full_path = svn_fspath__join(b->repository->fs_path->data,
                                svn_relpath_canonicalize(path, pool), pool);
 
   /* Check authorizations */
@@ -2292,12 +2303,12 @@ static svn_error_t *check_path(svn_ra_sv
                            full_path, FALSE));
 
   if (!SVN_IS_VALID_REVNUM(rev))
-    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
+    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
 
   SVN_ERR(log_command(b, conn, pool, "check-path %s@%d",
                       svn_path_uri_encode(full_path, pool), rev));
 
-  SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool));
+  SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, pool));
   SVN_CMD_ERR(svn_fs_check_path(&kind, root, full_path, pool));
   SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "w",
                                          svn_node_kind_to_word(kind)));
@@ -2314,7 +2325,7 @@ static svn_error_t *stat_cmd(svn_ra_svn_
   svn_dirent_t *dirent;
 
   SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)", &path, &rev));
-  full_path = svn_fspath__join(b->fs_path->data,
+  full_path = svn_fspath__join(b->repository->fs_path->data,
                                svn_relpath_canonicalize(path, pool), pool);
 
   /* Check authorizations */
@@ -2322,12 +2333,12 @@ static svn_error_t *stat_cmd(svn_ra_svn_
                            full_path, FALSE));
 
   if (!SVN_IS_VALID_REVNUM(rev))
-    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
+    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
 
   SVN_ERR(log_command(b, conn, pool, "stat %s@%d",
                       svn_path_uri_encode(full_path, pool), rev));
 
-  SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool));
+  SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, pool));
   SVN_CMD_ERR(svn_repos_stat(&dirent, root, full_path, pool));
 
   /* Need to return the equivalent of "(?l)", since that's what the
@@ -2375,7 +2386,8 @@ static svn_error_t *get_locations(svn_ra
                                   &loc_revs_proto));
   relative_path = svn_relpath_canonicalize(relative_path, pool);
 
-  abs_path = svn_fspath__join(b->fs_path->data, relative_path, pool);
+  abs_path = svn_fspath__join(b->repository->fs_path->data, relative_path,
+                              pool);
 
   location_revisions = apr_array_make(pool, loc_revs_proto->nelts,
                                       sizeof(svn_revnum_t));
@@ -2400,8 +2412,9 @@ static svn_error_t *get_locations(svn_ra
   /* We store both err and write_err here, so the client will get
    * the "done" even if there was an error in fetching the results. */
 
-  err = svn_repos_trace_node_locations(b->fs, &fs_locations, abs_path,
-                                       peg_revision, location_revisions,
+  err = svn_repos_trace_node_locations(b->repository->fs, &fs_locations,
+                                       abs_path, peg_revision,
+                                       location_revisions,
                                        authz_check_access_cb_func(b), &ab,
                                        pool);
 
@@ -2469,7 +2482,8 @@ static svn_error_t *get_location_segment
                                   &start_rev, &end_rev));
   relative_path = svn_relpath_canonicalize(relative_path, pool);
 
-  abs_path = svn_fspath__join(b->fs_path->data, relative_path, pool);
+  abs_path = svn_fspath__join(b->repository->fs_path->data, relative_path,
+                              pool);
 
   if (SVN_IS_VALID_REVNUM(start_rev)
       && SVN_IS_VALID_REVNUM(end_rev)
@@ -2503,7 +2517,7 @@ static svn_error_t *get_location_segment
   /* We store both err and write_err here, so the client will get
    * the "done" even if there was an error in fetching the results. */
 
-  err = svn_repos_node_location_segments(b->repos, abs_path,
+  err = svn_repos_node_location_segments(b->repository->repos, abs_path,
                                          peg_revision, start_rev, end_rev,
                                          gls_receiver, (void *)conn,
                                          authz_check_access_cb_func(b), &ab,
@@ -2611,7 +2625,7 @@ static svn_error_t *get_file_revs(svn_ra
                                   &include_merged_revs_param));
   path = svn_relpath_canonicalize(path, pool);
   SVN_ERR(trivial_auth_request(conn, pool, b));
-  full_path = svn_fspath__join(b->fs_path->data, path, pool);
+  full_path = svn_fspath__join(b->repository->fs_path->data, path, pool);
 
   if (include_merged_revs_param == SVN_RA_SVN_UNSPECIFIED_NUMBER)
     include_merged_revisions = FALSE;
@@ -2626,8 +2640,8 @@ static svn_error_t *get_file_revs(svn_ra
   frb.conn = conn;
   frb.pool = NULL;
 
-  err = svn_repos_get_file_revs2(b->repos, full_path, start_rev, end_rev,
-                                 include_merged_revisions,
+  err = svn_repos_get_file_revs2(b->repository->repos, full_path, start_rev,
+                                 end_rev, include_merged_revisions,
                                  authz_check_access_cb_func(b), &ab,
                                  file_rev_handler, &frb, pool);
   write_err = svn_ra_svn__write_word(conn, pool, "done");
@@ -2655,7 +2669,7 @@ static svn_error_t *lock(svn_ra_svn_conn
 
   SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?c)b(?r)", &path, &comment,
                                   &steal_lock, &current_rev));
-  full_path = svn_fspath__join(b->fs_path->data,
+  full_path = svn_fspath__join(b->repository->fs_path->data,
                                svn_relpath_canonicalize(path, pool), pool);
 
   SVN_ERR(must_have_access(conn, pool, b, svn_authz_write,
@@ -2663,8 +2677,8 @@ static svn_error_t *lock(svn_ra_svn_conn
   SVN_ERR(log_command(b, conn, pool, "%s",
                       svn_log__lock_one_path(full_path, steal_lock, pool)));
 
-  SVN_CMD_ERR(svn_repos_fs_lock(&l, b->repos, full_path, NULL, comment, 0,
-                                0, /* No expiration time. */
+  SVN_CMD_ERR(svn_repos_fs_lock(&l, b->repository->repos, full_path, NULL,
+                                comment, 0, 0, /* No expiration time. */
                                 current_rev, steal_lock, pool));
 
   SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(!", "success"));
@@ -2719,19 +2733,19 @@ static svn_error_t *lock_many(svn_ra_svn
 
       /* Allocate the full_path out of pool so it will survive for use
        * by operational logging, after this loop. */
-      full_path = svn_fspath__join(b->fs_path->data,
+      full_path = svn_fspath__join(b->repository->fs_path->data,
                                    svn_relpath_canonicalize(path, subpool),
                                    pool);
       APR_ARRAY_PUSH(log_paths, const char *) = full_path;
 
-      if (! lookup_access(pool, b, conn, svn_authz_write, full_path, TRUE))
+      if (! lookup_access(pool, b, svn_authz_write, full_path, TRUE))
         {
           err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, NULL,
-                                     b, conn, pool);
+                                     b);
           break;
         }
 
-      err = svn_repos_fs_lock(&l, b->repos, full_path,
+      err = svn_repos_fs_lock(&l, b->repository->repos, full_path,
                               NULL, comment, FALSE,
                               0, /* No expiration time. */
                               current_rev,
@@ -2783,7 +2797,7 @@ static svn_error_t *unlock(svn_ra_svn_co
   SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?c)b", &path, &token,
                                  &break_lock));
 
-  full_path = svn_fspath__join(b->fs_path->data,
+  full_path = svn_fspath__join(b->repository->fs_path->data,
                                svn_relpath_canonicalize(path, pool), pool);
 
   /* Username required unless break_lock was specified. */
@@ -2792,8 +2806,8 @@ static svn_error_t *unlock(svn_ra_svn_co
   SVN_ERR(log_command(b, conn, pool, "%s",
                       svn_log__unlock_one_path(full_path, break_lock, pool)));
 
-  SVN_CMD_ERR(svn_repos_fs_unlock(b->repos, full_path, token, break_lock,
-                                  pool));
+  SVN_CMD_ERR(svn_repos_fs_unlock(b->repository->repos, full_path, token,
+                                  break_lock, pool));
 
   SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
 
@@ -2840,21 +2854,20 @@ static svn_error_t *unlock_many(svn_ra_s
 
       /* Allocate the full_path out of pool so it will survive for use
        * by operational logging, after this loop. */
-      full_path = svn_fspath__join(b->fs_path->data,
+      full_path = svn_fspath__join(b->repository->fs_path->data,
                                    svn_relpath_canonicalize(path, subpool),
                                    pool);
       APR_ARRAY_PUSH(log_paths, const char *) = full_path;
 
-      if (! lookup_access(subpool, b, conn, svn_authz_write, full_path,
+      if (! lookup_access(subpool, b, svn_authz_write, full_path,
                           ! break_lock))
         return svn_error_create(SVN_ERR_RA_SVN_CMD_ERR,
                                 error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED,
-                                                     NULL, NULL,
-                                                     b, conn, pool),
+                                                     NULL, NULL, b),
                                 NULL);
 
-      err = svn_repos_fs_unlock(b->repos, full_path, token, break_lock,
-                                subpool);
+      err = svn_repos_fs_unlock(b->repository->repos, full_path, token,
+                                break_lock, subpool);
       if (err)
         {
           if (SVN_ERR_IS_UNLOCK_ERROR(err))
@@ -2897,7 +2910,7 @@ static svn_error_t *get_lock(svn_ra_svn_
 
   SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &path));
 
-  full_path = svn_fspath__join(b->fs_path->data,
+  full_path = svn_fspath__join(b->repository->fs_path->data,
                                svn_relpath_canonicalize(path, pool), pool);
 
   SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
@@ -2905,7 +2918,7 @@ static svn_error_t *get_lock(svn_ra_svn_
   SVN_ERR(log_command(b, conn, pool, "get-lock %s",
                       svn_path_uri_encode(full_path, pool)));
 
-  SVN_CMD_ERR(svn_fs_get_lock(&l, b->fs, full_path, pool));
+  SVN_CMD_ERR(svn_fs_get_lock(&l, b->repository->fs, full_path, pool));
 
   SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success"));
   if (l)
@@ -2944,14 +2957,15 @@ static svn_error_t *get_locks(svn_ra_svn
       return log_fail_and_flush(err, b, conn, pool);
     }
 
-  full_path = svn_fspath__join(b->fs_path->data,
+  full_path = svn_fspath__join(b->repository->fs_path->data,
                                svn_relpath_canonicalize(path, pool), pool);
 
   SVN_ERR(trivial_auth_request(conn, pool, b));
 
   SVN_ERR(log_command(b, conn, pool, "get-locks %s",
                       svn_path_uri_encode(full_path, pool)));
-  SVN_CMD_ERR(svn_repos_fs_get_locks2(&locks, b->repos, full_path, depth,
+  SVN_CMD_ERR(svn_repos_fs_get_locks2(&locks, b->repository->repos,
+                                      full_path, depth,
                                       authz_check_access_cb_func(b), &ab,
                                       pool));
 
@@ -2984,15 +2998,16 @@ static svn_error_t *replay_one_revision(
   ab.conn = conn;
 
   SVN_ERR(log_command(b, conn, pool,
-                      svn_log__replay(b->fs_path->data, rev, pool)));
+                      svn_log__replay(b->repository->fs_path->data, rev,
+                                      pool)));
 
   svn_ra_svn_get_editor(&editor, &edit_baton, conn, pool, NULL, NULL);
 
-  err = svn_fs_revision_root(&root, b->fs, rev, pool);
+  err = svn_fs_revision_root(&root, b->repository->fs, rev, pool);
 
   if (! err)
-    err = svn_repos_replay2(root, b->fs_path->data, low_water_mark,
-                            send_deltas, editor, edit_baton,
+    err = svn_repos_replay2(root, b->repository->fs_path->data,
+                            low_water_mark, send_deltas, editor, edit_baton,
                             authz_check_access_cb_func(b), &ab, pool);
 
   if (err)
@@ -3047,7 +3062,8 @@ static svn_error_t *replay_range(svn_ra_
 
       svn_pool_clear(iterpool);
 
-      SVN_CMD_ERR(svn_repos_fs_revision_proplist(&props, b->repos, rev,
+      SVN_CMD_ERR(svn_repos_fs_revision_proplist(&props,
+                                                 b->repository->repos, rev,
                                                  authz_check_access_cb_func(b),
                                                  &ab,
                                                  iterpool));
@@ -3080,12 +3096,12 @@ get_deleted_rev(svn_ra_svn_conn_t *conn,
 
   SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "crr",
                                  &path, &peg_revision, &end_revision));
-  full_path = svn_fspath__join(b->fs_path->data,
+  full_path = svn_fspath__join(b->repository->fs_path->data,
                                svn_relpath_canonicalize(path, pool), pool);
   SVN_ERR(log_command(b, conn, pool, "get-deleted-rev"));
   SVN_ERR(trivial_auth_request(conn, pool, b));
-  SVN_ERR(svn_repos_deleted_rev(b->fs, full_path, peg_revision, end_revision,
-                                &revision_deleted, pool));
+  SVN_ERR(svn_repos_deleted_rev(b->repository->fs, full_path, peg_revision,
+                                end_revision, &revision_deleted, pool));
   SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", revision_deleted));
   return SVN_NO_ERROR;
 }
@@ -3111,7 +3127,7 @@ get_inherited_props(svn_ra_svn_conn_t *c
   /* Parse arguments. */
   SVN_ERR(svn_ra_svn__parse_tuple(params, iterpool, "c(?r)", &path, &rev));
 
-  full_path = svn_fspath__join(b->fs_path->data,
+  full_path = svn_fspath__join(b->repository->fs_path->data,
                                svn_relpath_canonicalize(path, iterpool),
                                pool);
 
@@ -3120,14 +3136,14 @@ get_inherited_props(svn_ra_svn_conn_t *c
                            full_path, FALSE));
 
   if (!SVN_IS_VALID_REVNUM(rev))
-    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
+    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
 
   SVN_ERR(log_command(b, conn, pool, "%s",
                       svn_log__get_inherited_props(full_path, rev,
                                                    iterpool)));
 
   /* Fetch the properties and a stream for the contents. */
-  SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, iterpool));
+  SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, iterpool));
   SVN_CMD_ERR(get_props(NULL, &inherited_props, &ab, root, full_path, pool));
 
   /* Send successful command response with revision and props. */
@@ -3241,18 +3257,19 @@ repos_path_valid(const char *path)
 }
 
 /* Look for the repository given by URL, using ROOT as the virtual
- * repository root.  If we find one, fill in the repos, fs, cfg,
- * repos_url, and fs_path fields of B.  Set B->repos's client
+ * repository root.  If we find one, fill in the repos, fs, repos_url,
+ * and fs_path fields of REPOSITORY.  Set REPOSITORY->repos's client
  * capabilities to CAPABILITIES, which must be at least as long-lived
- * as POOL, and whose elements are SVN_RA_CAPABILITY_*.
+ * as POOL, and whose elements are SVN_RA_CAPABILITY_*.  VHOST and
+ * READ_ONLY flags are the same as in the server baton.
  */
 static svn_error_t *find_repos(const char *url, const char *root,
-                               server_baton_t *b,
-                               svn_ra_svn_conn_t *conn,
+                               svn_boolean_t vhost, svn_boolean_t read_only,
+                               svn_config_t *cfg, repository_t *repository,
                                const apr_array_header_t *capabilities,
                                apr_pool_t *pool)
 {
-  const char *path, *full_path, *repos_root, *fs_path, *hooks_env;
+  const char *path, *full_path, *repos_root, *fs_path, *hooks_env, *val;
   svn_stringbuf_t *url_buf;
 
   /* Skip past the scheme and authority part. */
@@ -3261,7 +3278,7 @@ static svn_error_t *find_repos(const cha
     return svn_error_createf(SVN_ERR_BAD_URL, NULL,
                              "Non-svn URL passed to svn server: '%s'", url);
 
-  if (! b->vhost)
+  if (! vhost)
     {
       path = strchr(path, '/');
       if (path == NULL)
@@ -3287,72 +3304,75 @@ static svn_error_t *find_repos(const cha
                              "No repository found in '%s'", url);
 
   /* Open the repository and fill in b with the resulting information. */
-  SVN_ERR(svn_repos_open2(&b->repos, repos_root, b->fs_config, pool));
-  SVN_ERR(svn_repos_remember_client_capabilities(b->repos, capabilities));
-  b->fs = svn_repos_fs(b->repos);
+  SVN_ERR(svn_repos_open2(&repository->repos, repos_root,
+                          repository->fs_config, pool));
+  SVN_ERR(svn_repos_remember_client_capabilities(repository->repos,
+                                                 capabilities));
+  repository->fs = svn_repos_fs(repository->repos);
   fs_path = full_path + strlen(repos_root);
-  b->fs_path = svn_stringbuf_create(*fs_path ? fs_path : "/", pool);
+  repository->fs_path = svn_stringbuf_create(*fs_path ? fs_path : "/", pool);
   url_buf = svn_stringbuf_create(url, pool);
   svn_path_remove_components(url_buf,
-                             svn_path_component_count(b->fs_path->data));
-  b->repos_url = url_buf->data;
-  b->authz_repos_name = svn_dirent_is_child(root, repos_root, pool);
-  if (b->authz_repos_name == NULL)
-    b->repos_name = svn_dirent_basename(repos_root, pool);
+                        svn_path_component_count(repository->fs_path->data));
+  repository->repos_url = url_buf->data;
+  repository->authz_repos_name = svn_dirent_is_child(root, repos_root, pool);
+  if (repository->authz_repos_name == NULL)
+    repository->repos_name = svn_dirent_basename(repos_root, pool);
   else
-    b->repos_name = b->authz_repos_name;
-  b->repos_name = svn_path_uri_encode(b->repos_name, pool);
+    repository->repos_name = repository->authz_repos_name;
+  repository->repos_name = svn_path_uri_encode(repository->repos_name, pool);
 
   /* If the svnserve configuration has not been loaded then load it from the
    * repository. */
-  if (NULL == b->cfg)
+  if (NULL == cfg)
     {
-      b->base = svn_repos_conf_dir(b->repos, pool);
+      repository->base = svn_repos_conf_dir(repository->repos, pool);
 
-      SVN_ERR(svn_config_read3(&b->cfg, svn_repos_svnserve_conf(b->repos, pool),
+      SVN_ERR(svn_config_read3(&cfg,
+                               svn_repos_svnserve_conf(repository->repos,
+                                                       pool),
                                FALSE, /* must_exist */
                                FALSE, /* section_names_case_sensitive */
                                FALSE, /* option_names_case_sensitive */
                                pool));
-      SVN_ERR(load_pwdb_config(b, conn, pool));
-      SVN_ERR(load_authz_config(b, conn, repos_root, pool));
-    }
-  /* svnserve.conf has been loaded via the --config-file option so need
-   * to load pwdb and authz. */
-  else
-    {
-      SVN_ERR(load_pwdb_config(b, conn, pool));
-      SVN_ERR(load_authz_config(b, conn, repos_root, pool));
     }
 
+  SVN_ERR(load_pwdb_config(repository, cfg, pool));
+  SVN_ERR(load_authz_config(repository, repos_root, cfg, pool));
+
 #ifdef SVN_HAVE_SASL
   /* Should we use Cyrus SASL? */
-  SVN_ERR(svn_config_get_bool(b->cfg, &b->use_sasl, SVN_CONFIG_SECTION_SASL,
+  SVN_ERR(svn_config_get_bool(cfg, &repository->use_sasl,
+                              SVN_CONFIG_SECTION_SASL,
                               SVN_CONFIG_OPTION_USE_SASL, FALSE));
+
+  svn_config_get(cfg, &val, SVN_CONFIG_SECTION_SASL,
+                 SVN_CONFIG_OPTION_MIN_SSF, "0");
+  SVN_ERR(svn_cstring_atoui(&repository->min_ssf, val));
+
+  svn_config_get(cfg, &val, SVN_CONFIG_SECTION_SASL,
+                 SVN_CONFIG_OPTION_MAX_SSF, "256");
+  SVN_ERR(svn_cstring_atoui(&repository->max_ssf, val));
+
 #endif
 
   /* Use the repository UUID as the default realm. */
-  SVN_ERR(svn_fs_get_uuid(b->fs, &b->realm, pool));
-  svn_config_get(b->cfg, &b->realm, SVN_CONFIG_SECTION_GENERAL,
-                 SVN_CONFIG_OPTION_REALM, b->realm);
+  SVN_ERR(svn_fs_get_uuid(repository->fs, &repository->realm, pool));
+  svn_config_get(cfg, &repository->realm, SVN_CONFIG_SECTION_GENERAL,
+                 SVN_CONFIG_OPTION_REALM, repository->realm);
 
   /* Make sure it's possible for the client to authenticate.  Note
      that this doesn't take into account any authz configuration read
      above, because we can't know about access it grants until paths
      are given by the client. */
-  if (get_access(b, UNAUTHENTICATED) == NO_ACCESS
-      && (get_access(b, AUTHENTICATED) == NO_ACCESS
-          || (!b->tunnel_user && !b->pwdb && !b->use_sasl)))
-    return error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
-                                 "No access allowed to this repository",
-                                 b, conn, pool);
+  set_access(repository, cfg, read_only);
 
   /* Configure hook script environment variables. */
-  svn_config_get(b->cfg, &hooks_env, SVN_CONFIG_SECTION_GENERAL,
+  svn_config_get(cfg, &hooks_env, SVN_CONFIG_SECTION_GENERAL,
                  SVN_CONFIG_OPTION_HOOKS_ENV, NULL);
   if (hooks_env)
     hooks_env = svn_dirent_internal_style(hooks_env, pool);
-  SVN_ERR(svn_repos_hooks_setenv(b->repos, hooks_env, pool));
+  SVN_ERR(svn_repos_hooks_setenv(repository->repos, hooks_env, pool));
 
   return SVN_NO_ERROR;
 }
@@ -3375,7 +3395,7 @@ static void
 fs_warning_func(void *baton, svn_error_t *err)
 {
   fs_warning_baton_t *b = baton;
-  log_server_error(err, b->server, b->conn, b->pool);
+  log_error(err, b->server);
   /* TODO: Keep log_pool in the server baton, cleared after every log? */
   svn_pool_clear(b->pool);
 }
@@ -3394,7 +3414,7 @@ get_normalized_repo_rel_path(void *baton
   if (svn_path_is_url(path))
     {
       /* This is a copyfrom URL. */
-      path = svn_uri_skip_ancestor(sb->repos_url, path, pool);
+      path = svn_uri_skip_ancestor(sb->repository->repos_url, path, pool);
       path = svn_fspath__canonicalize(path, pool);
     }
   else
@@ -3402,7 +3422,7 @@ get_normalized_repo_rel_path(void *baton
       /* This is a base-relative path. */
       if ((path)[0] != '/')
         /* Get an absolute path for use in the FS. */
-        path = svn_fspath__join(sb->fs_path->data, path, pool);
+        path = svn_fspath__join(sb->repository->fs_path->data, path, pool);
     }
 
   return path;
@@ -3421,9 +3441,9 @@ get_revision_root(svn_fs_root_t **fs_roo
   server_baton_t *sb = baton;
 
   if (!SVN_IS_VALID_REVNUM(revision))
-    SVN_ERR(svn_fs_youngest_rev(&revision, sb->fs, pool));
+    SVN_ERR(svn_fs_youngest_rev(&revision, sb->repository->fs, pool));
 
-  SVN_ERR(svn_fs_revision_root(fs_root, sb->fs, revision, pool));
+  SVN_ERR(svn_fs_revision_root(fs_root, sb->repository->fs, revision, pool));
 
   return SVN_NO_ERROR;
 }
@@ -3508,6 +3528,22 @@ fetch_base_func(const char **filename,
   return SVN_NO_ERROR;
 }
 
+client_info_t *
+get_client_info(svn_ra_svn_conn_t *conn,
+                serve_params_t *params,
+                apr_pool_t *pool)
+{
+  client_info_t *client_info = apr_pcalloc(pool, sizeof(*client_info));
+
+  client_info->tunnel = params->tunnel;
+  client_info->tunnel_user = get_tunnel_user(params, pool);
+  client_info->user = NULL;
+  client_info->authz_user = NULL;
+  client_info->remote_host = svn_ra_svn_conn_remote_host(conn);
+
+  return client_info;
+}
+
 svn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params,
                    apr_pool_t *pool)
 {
@@ -3515,33 +3551,33 @@ svn_error_t *serve(svn_ra_svn_conn_t *co
   apr_uint64_t ver;
   const char *uuid, *client_url, *ra_client_string, *client_string;
   apr_array_header_t *caplist, *cap_words;
-  server_baton_t b;
+  server_baton_t b = { 0 };
+  repository_t repository = { 0 };
   fs_warning_baton_t warn_baton;
   svn_stringbuf_t *cap_log = svn_stringbuf_create_empty(pool);
 
-  b.tunnel = params->tunnel;
-  b.tunnel_user = get_tunnel_user(params, pool);
+  repository.username_case = params->username_case;
+  repository.base = params->base;
+  repository.pwdb = NULL;
+  repository.authzdb = NULL;
+  repository.realm = NULL;
+  repository.use_sasl = FALSE;
+
   b.read_only = params->read_only;
-  b.user = NULL;
-  b.username_case = params->username_case;
-  b.authz_user = NULL;
-  b.base = params->base;
-  b.cfg = params->cfg;
-  b.pwdb = NULL;
-  b.authzdb = NULL;
-  b.realm = NULL;
-  b.log_file = params->log_file;
   b.pool = pool;
-  b.use_sasl = FALSE;
   b.vhost = params->vhost;
 
+  b.repository = &repository;
+  b.logger = params->logger;
+  b.client_info = get_client_info(conn, params, pool);
+
   /* construct FS configuration parameters */
-  b.fs_config = apr_hash_make(pool);
-  svn_hash_sets(b.fs_config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS,
+  repository.fs_config = apr_hash_make(pool);
+  svn_hash_sets(repository.fs_config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS,
                 params->cache_txdeltas ? "1" :"0");
-  svn_hash_sets(b.fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS,
+  svn_hash_sets(repository.fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS,
                 params->cache_fulltexts ? "1" :"0");
-  svn_hash_sets(b.fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
+  svn_hash_sets(repository.fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
                 params->cache_revprops ? "1" :"0");
 
   /* Send greeting.  We don't support version 1 any more, so we can
@@ -3624,19 +3660,30 @@ svn_error_t *serve(svn_ra_svn_conn_t *co
       }
   }
 
-  err = find_repos(client_url, params->root, &b, conn, cap_words, pool);
+  err = handle_config_error(find_repos(client_url, params->root,
+                                       b.vhost, b.read_only,
+                                       params->cfg, b.repository,
+                                       cap_words, pool), &b);
+  if (!err)
+    {
+      if (repository.anon_access == NO_ACCESS
+          && (repository.auth_access == NO_ACCESS
+              || (!b.client_info->tunnel_user && !repository.pwdb
+                  && !repository.use_sasl)))
+        err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
+                                   "No access allowed to this repository",
+                                   &b);
+    }
   if (!err)
     {
       SVN_ERR(auth_request(conn, pool, &b, READ_ACCESS, FALSE));
       if (current_access(&b) == NO_ACCESS)
         err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
-                                   "Not authorized for access",
-                                   &b, conn, pool);
+                                   "Not authorized for access", &b);
     }
   if (err)
     {
-      log_error(err, b.log_file, svn_ra_svn_conn_remote_host(conn),
-                b.user, NULL, pool);
+      log_error(err, &b);
       io_err = svn_ra_svn__write_cmd_failure(conn, pool, err);
       svn_error_clear(err);
       SVN_ERR(io_err);
@@ -3655,15 +3702,15 @@ svn_error_t *serve(svn_ra_svn_conn_t *co
   SVN_ERR(log_command(&b, conn, pool,
                       "open %" APR_UINT64_T_FMT " cap=(%s) %s %s %s",
                       ver, cap_log->data,
-                      svn_path_uri_encode(b.fs_path->data, pool),
+                      svn_path_uri_encode(b.repository->fs_path->data, pool),
                       ra_client_string, client_string));
 
   warn_baton.server = &b;
   warn_baton.conn = conn;
   warn_baton.pool = svn_pool_create(pool);
-  svn_fs_set_warning_func(b.fs, fs_warning_func, &warn_baton);
+  svn_fs_set_warning_func(b.repository->fs, fs_warning_func, &warn_baton);
 
-  SVN_ERR(svn_fs_get_uuid(b.fs, &uuid, pool));
+  SVN_ERR(svn_fs_get_uuid(b.repository->fs, &uuid, pool));
 
   /* We can't claim mergeinfo capability until we know whether the
      repository supports mergeinfo (i.e., is not a 1.4 repository),
@@ -3673,11 +3720,12 @@ svn_error_t *serve(svn_ra_svn_conn_t *co
      the client has sent the url. */
   {
     svn_boolean_t supports_mergeinfo;
-    SVN_ERR(svn_repos_has_capability(b.repos, &supports_mergeinfo,
+    SVN_ERR(svn_repos_has_capability(repository.repos, &supports_mergeinfo,
                                      SVN_REPOS_CAPABILITY_MERGEINFO, pool));
 
     SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(cc(!",
-                                    "success", uuid, b.repos_url));
+                                    "success", uuid,
+                                    b.repository->repos_url));
     if (supports_mergeinfo)
       SVN_ERR(svn_ra_svn__write_word(conn, pool, SVN_RA_SVN_CAP_MERGEINFO));
     SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))"));

Modified: subversion/branches/fsfs-improvements/subversion/svnserve/server.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/svnserve/server.h?rev=1532583&r1=1532582&r2=1532583&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/svnserve/server.h (original)
+++ subversion/branches/fsfs-improvements/subversion/svnserve/server.h Tue Oct 15 22:57:03 2013
@@ -36,14 +36,18 @@ extern "C" {
 #include "svn_repos.h"
 #include "svn_ra_svn.h"
 
+#include "private/svn_mutex.h"
+
 enum username_case_type { CASE_FORCE_UPPER, CASE_FORCE_LOWER, CASE_ASIS };
 
-typedef struct server_baton_t {
+enum authn_type { UNAUTHENTICATED, AUTHENTICATED };
+enum access_type { NO_ACCESS, READ_ACCESS, WRITE_ACCESS };
+
+typedef struct repository_t {
   svn_repos_t *repos;
   const char *repos_name;  /* URI-encoded name of repository (not for authz) */
   svn_fs_t *fs;            /* For convenience; same as svn_repos_fs(repos) */
   const char *base;        /* Base directory for config files */
-  svn_config_t *cfg;       /* Parsed repository svnserve.conf */
   svn_config_t *pwdb;      /* Parsed password database */
   svn_authz_t *authzdb;    /* Parsed authz rules */
   const char *authz_repos_name; /* The name of the repository for authz */
@@ -51,24 +55,35 @@ typedef struct server_baton_t {
   const char *repos_url;   /* URL to base of repository */
   svn_stringbuf_t *fs_path;/* Decoded base in-repos path (w/ leading slash) */
   apr_hash_t *fs_config;   /* Additional FS configuration parameters */
-  const char *user;        /* Authenticated username of the user */
   enum username_case_type username_case; /* Case-normalize the username? */
+  svn_boolean_t use_sasl;  /* Use Cyrus SASL for authentication;
+                              always false if SVN_HAVE_SASL not defined */
+  unsigned min_ssf;        /* min-encryption SASL parameter */
+  unsigned max_ssf;        /* max-encryption SASL parameter */
+
+  enum access_type auth_access; /* access granted to authenticated users */
+  enum access_type anon_access; /* access granted to annonymous users */
+  
+} repository_t;
+
+typedef struct client_info_t {
+  const char *user;        /* Authenticated username of the user */
+  const char *remote_host; /* IP of the client that contacted the server */
   const char *authz_user;  /* Username for authz ('user' + 'username_case') */
   svn_boolean_t tunnel;    /* Tunneled through login agent */
   const char *tunnel_user; /* Allow EXTERNAL to authenticate as this */
+} client_info_t;
+
+typedef struct server_baton_t {
+  repository_t *repository; /* repository-specific data to use */
+  client_info_t *client_info; /* client-specific data to use */
+  struct logger_t *logger; /* Log file data structure.
+                              May be NULL even if log_file is not. */
   svn_boolean_t read_only; /* Disallow write access (global flag) */
-  svn_boolean_t use_sasl;  /* Use Cyrus SASL for authentication;
-                              always false if SVN_HAVE_SASL not defined */
-  apr_file_t *log_file;    /* Log filehandle. */
   svn_boolean_t vhost;     /* Use virtual-host-based path to repo. */
   apr_pool_t *pool;
 } server_baton_t;
 
-enum authn_type { UNAUTHENTICATED, AUTHENTICATED };
-enum access_type { NO_ACCESS, READ_ACCESS, WRITE_ACCESS };
-
-enum access_type get_access(server_baton_t *b, enum authn_type auth);
-
 typedef struct serve_params_t {
   /* The virtual root of the repositories to serve.  The client URL
      path is interpreted relative to this root and is not allowed to
@@ -97,8 +112,8 @@ typedef struct serve_params_t {
      per-repository svnserve.conf are not read. */
   svn_config_t *cfg;
 
-  /* A filehandle open for writing logs to; possibly NULL. */
-  apr_file_t *log_file;
+  /* logging data structure; possibly NULL. */
+  struct logger_t *logger;
 
   /* Username case normalization style. */
   enum username_case_type username_case;
@@ -133,19 +148,16 @@ typedef struct serve_params_t {
   svn_boolean_t vhost;
 } serve_params_t;
 
+/* Return a client_info_t structure allocated in POOL and initialize it
+ * with data from CONN. */
+client_info_t * get_client_info(svn_ra_svn_conn_t *conn,
+                                serve_params_t *params,
+                                apr_pool_t *pool);
+
 /* Serve the connection CONN according to the parameters PARAMS. */
 svn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params,
                    apr_pool_t *pool);
 
-/* Load the password database for the listening server based on the
-   entries in the SERVER struct.
-
-   SERVER and CONN must not be NULL. The real errors will be logged with
-   SERVER and CONN but return generic errors to the client. */
-svn_error_t *load_pwdb_config(server_baton_t *server,
-                              svn_ra_svn_conn_t *conn,
-                              apr_pool_t *pool);
-
 /* Initialize the Cyrus SASL library. POOL is used for allocations. */
 svn_error_t *cyrus_init(apr_pool_t *pool);
 
@@ -162,13 +174,6 @@ svn_error_t *cyrus_auth_request(svn_ra_s
 apr_size_t escape_errorlog_item(char *dest, const char *source,
                                 apr_size_t buflen);
 
-/* Log ERR to LOG_FILE if LOG_FILE is not NULL.  Include REMOTE_HOST,
-   USER, and REPOS in the log if they are not NULL.  Allocate temporary
-   char buffers in POOL (which caller can then clear or dispose of). */
-void
-log_error(svn_error_t *err, apr_file_t *log_file, const char *remote_host,
-          const char *user, const char *repos, apr_pool_t *pool);
-
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */