summaryrefslogtreecommitdiff
path: root/subversion/svnserve/serve.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/svnserve/serve.c')
-rw-r--r--subversion/svnserve/serve.c1498
1 files changed, 933 insertions, 565 deletions
diff --git a/subversion/svnserve/serve.c b/subversion/svnserve/serve.c
index 23ef6ed..91f1d5a 100644
--- a/subversion/svnserve/serve.c
+++ b/subversion/svnserve/serve.c
@@ -61,6 +61,7 @@
#endif
#include "server.h"
+#include "logger.h"
typedef struct commit_callback_baton_t {
apr_pool_t *pool;
@@ -96,7 +97,6 @@ typedef struct file_revs_baton_t {
typedef struct fs_warning_baton_t {
server_baton_t *server;
svn_ra_svn_conn_t *conn;
- apr_pool_t *pool;
} fs_warning_baton_t;
typedef struct authz_baton_t {
@@ -104,81 +104,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 +132,7 @@ log_fail_and_flush(svn_error_t *err, server_baton_t *server,
{
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 +149,7 @@ static svn_error_t *log_command(server_baton_t *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 +163,11 @@ static svn_error_t *log_command(server_baton_t *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 +175,59 @@ 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 through
+ * CONFIG_POOL and store it in REPOSITORY->PWDB.
+ */
+static svn_error_t *
+load_pwdb_config(repository_t *repository,
+ svn_config_t *cfg,
+ svn_repos__config_pool_t *config_pool,
+ 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,
- FALSE, FALSE, pool);
+ err = svn_repos__config_pool_get(&repository->pwdb, NULL, config_pool,
+ pwdb_path, TRUE, FALSE,
+ repository->repos, 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 +240,7 @@ svn_error_t *load_pwdb_config(server_baton_t *server,
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 +252,11 @@ svn_error_t *load_pwdb_config(server_baton_t *server,
}
/* 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,26 +276,33 @@ canonicalize_access_file(const char **access_file, server_baton_t *server,
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;
}
-svn_error_t *load_authz_config(server_baton_t *server,
- svn_ra_svn_conn_t *conn,
- const char *repos_root,
- apr_pool_t *pool)
+/* Load the authz database for the listening server through AUTHZ_POOL
+ 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. */
+static svn_error_t *
+load_authz_config(repository_t *repository,
+ const char *repos_root,
+ svn_config_t *cfg,
+ svn_repos__authz_pool_t *authz_pool,
+ 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)
@@ -361,48 +310,71 @@ svn_error_t *load_authz_config(server_baton_t *server,
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,
- groupsdb_path, TRUE, pool);
+ err = svn_repos__authz_pool_get(&repository->authzdb, authz_pool,
+ authzdb_path, groupsdb_path, TRUE,
+ repository->repos, 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).
@@ -450,14 +422,16 @@ static svn_error_t *authz_check_access(svn_boolean_t *allowed,
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;
@@ -475,21 +449,23 @@ static svn_error_t *authz_check_access(svn_boolean_t *allowed,
/* 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;
}
@@ -507,14 +483,14 @@ static svn_error_t *authz_check_access_cb(svn_boolean_t *allowed,
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;
}
@@ -533,27 +509,50 @@ static svn_error_t *authz_commit_cb(svn_repos_authz_access_t required,
{
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;
}
-static enum access_type current_access(server_baton_t *b)
+/* 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)
{
- return get_access(b, (b->user) ? AUTHENTICATED : UNAUTHENTICATED);
+ 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);
+}
+
+/* Return the access level for the user in B.
+ */
+static enum access_type
+current_access(server_baton_t *b)
+{
+ 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
@@ -563,11 +562,11 @@ static svn_error_t *send_mechs(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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;
}
@@ -607,15 +606,15 @@ create_fs_access(server_baton_t *b, apr_pool_t *pool)
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);
@@ -637,19 +636,19 @@ static svn_error_t *auth(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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"));
@@ -657,11 +656,12 @@ static svn_error_t *auth(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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;
}
@@ -680,7 +680,7 @@ internal_auth_request(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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));
@@ -702,7 +702,7 @@ static svn_error_t *auth_request(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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
@@ -732,7 +732,6 @@ static svn_error_t *trivial_auth_request(svn_ra_svn_conn_t *conn,
*/
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)
@@ -743,12 +742,12 @@ static svn_boolean_t lookup_access(apr_pool_t *pool,
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;
}
@@ -758,7 +757,7 @@ static svn_boolean_t lookup_access(apr_pool_t *pool,
lookup has succeeded. */
if (current_access(baton) >= req
&& authorized
- && (! needs_username || baton->user))
+ && (! needs_username || baton->client_info->user))
return TRUE;
return FALSE;
@@ -791,7 +790,7 @@ static svn_error_t *must_have_access(svn_ra_svn_conn_t *conn,
/* 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);
@@ -803,17 +802,18 @@ static svn_error_t *must_have_access(svn_ra_svn_conn_t *conn,
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. */
@@ -966,17 +966,18 @@ static svn_error_t *accept_report(svn_boolean_t *only_empty_entry,
/* 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;
@@ -1028,7 +1029,7 @@ static svn_error_t *write_prop_diffs(svn_ra_svn_conn_t *conn,
/* Write out a lock to the client. */
static svn_error_t *write_lock(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- svn_lock_t *lock)
+ const svn_lock_t *lock)
{
const char *cdate, *edate;
@@ -1065,8 +1066,7 @@ get_props(apr_hash_t **props,
/* Hardcode the values for the committed revision, date, and author. */
SVN_ERR(svn_repos_get_committed_info(&crev, &cdate, &cauthor, root,
path, pool));
- str = svn_string_create(apr_psprintf(pool, "%ld", crev),
- pool);
+ str = svn_string_createf(pool, "%ld", crev);
svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_REV, str);
str = (cdate) ? svn_string_create(cdate, pool) : NULL;
svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_DATE, str);
@@ -1102,11 +1102,11 @@ static svn_error_t *reparent(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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;
}
@@ -1120,7 +1120,7 @@ static svn_error_t *get_latest_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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;
}
@@ -1138,7 +1138,7 @@ static svn_error_t *get_dated_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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;
}
@@ -1160,7 +1160,8 @@ static svn_error_t *do_change_rev_prop(svn_ra_svn_conn_t *conn,
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,
@@ -1239,9 +1240,10 @@ static svn_error_t *rev_proplist(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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, "!))"));
@@ -1265,9 +1267,9 @@ static svn_error_t *rev_prop(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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;
}
@@ -1294,15 +1296,14 @@ static svn_error_t *commit_done(const svn_commit_info_t *commit_info,
* 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)
@@ -1329,14 +1330,13 @@ static svn_error_t *add_lock_tokens(svn_ra_svn_conn_t *conn,
"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));
@@ -1345,45 +1345,58 @@ static svn_error_t *add_lock_tokens(svn_ra_svn_conn_t *conn,
return SVN_NO_ERROR;
}
+/* Implements svn_fs_lock_callback_t. */
+static svn_error_t *
+lock_cb(void *baton,
+ const char *path,
+ const svn_lock_t *lock,
+ svn_error_t *fs_err,
+ apr_pool_t *pool)
+{
+ server_baton_t *sb = baton;
+
+ log_error(fs_err, sb);
+
+ return SVN_NO_ERROR;
+}
+
/* Unlock the paths with lock tokens in LOCK_TOKENS, ignoring any errors.
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;
- apr_pool_t *iterpool;
-
- iterpool = svn_pool_create(pool);
+ apr_pool_t *subpool = svn_pool_create(pool);
+ apr_hash_t *targets = apr_hash_make(subpool);
+ svn_error_t *err;
for (i = 0; i < lock_tokens->nelts; ++i)
{
svn_ra_svn_item_t *item, *path_item, *token_item;
const char *path, *token, *full_path;
- svn_error_t *err;
- svn_pool_clear(iterpool);
item = &APR_ARRAY_IDX(lock_tokens, i, svn_ra_svn_item_t);
path_item = &APR_ARRAY_IDX(item->u.list, 0, svn_ra_svn_item_t);
token_item = &APR_ARRAY_IDX(item->u.list, 1, svn_ra_svn_item_t);
path = path_item->u.string->data;
+ full_path = svn_fspath__join(sb->repository->fs_path->data,
+ svn_relpath_canonicalize(path, subpool),
+ subpool);
token = token_item->u.string->data;
+ svn_hash_sets(targets, full_path, token);
+ }
- full_path = svn_fspath__join(sb->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,
- FALSE, iterpool);
- log_server_error(err, sb, conn, iterpool);
- svn_error_clear(err);
- }
+ /* The lock may have become defunct after the commit, so ignore such
+ errors. */
+ err = svn_repos_fs_unlock_many(sb->repository->repos, targets, FALSE,
+ lock_cb, sb, subpool, subpool);
+ log_error(err, sb);
+ svn_error_clear(err);
- svn_pool_destroy(iterpool);
+ svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
@@ -1392,13 +1405,13 @@ static svn_error_t *commit(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
- const char *log_msg = NULL,
+ const char *log_msg,
*date = NULL,
*author = NULL,
*post_commit_err = NULL;
apr_array_header_t *lock_tokens;
svn_boolean_t keep_locks;
- apr_array_header_t *revprop_list = NULL;
+ apr_array_header_t *revprop_list;
apr_hash_t *revprop_table;
const svn_delta_editor_t *editor;
void *edit_baton;
@@ -1439,7 +1452,7 @@ static svn_error_t *commit(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
/* 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)
@@ -1454,7 +1467,9 @@ static svn_error_t *commit(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
/* 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;
@@ -1463,9 +1478,9 @@ static svn_error_t *commit(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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, ""));
@@ -1482,18 +1497,18 @@ static svn_error_t *commit(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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;
}
@@ -1529,7 +1544,7 @@ static svn_error_t *get_file(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
if (wants_inherited_props == SVN_RA_SVN_UNSPECIFIED_NUMBER)
wants_inherited_props = FALSE;
- 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 */
@@ -1537,14 +1552,14 @@ static svn_error_t *get_file(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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);
@@ -1594,7 +1609,7 @@ static svn_error_t *get_file(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
while (1)
{
len = sizeof(buf);
- err = svn_stream_read(contents, buf, &len);
+ err = svn_stream_read_full(contents, buf, &len);
if (err)
break;
if (len > 0)
@@ -1683,7 +1698,7 @@ static svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *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);
/* Check authorizations */
@@ -1691,7 +1706,7 @@ static svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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,
@@ -1699,7 +1714,7 @@ static svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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. Although the wants-iprops boolean was added to the
@@ -1731,8 +1746,8 @@ static svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
subpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
{
- const char *name = svn__apr_hash_index_key(hi);
- svn_fs_dirent_t *fsent = svn__apr_hash_index_val(hi);
+ const char *name = apr_hash_this_key(hi);
+ svn_fs_dirent_t *fsent = apr_hash_this_val(hi);
const char *file_path;
/* The fields in the entry tuple. */
@@ -1751,8 +1766,7 @@ static svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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)
@@ -1765,12 +1779,9 @@ static svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
if (dirent_fields & SVN_DIRENT_HAS_PROPS)
{
- apr_hash_t *file_props;
-
/* has_props */
- SVN_CMD_ERR(svn_fs_node_proplist(&file_props, root, file_path,
+ SVN_CMD_ERR(svn_fs_node_has_props(&has_props, root, file_path,
subpool));
- has_props = (apr_hash_count(file_props) > 0);
}
if ((dirent_fields & SVN_DIRENT_LAST_AUTHOR)
@@ -1834,15 +1845,15 @@ static svn_error_t *update(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
svn_revnum_t rev;
const char *target, *full_path, *depth_word;
svn_boolean_t recurse;
- apr_uint64_t send_copyfrom_args; /* Optional; default FALSE */
- apr_uint64_t ignore_ancestry; /* Optional; default FALSE */
+ svn_tristate_t send_copyfrom_args; /* Optional; default FALSE */
+ svn_tristate_t ignore_ancestry; /* Optional; default FALSE */
/* Default to unknown. Old clients won't send depth, but we'll
handle that by converting recurse if necessary. */
svn_depth_t depth = svn_depth_unknown;
svn_boolean_t is_checkout;
/* Parse the arguments. */
- SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cb?wB?B", &rev, &target,
+ SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cb?w3?3", &rev, &target,
&recurse, &depth_word,
&send_copyfrom_args, &ignore_ancestry));
target = svn_relpath_canonicalize(target, pool);
@@ -1852,18 +1863,18 @@ static svn_error_t *update(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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,
depth,
- (send_copyfrom_args == TRUE) /* send_copyfrom_args */,
- (ignore_ancestry == TRUE) /* ignore_ancestry */));
+ (send_copyfrom_args == svn_tristate_true),
+ (ignore_ancestry == svn_tristate_true)));
if (is_checkout)
{
SVN_ERR(log_command(b, conn, pool, "%s",
@@ -1874,7 +1885,9 @@ static svn_error_t *update(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
{
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__update(full_path, rev, depth,
- send_copyfrom_args, pool)));
+ (send_copyfrom_args
+ == svn_tristate_true),
+ pool)));
}
return SVN_NO_ERROR;
@@ -1891,11 +1904,11 @@ static svn_error_t *switch_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
/* Default to unknown. Old clients won't send depth, but we'll
handle that by converting recurse if necessary. */
svn_depth_t depth = svn_depth_unknown;
- apr_uint64_t send_copyfrom_args; /* Optional; default FALSE */
- apr_uint64_t ignore_ancestry; /* Optional; default TRUE */
+ svn_tristate_t send_copyfrom_args; /* Optional; default FALSE */
+ svn_tristate_t ignore_ancestry; /* Optional; default TRUE */
/* Parse the arguments. */
- SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbc?w?BB", &rev, &target,
+ SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbc?w?33", &rev, &target,
&recurse, &switch_url, &depth_word,
&send_copyfrom_args, &ignore_ancestry));
target = svn_relpath_canonicalize(target, pool);
@@ -1908,14 +1921,16 @@ static svn_error_t *switch_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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)));
@@ -1924,8 +1939,8 @@ static svn_error_t *switch_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
return accept_report(NULL, NULL,
conn, pool, b, rev, target, switch_path, TRUE,
depth,
- (send_copyfrom_args == TRUE) /* send_copyfrom_args */,
- (ignore_ancestry != FALSE) /* ignore_ancestry */);
+ (send_copyfrom_args == svn_tristate_true),
+ (ignore_ancestry != svn_tristate_false));
}
static svn_error_t *status(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
@@ -1951,10 +1966,11 @@ static svn_error_t *status(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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)));
}
@@ -2002,13 +2018,15 @@ static svn_error_t *diff(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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,
@@ -2060,7 +2078,7 @@ static svn_error_t *get_mergeinfo(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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;
}
@@ -2070,20 +2088,20 @@ static svn_error_t *get_mergeinfo(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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))
{
- const char *key = svn__apr_hash_index_key(hi);
- svn_mergeinfo_t value = svn__apr_hash_index_val(hi);
+ const char *key = apr_hash_this_key(hi);
+ svn_mergeinfo_t value = apr_hash_this_val(hi);
svn_string_t *mergeinfo_string;
svn_pool_clear(iterpool);
@@ -2107,9 +2125,8 @@ static svn_error_t *log_receiver(void *baton,
svn_ra_svn_conn_t *conn = b->conn;
apr_hash_index_t *h;
svn_boolean_t invalid_revnum = FALSE;
- char action[2];
- const char *author, *date, *message;
- apr_uint64_t revprop_count;
+ const svn_string_t *author, *date, *message;
+ unsigned revprop_count;
if (log_entry->revision == SVN_INVALID_REVNUM)
{
@@ -2125,43 +2142,57 @@ static svn_error_t *log_receiver(void *baton,
b->stack_depth--;
}
- SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "(!"));
+ svn_compat_log_revprops_out_string(&author, &date, &message,
+ log_entry->revprops);
+ svn_compat_log_revprops_clear(log_entry->revprops);
+ if (log_entry->revprops)
+ revprop_count = apr_hash_count(log_entry->revprops);
+ else
+ revprop_count = 0;
+
+ /* send LOG_ENTRY */
+ SVN_ERR(svn_ra_svn__start_list(conn, pool));
+
+ /* send LOG_ENTRY->CHANGED_PATHS2 */
+ SVN_ERR(svn_ra_svn__start_list(conn, pool));
if (log_entry->changed_paths2)
{
for (h = apr_hash_first(pool, log_entry->changed_paths2); h;
h = apr_hash_next(h))
{
- const char *path = svn__apr_hash_index_key(h);
- svn_log_changed_path2_t *change = svn__apr_hash_index_val(h);
+ const char *path = apr_hash_this_key(h);
+ svn_log_changed_path2_t *change = apr_hash_this_val(h);
- action[0] = change->action;
- action[1] = '\0';
- SVN_ERR(svn_ra_svn__write_tuple(
- conn, pool, "cw(?cr)(cbb)",
+ SVN_ERR(svn_ra_svn__write_data_log_changed_path(
+ conn, pool,
path,
- action,
+ change->action,
change->copyfrom_path,
change->copyfrom_rev,
- svn_node_kind_to_word(change->node_kind),
+ change->node_kind,
/* text_modified and props_modified are never unknown */
change->text_modified == svn_tristate_true,
change->props_modified == svn_tristate_true));
}
}
- svn_compat_log_revprops_out(&author, &date, &message, log_entry->revprops);
- svn_compat_log_revprops_clear(log_entry->revprops);
- if (log_entry->revprops)
- revprop_count = apr_hash_count(log_entry->revprops);
- else
- revprop_count = 0;
- SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)r(?c)(?c)(?c)bbn(!",
- log_entry->revision,
- author, date, message,
- log_entry->has_children,
- invalid_revnum, revprop_count));
- SVN_ERR(svn_ra_svn__write_proplist(conn, pool, log_entry->revprops));
- SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)b",
- log_entry->subtractive_merge));
+ SVN_ERR(svn_ra_svn__end_list(conn, pool));
+
+ /* send LOG_ENTRY main members */
+ SVN_ERR(svn_ra_svn__write_data_log_entry(conn, pool,
+ log_entry->revision,
+ author, date, message,
+ log_entry->has_children,
+ invalid_revnum, revprop_count));
+
+ /* send LOG_ENTRY->REVPROPS */
+ SVN_ERR(svn_ra_svn__start_list(conn, pool));
+ if (revprop_count)
+ SVN_ERR(svn_ra_svn__write_proplist(conn, pool, log_entry->revprops));
+ SVN_ERR(svn_ra_svn__end_list(conn, pool));
+
+ /* send LOG_ENTRY members that were added in later SVN releases */
+ SVN_ERR(svn_ra_svn__write_boolean(conn, pool, log_entry->subtractive_merge));
+ SVN_ERR(svn_ra_svn__end_list(conn, pool));
if (log_entry->has_children)
b->stack_depth++;
@@ -2239,7 +2270,8 @@ static svn_error_t *log_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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));
@@ -2251,14 +2283,14 @@ static svn_error_t *log_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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_logs4(b->repository->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);
write_err = svn_ra_svn__write_word(conn, pool, "done");
if (write_err)
@@ -2281,7 +2313,7 @@ static svn_error_t *check_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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 */
@@ -2289,12 +2321,12 @@ static svn_error_t *check_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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)));
@@ -2311,7 +2343,7 @@ static svn_error_t *stat_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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 */
@@ -2319,12 +2351,12 @@ static svn_error_t *stat_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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
@@ -2372,7 +2404,8 @@ static svn_error_t *get_locations(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
&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));
@@ -2397,8 +2430,9 @@ static svn_error_t *get_locations(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
/* 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);
@@ -2412,8 +2446,8 @@ static svn_error_t *get_locations(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
for (iter = apr_hash_first(pool, fs_locations); iter;
iter = apr_hash_next(iter))
{
- const svn_revnum_t *iter_key = svn__apr_hash_index_key(iter);
- const char *iter_value = svn__apr_hash_index_val(iter);
+ const svn_revnum_t *iter_key = apr_hash_this_key(iter);
+ const char *iter_value = apr_hash_this_val(iter);
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "rc",
*iter_key, iter_value));
@@ -2466,7 +2500,8 @@ static svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn,
&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);
SVN_ERR(trivial_auth_request(conn, pool, b));
SVN_ERR(log_command(baton, conn, pool, "%s",
@@ -2479,7 +2514,16 @@ static svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn,
{
svn_revnum_t youngest;
- SVN_CMD_ERR(svn_fs_youngest_rev(&youngest, b->fs, pool));
+ err = svn_fs_youngest_rev(&youngest, b->repository->fs, pool);
+
+ if (err)
+ {
+ err = svn_error_compose_create(
+ svn_ra_svn__write_word(conn, pool, "done"),
+ err);
+
+ return log_fail_and_flush(err, b, conn, pool);
+ }
if (!SVN_IS_VALID_REVNUM(start_rev))
start_rev = youngest;
@@ -2493,7 +2537,8 @@ static svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn,
if (end_rev > start_rev)
{
- err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
+ err = svn_ra_svn__write_word(conn, pool, "done");
+ err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, err,
"Get-location-segments end revision must not be "
"younger than start revision");
return log_fail_and_flush(err, b, conn, pool);
@@ -2501,7 +2546,8 @@ static svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn,
if (start_rev > peg_revision)
{
- err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
+ err = svn_ra_svn__write_word(conn, pool, "done");
+ err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, err,
"Get-location-segments start revision must not "
"be younger than peg revision");
return log_fail_and_flush(err, b, conn, pool);
@@ -2513,7 +2559,7 @@ static svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn,
/* 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,
@@ -2521,8 +2567,7 @@ static svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn,
write_err = svn_ra_svn__write_word(conn, pool, "done");
if (write_err)
{
- svn_error_clear(err);
- return write_err;
+ return svn_error_compose_create(write_err, err);
}
SVN_CMD_ERR(err);
@@ -2621,7 +2666,7 @@ static svn_error_t *get_file_revs(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
&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;
@@ -2636,8 +2681,8 @@ static svn_error_t *get_file_revs(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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");
@@ -2665,7 +2710,7 @@ static svn_error_t *lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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,
@@ -2673,8 +2718,8 @@ static svn_error_t *lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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"));
@@ -2684,6 +2729,48 @@ static svn_error_t *lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
return SVN_NO_ERROR;
}
+struct lock_result_t {
+ const svn_lock_t *lock;
+ svn_error_t *err;
+};
+
+struct lock_many_baton_t {
+ apr_hash_t *results;
+ apr_pool_t *pool;
+};
+
+/* Implements svn_fs_lock_callback_t. */
+static svn_error_t *
+lock_many_cb(void *baton,
+ const char *path,
+ const svn_lock_t *fs_lock,
+ svn_error_t *fs_err,
+ apr_pool_t *pool)
+{
+ struct lock_many_baton_t *b = baton;
+ struct lock_result_t *result = apr_palloc(b->pool,
+ sizeof(struct lock_result_t));
+
+ result->lock = fs_lock;
+ result->err = svn_error_dup(fs_err);
+ svn_hash_sets(b->results, apr_pstrdup(b->pool, path), result);
+
+ return SVN_NO_ERROR;
+}
+
+static void
+clear_lock_result_hash(apr_hash_t *results,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_index_t *hi;
+
+ for (hi = apr_hash_first(scratch_pool, results); hi; hi = apr_hash_next(hi))
+ {
+ struct lock_result_t *result = apr_hash_this_val(hi);
+ svn_error_clear(result->err);
+ }
+}
+
static svn_error_t *lock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
@@ -2693,12 +2780,11 @@ static svn_error_t *lock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
svn_boolean_t steal_lock;
int i;
apr_pool_t *subpool;
- const char *path;
- const char *full_path;
- svn_revnum_t current_rev;
- apr_array_header_t *log_paths;
- svn_lock_t *l;
- svn_error_t *err = SVN_NO_ERROR, *write_err;
+ svn_error_t *err, *write_err = SVN_NO_ERROR;
+ apr_hash_t *targets = apr_hash_make(pool);
+ apr_hash_t *authz_results = apr_hash_make(pool);
+ apr_hash_index_t *hi;
+ struct lock_many_baton_t lmb;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?c)bl", &comment, &steal_lock,
&path_revs));
@@ -2711,12 +2797,14 @@ static svn_error_t *lock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
an error. */
SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, TRUE));
- /* Loop through the lock requests. */
- log_paths = apr_array_make(pool, path_revs->nelts, sizeof(full_path));
+ /* Parse the lock requests from PATH_REVS into TARGETS. */
for (i = 0; i < path_revs->nelts; ++i)
{
+ const char *path, *full_path;
+ svn_revnum_t current_rev;
svn_ra_svn_item_t *item = &APR_ARRAY_IDX(path_revs, i,
svn_ra_svn_item_t);
+ svn_fs_lock_target_t *target;
svn_pool_clear(subpool);
@@ -2724,56 +2812,112 @@ static svn_error_t *lock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
"Lock requests should be list of lists");
- SVN_ERR(svn_ra_svn__parse_tuple(item->u.list, pool, "c(?r)", &path,
+ SVN_ERR(svn_ra_svn__parse_tuple(item->u.list, subpool, "c(?r)", &path,
&current_rev));
- /* 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;
+ target = svn_fs_lock_target_create(NULL, current_rev, pool);
+
+ /* Any duplicate paths, once canonicalized, get collapsed into a
+ single path that is processed once. The result is then
+ returned multiple times. */
+ svn_hash_sets(targets, full_path, target);
+ }
+
+ SVN_ERR(log_command(b, conn, subpool, "%s",
+ svn_log__lock(targets, steal_lock, subpool)));
- if (! lookup_access(pool, b, conn, svn_authz_write, full_path, TRUE))
+ /* Check authz.
+
+ Note: From here on we need to make sure any errors in authz_results, or
+ results, are cleared before returning from this function. */
+ for (hi = apr_hash_first(pool, targets); hi; hi = apr_hash_next(hi))
+ {
+ const char *full_path = apr_hash_this_key(hi);
+
+ svn_pool_clear(subpool);
+
+ if (! lookup_access(subpool, b, svn_authz_write, full_path, TRUE))
{
- err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, NULL,
- b, conn, pool);
- break;
+ struct lock_result_t *result
+ = apr_palloc(pool, sizeof(struct lock_result_t));
+
+ result->lock = NULL;
+ result->err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED,
+ NULL, NULL, b);
+ svn_hash_sets(authz_results, full_path, result);
+ svn_hash_sets(targets, full_path, NULL);
}
+ }
- err = svn_repos_fs_lock(&l, b->repos, full_path,
- NULL, comment, FALSE,
- 0, /* No expiration time. */
- current_rev,
- steal_lock, subpool);
+ lmb.results = apr_hash_make(pool);
+ lmb.pool = pool;
- if (err)
+ err = svn_repos_fs_lock_many(b->repository->repos, targets,
+ comment, FALSE,
+ 0, /* No expiration time. */
+ steal_lock, lock_many_cb, &lmb,
+ pool, subpool);
+
+ /* Return results in the same order as the paths were supplied. */
+ for (i = 0; i < path_revs->nelts; ++i)
+ {
+ const char *path, *full_path;
+ svn_revnum_t current_rev;
+ svn_ra_svn_item_t *item = &APR_ARRAY_IDX(path_revs, i,
+ svn_ra_svn_item_t);
+ struct lock_result_t *result;
+
+ svn_pool_clear(subpool);
+
+ write_err = svn_ra_svn__parse_tuple(item->u.list, subpool, "c(?r)", &path,
+ &current_rev);
+ if (write_err)
+ break;
+
+ full_path = svn_fspath__join(b->repository->fs_path->data,
+ svn_relpath_canonicalize(path, subpool),
+ subpool);
+
+ result = svn_hash_gets(lmb.results, full_path);
+ if (!result)
+ result = svn_hash_gets(authz_results, full_path);
+ if (!result)
{
- if (SVN_ERR_IS_LOCK_ERROR(err))
- {
- write_err = svn_ra_svn__write_cmd_failure(conn, pool, err);
- svn_error_clear(err);
- err = NULL;
- SVN_ERR(write_err);
- }
- else
- break;
+ /* No result? Something really odd happened, create a
+ placeholder error so that any other results can be
+ reported in the correct order. */
+ result = apr_palloc(pool, sizeof(struct lock_result_t));
+ result->err = svn_error_createf(SVN_ERR_FS_LOCK_OPERATION_FAILED, 0,
+ _("No result for '%s'."), path);
+ svn_hash_sets(lmb.results, full_path, result);
}
+
+ if (result->err)
+ write_err = svn_ra_svn__write_cmd_failure(conn, subpool,
+ result->err);
else
{
- SVN_ERR(svn_ra_svn__write_tuple(conn, subpool, "w!", "success"));
- SVN_ERR(write_lock(conn, subpool, l));
- SVN_ERR(svn_ra_svn__write_tuple(conn, subpool, "!"));
+ write_err = svn_ra_svn__write_tuple(conn, subpool,
+ "w!", "success");
+ if (!write_err)
+ write_err = write_lock(conn, subpool, result->lock);
+ if (!write_err)
+ write_err = svn_ra_svn__write_tuple(conn, subpool, "!");
}
+ if (write_err)
+ break;
}
- svn_pool_destroy(subpool);
+ clear_lock_result_hash(authz_results, subpool);
+ clear_lock_result_hash(lmb.results, subpool);
- SVN_ERR(log_command(b, conn, pool, "%s",
- svn_log__lock(log_paths, steal_lock, pool)));
+ svn_pool_destroy(subpool);
- /* NOTE: err might contain a fatal locking error from the loop above. */
- write_err = svn_ra_svn__write_word(conn, pool, "done");
+ if (!write_err)
+ write_err = svn_ra_svn__write_word(conn, pool, "done");
if (!write_err)
SVN_CMD_ERR(err);
svn_error_clear(err);
@@ -2793,7 +2937,7 @@ static svn_error_t *unlock(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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. */
@@ -2802,8 +2946,8 @@ static svn_error_t *unlock(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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, ""));
@@ -2818,11 +2962,11 @@ static svn_error_t *unlock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *unlock_tokens;
int i;
apr_pool_t *subpool;
- const char *path;
- const char *full_path;
- apr_array_header_t *log_paths;
- const char *token;
- svn_error_t *err = SVN_NO_ERROR, *write_err;
+ svn_error_t *err = SVN_NO_ERROR, *write_err = SVN_NO_ERROR;
+ apr_hash_t *targets = apr_hash_make(pool);
+ apr_hash_t *authz_results = apr_hash_make(pool);
+ apr_hash_index_t *hi;
+ struct lock_many_baton_t lmb;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "bl", &break_lock,
&unlock_tokens));
@@ -2832,12 +2976,12 @@ static svn_error_t *unlock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
subpool = svn_pool_create(pool);
- /* Loop through the unlock requests. */
- log_paths = apr_array_make(pool, unlock_tokens->nelts, sizeof(full_path));
+ /* Parse the unlock requests from PATH_REVS into TARGETS. */
for (i = 0; i < unlock_tokens->nelts; i++)
{
svn_ra_svn_item_t *item = &APR_ARRAY_IDX(unlock_tokens, i,
svn_ra_svn_item_t);
+ const char *path, *full_path, *token;
svn_pool_clear(subpool);
@@ -2847,51 +2991,106 @@ static svn_error_t *unlock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
SVN_ERR(svn_ra_svn__parse_tuple(item->u.list, subpool, "c(?c)", &path,
&token));
+ if (!token)
+ token = "";
- /* 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,
+ /* Any duplicate paths, once canonicalized, get collapsed into a
+ single path that is processed once. The result is then
+ returned multiple times. */
+ svn_hash_sets(targets, full_path, token);
+ }
+
+ SVN_ERR(log_command(b, conn, subpool, "%s",
+ svn_log__unlock(targets, break_lock, subpool)));
+
+ /* Check authz.
+
+ Note: From here on we need to make sure any errors in authz_results, or
+ results, are cleared before returning from this function. */
+ for (hi = apr_hash_first(pool, targets); hi; hi = apr_hash_next(hi))
+ {
+ const char *full_path = apr_hash_this_key(hi);
+
+ svn_pool_clear(subpool);
+
+ 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);
-
- err = svn_repos_fs_unlock(b->repos, full_path, token, break_lock,
- subpool);
- if (err)
{
- if (SVN_ERR_IS_UNLOCK_ERROR(err))
- {
- write_err = svn_ra_svn__write_cmd_failure(conn, pool, err);
- svn_error_clear(err);
- err = NULL;
- SVN_ERR(write_err);
- }
- else
- break;
+ struct lock_result_t *result
+ = apr_palloc(pool, sizeof(struct lock_result_t));
+
+ result->lock = NULL;
+ result->err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED,
+ NULL, NULL, b);
+ svn_hash_sets(authz_results, full_path, result);
+ svn_hash_sets(targets, full_path, NULL);
}
+ }
+
+ lmb.results = apr_hash_make(pool);
+ lmb.pool = pool;
+
+ err = svn_repos_fs_unlock_many(b->repository->repos, targets,
+ break_lock, lock_many_cb, &lmb,
+ pool, subpool);
+
+ /* Return results in the same order as the paths were supplied. */
+ for (i = 0; i < unlock_tokens->nelts; ++i)
+ {
+ const char *path, *token, *full_path;
+ svn_ra_svn_item_t *item = &APR_ARRAY_IDX(unlock_tokens, i,
+ svn_ra_svn_item_t);
+ struct lock_result_t *result;
+
+ svn_pool_clear(subpool);
+
+ write_err = svn_ra_svn__parse_tuple(item->u.list, subpool, "c(?c)", &path,
+ &token);
+ if (write_err)
+ break;
+
+ full_path = svn_fspath__join(b->repository->fs_path->data,
+ svn_relpath_canonicalize(path, subpool),
+ pool);
+
+ result = svn_hash_gets(lmb.results, full_path);
+ if (!result)
+ result = svn_hash_gets(authz_results, full_path);
+ if (!result)
+ {
+ /* No result? Something really odd happened, create a
+ placeholder error so that any other results can be
+ reported in the correct order. */
+ result = apr_palloc(pool, sizeof(struct lock_result_t));
+ result->err = svn_error_createf(SVN_ERR_FS_LOCK_OPERATION_FAILED, 0,
+ _("No result for '%s'."), path);
+ svn_hash_sets(lmb.results, full_path, result);
+ }
+
+ if (result->err)
+ write_err = svn_ra_svn__write_cmd_failure(conn, pool, result->err);
else
- SVN_ERR(svn_ra_svn__write_tuple(conn, subpool, "w(c)", "success",
- path));
+ write_err = svn_ra_svn__write_tuple(conn, subpool, "w(c)", "success",
+ path);
+ if (write_err)
+ break;
}
- svn_pool_destroy(subpool);
+ clear_lock_result_hash(authz_results, subpool);
+ clear_lock_result_hash(lmb.results, subpool);
- SVN_ERR(log_command(b, conn, pool, "%s",
- svn_log__unlock(log_paths, break_lock, pool)));
+ svn_pool_destroy(subpool);
- /* NOTE: err might contain a fatal unlocking error from the loop above. */
- write_err = svn_ra_svn__write_word(conn, pool, "done");
+ if (!write_err)
+ write_err = svn_ra_svn__write_word(conn, pool, "done");
if (! write_err)
SVN_CMD_ERR(err);
svn_error_clear(err);
+ SVN_ERR(write_err);
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
return SVN_NO_ERROR;
@@ -2907,7 +3106,7 @@ static svn_error_t *get_lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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,
@@ -2915,7 +3114,7 @@ static svn_error_t *get_lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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)
@@ -2954,21 +3153,22 @@ static svn_error_t *get_locks(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success"));
for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi))
{
- svn_lock_t *l = svn__apr_hash_index_val(hi);
+ svn_lock_t *l = apr_hash_this_val(hi);
SVN_ERR(write_lock(conn, pool, l));
}
@@ -2994,15 +3194,16 @@ static svn_error_t *replay_one_revision(svn_ra_svn_conn_t *conn,
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)
@@ -3057,7 +3258,8 @@ static svn_error_t *replay_range(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
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));
@@ -3090,12 +3292,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;
}
@@ -3121,7 +3323,7 @@ get_inherited_props(svn_ra_svn_conn_t *conn,
/* 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);
@@ -3130,14 +3332,14 @@ get_inherited_props(svn_ra_svn_conn_t *conn,
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. */
@@ -3251,18 +3453,30 @@ 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
- * capabilities to CAPABILITIES, which must be at least as long-lived
- * as POOL, and whose elements are SVN_RA_CAPABILITY_*.
+ * repository root. If we find one, fill in the repos, fs, repos_url,
+ * and fs_path fields of REPOSITORY. VHOST and READ_ONLY flags are the
+ * same as in the server baton.
+ *
+ * CONFIG_POOL and AUTHZ_POOL shall be used to load any object of the
+ * respective type.
+ *
+ * Use SCRATCH_POOL for temporary allocations.
+ *
*/
-static svn_error_t *find_repos(const char *url, const char *root,
- server_baton_t *b,
- svn_ra_svn_conn_t *conn,
- const apr_array_header_t *capabilities,
- apr_pool_t *pool)
-{
- const char *path, *full_path, *repos_root, *fs_path, *hooks_env;
+static svn_error_t *
+find_repos(const char *url,
+ const char *root,
+ svn_boolean_t vhost,
+ svn_boolean_t read_only,
+ svn_config_t *cfg,
+ repository_t *repository,
+ svn_repos__config_pool_t *config_pool,
+ svn_repos__authz_pool_t *authz_pool,
+ apr_hash_t *fs_config,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *path, *full_path, *fs_path, *hooks_env;
svn_stringbuf_t *url_buf;
/* Skip past the scheme and authority part. */
@@ -3271,14 +3485,14 @@ static svn_error_t *find_repos(const char *url, const char *root,
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)
path = "";
}
- path = svn_relpath_canonicalize(path, pool);
- path = svn_path_uri_decode(path, pool);
+ path = svn_relpath_canonicalize(path, scratch_pool);
+ path = svn_path_uri_decode(path, scratch_pool);
/* Ensure that it isn't possible to escape the root by disallowing
'..' segments. */
@@ -3287,82 +3501,95 @@ static svn_error_t *find_repos(const char *url, const char *root,
"Couldn't determine repository path");
/* Join the server-configured root with the client path. */
- full_path = svn_dirent_join(svn_dirent_canonicalize(root, pool),
- path, pool);
+ full_path = svn_dirent_join(svn_dirent_canonicalize(root, scratch_pool),
+ path, scratch_pool);
/* Search for a repository in the full path. */
- repos_root = svn_repos_find_root_path(full_path, pool);
- if (!repos_root)
+ repository->repos_root = svn_repos_find_root_path(full_path, result_pool);
+ if (!repository->repos_root)
return svn_error_createf(SVN_ERR_RA_SVN_REPOS_NOT_FOUND, NULL,
"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);
- fs_path = full_path + strlen(repos_root);
- b->fs_path = svn_stringbuf_create(*fs_path ? fs_path : "/", pool);
- url_buf = svn_stringbuf_create(url, pool);
+ SVN_ERR(svn_repos_open3(&repository->repos, repository->repos_root,
+ fs_config, result_pool, scratch_pool));
+ SVN_ERR(svn_repos_remember_client_capabilities(repository->repos,
+ repository->capabilities));
+ repository->fs = svn_repos_fs(repository->repos);
+ fs_path = full_path + strlen(repository->repos_root);
+ repository->fs_path = svn_stringbuf_create(*fs_path ? fs_path : "/",
+ result_pool);
+ url_buf = svn_stringbuf_create(url, result_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,
+ repository->repos_root,
+ result_pool);
+ if (repository->authz_repos_name == NULL)
+ repository->repos_name = svn_dirent_basename(repository->repos_root,
+ result_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,
+ result_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, result_pool);
- SVN_ERR(svn_config_read3(&b->cfg, svn_repos_svnserve_conf(b->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(svn_repos__config_pool_get(&cfg, NULL, config_pool,
+ svn_repos_svnserve_conf
+ (repository->repos, result_pool),
+ FALSE, FALSE, repository->repos,
+ result_pool));
}
+ SVN_ERR(load_pwdb_config(repository, cfg, config_pool, result_pool));
+ SVN_ERR(load_authz_config(repository, repository->repos_root, cfg,
+ authz_pool, result_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_CONFIG_OPTION_USE_SASL, FALSE));
+ {
+ const char *val;
+
+ /* Should we use Cyrus 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, scratch_pool));
+ svn_config_get(cfg, &repository->realm, SVN_CONFIG_SECTION_GENERAL,
+ SVN_CONFIG_OPTION_REALM, repository->realm);
+ repository->realm = apr_pstrdup(result_pool, 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));
+ hooks_env = svn_dirent_internal_style(hooks_env, scratch_pool);
+
+ SVN_ERR(svn_repos_hooks_setenv(repository->repos, hooks_env, scratch_pool));
+ repository->hooks_env = apr_pstrdup(result_pool, hooks_env);
return SVN_NO_ERROR;
}
@@ -3385,9 +3612,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);
- /* TODO: Keep log_pool in the server baton, cleared after every log? */
- svn_pool_clear(b->pool);
+ log_error(err, b->server);
}
/* Return the normalized repository-relative path for the given PATH
@@ -3404,7 +3629,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
@@ -3412,7 +3637,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;
@@ -3431,9 +3656,9 @@ get_revision_root(svn_fs_root_t **fs_root,
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;
}
@@ -3518,46 +3743,60 @@ fetch_base_func(const char **filename,
return SVN_NO_ERROR;
}
-svn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params,
- apr_pool_t *pool)
+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;
+}
+
+/* Construct the server baton for CONN using PARAMS and return it in *BATON.
+ * It's lifetime is the same as that of CONN. SCRATCH_POOL
+ */
+static svn_error_t *
+construct_server_baton(server_baton_t **baton,
+ svn_ra_svn_conn_t *conn,
+ serve_params_t *params,
+ apr_pool_t *scratch_pool)
{
svn_error_t *err, *io_err;
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;
- 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);
- 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;
-
- /* construct FS configuration parameters */
- b.fs_config = apr_hash_make(pool);
- svn_hash_sets(b.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,
- params->cache_fulltexts ? "1" :"0");
- svn_hash_sets(b.fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
- params->cache_revprops ? "1" :"0");
+ const char *client_url, *ra_client_string, *client_string;
+ apr_array_header_t *caplist;
+ apr_pool_t *conn_pool = svn_ra_svn__get_pool(conn);
+ server_baton_t *b = apr_pcalloc(conn_pool, sizeof(*b));
+ fs_warning_baton_t *warn_baton;
+ svn_stringbuf_t *cap_log = svn_stringbuf_create_empty(scratch_pool);
+
+ b->repository = apr_pcalloc(conn_pool, sizeof(*b->repository));
+ b->repository->username_case = params->username_case;
+ b->repository->base = params->base;
+ b->repository->pwdb = NULL;
+ b->repository->authzdb = NULL;
+ b->repository->realm = NULL;
+ b->repository->use_sasl = FALSE;
+
+ b->read_only = params->read_only;
+ b->pool = conn_pool;
+ b->vhost = params->vhost;
+
+ b->logger = params->logger;
+ b->client_info = get_client_info(conn, params, conn_pool);
/* Send greeting. We don't support version 1 any more, so we can
* send an empty mechlist. */
if (params->compression_level > 0)
- SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "nn()(wwwwwwwwwww)",
+ SVN_ERR(svn_ra_svn__write_cmd_response(conn, scratch_pool,
+ "nn()(wwwwwwwwwww)",
(apr_uint64_t) 2, (apr_uint64_t) 2,
SVN_RA_SVN_CAP_EDIT_PIPELINE,
SVN_RA_SVN_CAP_SVNDIFF1,
@@ -3572,7 +3811,8 @@ svn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params,
SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE
));
else
- SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "nn()(wwwwwwwwww)",
+ SVN_ERR(svn_ra_svn__write_cmd_response(conn, scratch_pool,
+ "nn()(wwwwwwwwww)",
(apr_uint64_t) 2, (apr_uint64_t) 2,
SVN_RA_SVN_CAP_EDIT_PIPELINE,
SVN_RA_SVN_CAP_ABSENT_ENTRIES,
@@ -3589,14 +3829,14 @@ svn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params,
/* Read client response, which we assume to be in version 2 format:
* version, capability list, and client URL; then we do an auth
* request. */
- SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "nlc?c(?c)",
+ SVN_ERR(svn_ra_svn__read_tuple(conn, scratch_pool, "nlc?c(?c)",
&ver, &caplist, &client_url,
&ra_client_string,
&client_string));
if (ver != 2)
return SVN_NO_ERROR;
- client_url = svn_uri_canonicalize(client_url, pool);
+ client_url = svn_uri_canonicalize(client_url, conn_pool);
SVN_ERR(svn_ra_svn_set_capabilities(conn, caplist));
/* All released versions of Subversion support edit-pipeline,
@@ -3617,14 +3857,15 @@ svn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params,
int i;
svn_ra_svn_item_t *item;
- cap_words = apr_array_make(pool, 1, sizeof(const char *));
+ b->repository->capabilities = apr_array_make(conn_pool, 1,
+ sizeof(const char *));
for (i = 0; i < caplist->nelts; i++)
{
item = &APR_ARRAY_IDX(caplist, i, svn_ra_svn_item_t);
/* ra_svn_set_capabilities() already type-checked for us */
if (strcmp(item->u.word, SVN_RA_SVN_CAP_MERGEINFO) == 0)
{
- APR_ARRAY_PUSH(cap_words, const char *)
+ APR_ARRAY_PUSH(b->repository->capabilities, const char *)
= SVN_RA_CAPABILITY_MERGEINFO;
}
/* Save for operational log. */
@@ -3634,46 +3875,40 @@ svn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params,
}
}
- 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, params->config_pool,
+ params->authz_pool, params->fs_config,
+ conn_pool, scratch_pool),
+ b);
+ if (!err)
+ {
+ if (b->repository->anon_access == NO_ACCESS
+ && (b->repository->auth_access == NO_ACCESS
+ || (!b->client_info->tunnel_user && !b->repository->pwdb
+ && !b->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)
+ SVN_ERR(auth_request(conn, scratch_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);
- io_err = svn_ra_svn__write_cmd_failure(conn, pool, err);
+ log_error(err, b);
+ io_err = svn_ra_svn__write_cmd_failure(conn, scratch_pool, err);
svn_error_clear(err);
SVN_ERR(io_err);
- return svn_ra_svn__flush(conn, pool);
+ return svn_ra_svn__flush(conn, scratch_pool);
}
- /* Log the open. */
- if (ra_client_string == NULL || ra_client_string[0] == '\0')
- ra_client_string = "-";
- else
- ra_client_string = svn_path_uri_encode(ra_client_string, pool);
- if (client_string == NULL || client_string[0] == '\0')
- client_string = "-";
- else
- client_string = svn_path_uri_encode(client_string, pool);
- 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),
- 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_ERR(svn_fs_get_uuid(b.fs, &uuid, pool));
+ SVN_ERR(svn_fs_get_uuid(b->repository->fs, &b->repository->uuid,
+ conn_pool));
/* We can't claim mergeinfo capability until we know whether the
repository supports mergeinfo (i.e., is not a 1.4 repository),
@@ -3683,28 +3918,161 @@ svn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params,
the client has sent the url. */
{
svn_boolean_t supports_mergeinfo;
- SVN_ERR(svn_repos_has_capability(b.repos, &supports_mergeinfo,
- SVN_REPOS_CAPABILITY_MERGEINFO, pool));
-
- SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(cc(!",
- "success", uuid, b.repos_url));
+ SVN_ERR(svn_repos_has_capability(b->repository->repos,
+ &supports_mergeinfo,
+ SVN_REPOS_CAPABILITY_MERGEINFO,
+ scratch_pool));
+
+ SVN_ERR(svn_ra_svn__write_tuple(conn, scratch_pool, "w(cc(!",
+ "success", b->repository->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, "!))"));
+ SVN_ERR(svn_ra_svn__write_word(conn, scratch_pool,
+ SVN_RA_SVN_CAP_MERGEINFO));
+ SVN_ERR(svn_ra_svn__write_tuple(conn, scratch_pool, "!))"));
+ SVN_ERR(svn_ra_svn__flush(conn, scratch_pool));
}
+ /* Log the open. */
+ if (ra_client_string == NULL || ra_client_string[0] == '\0')
+ ra_client_string = "-";
+ else
+ ra_client_string = svn_path_uri_encode(ra_client_string, scratch_pool);
+ if (client_string == NULL || client_string[0] == '\0')
+ client_string = "-";
+ else
+ client_string = svn_path_uri_encode(client_string, scratch_pool);
+ SVN_ERR(log_command(b, conn, scratch_pool,
+ "open %" APR_UINT64_T_FMT " cap=(%s) %s %s %s",
+ ver, cap_log->data,
+ svn_path_uri_encode(b->repository->fs_path->data,
+ scratch_pool),
+ ra_client_string, client_string));
+
+ warn_baton = apr_pcalloc(conn_pool, sizeof(*warn_baton));
+ warn_baton->server = b;
+ warn_baton->conn = conn;
+ svn_fs_set_warning_func(b->repository->fs, fs_warning_func, warn_baton);
+
/* Set up editor shims. */
{
svn_delta_shim_callbacks_t *callbacks =
- svn_delta_shim_callbacks_default(pool);
+ svn_delta_shim_callbacks_default(conn_pool);
callbacks->fetch_base_func = fetch_base_func;
callbacks->fetch_props_func = fetch_props_func;
callbacks->fetch_kind_func = fetch_kind_func;
- callbacks->fetch_baton = &b;
+ callbacks->fetch_baton = b;
SVN_ERR(svn_ra_svn__set_shim_callbacks(conn, callbacks));
}
- return svn_ra_svn__handle_commands2(conn, pool, main_commands, &b, FALSE);
+ *baton = b;
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+serve_interruptable(svn_boolean_t *terminate_p,
+ connection_t *connection,
+ svn_boolean_t (* is_busy)(connection_t *),
+ apr_pool_t *pool)
+{
+ svn_boolean_t terminate = FALSE;
+ svn_error_t *err = NULL;
+ const svn_ra_svn_cmd_entry_t *command;
+ apr_pool_t *iterpool = svn_pool_create(pool);
+
+ /* Prepare command parser. */
+ apr_hash_t *cmd_hash = apr_hash_make(pool);
+ for (command = main_commands; command->cmdname; command++)
+ svn_hash_sets(cmd_hash, command->cmdname, command);
+
+ /* Auto-initialize connection */
+ if (! connection->conn)
+ {
+ apr_status_t ar;
+
+ /* Enable TCP keep-alives on the socket so we time out when
+ * the connection breaks due to network-layer problems.
+ * If the peer has dropped the connection due to a network partition
+ * or a crash, or if the peer no longer considers the connection
+ * valid because we are behind a NAT and our public IP has changed,
+ * it will respond to the keep-alive probe with a RST instead of an
+ * acknowledgment segment, which will cause svn to abort the session
+ * even while it is currently blocked waiting for data from the peer. */
+ ar = apr_socket_opt_set(connection->usock, APR_SO_KEEPALIVE, 1);
+ if (ar)
+ {
+ /* It's not a fatal error if we cannot enable keep-alives. */
+ }
+
+ /* create the connection, configure ports etc. */
+ connection->conn
+ = svn_ra_svn_create_conn4(connection->usock, NULL, NULL,
+ connection->params->compression_level,
+ connection->params->zero_copy_limit,
+ connection->params->error_check_interval,
+ connection->pool);
+
+ /* Construct server baton and open the repository for the first time. */
+ err = construct_server_baton(&connection->baton, connection->conn,
+ connection->params, pool);
+ }
+
+ /* If we can't access the repo for some reason, end this connection. */
+ if (err)
+ terminate = TRUE;
+
+ /* Process incoming commands. */
+ while (!terminate && !err)
+ {
+ svn_pool_clear(iterpool);
+ if (is_busy && is_busy(connection))
+ {
+ svn_boolean_t has_command;
+
+ /* If the server is busy, execute just one command and only if
+ * there is one currently waiting in our receive buffers.
+ */
+ err = svn_ra_svn__has_command(&has_command, &terminate,
+ connection->conn, iterpool);
+ if (!err && has_command)
+ err = svn_ra_svn__handle_command(&terminate, cmd_hash,
+ connection->baton,
+ connection->conn,
+ FALSE, iterpool);
+
+ break;
+ }
+ else
+ {
+ /* The server is not busy, thus let's serve whichever command
+ * comes in next and whenever it comes in. This requires the
+ * busy() callback test to return TRUE while there are still some
+ * resources left.
+ */
+ err = svn_ra_svn__handle_command(&terminate, cmd_hash,
+ connection->baton,
+ connection->conn,
+ FALSE, iterpool);
+ }
+ }
+
+ /* error or normal end of session. Close the connection */
+ svn_pool_destroy(iterpool);
+ if (terminate_p)
+ *terminate_p = terminate;
+
+ return svn_error_trace(err);
+}
+
+svn_error_t *serve(svn_ra_svn_conn_t *conn,
+ serve_params_t *params,
+ apr_pool_t *pool)
+{
+ server_baton_t *baton = NULL;
+
+ SVN_ERR(construct_server_baton(&baton, conn, params, pool));
+ return svn_ra_svn__handle_commands2(conn, pool, main_commands, baton, FALSE);
}