summaryrefslogtreecommitdiff
path: root/subversion/svnserve
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-08-05 16:22:51 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-08-05 16:22:51 +0000
commitcf46733632c7279a9fd0fe6ce26f9185a4ae82a9 (patch)
treeda27775a2161723ef342e91af41a8b51fedef405 /subversion/svnserve
parentbb0ef45f7c46b0ae221b26265ef98a768c33f820 (diff)
downloadsubversion-tarball-cf46733632c7279a9fd0fe6ce26f9185a4ae82a9.tar.gz
Diffstat (limited to 'subversion/svnserve')
-rw-r--r--subversion/svnserve/cyrus_auth.c49
-rw-r--r--subversion/svnserve/logger.c161
-rw-r--r--subversion/svnserve/logger.h79
-rw-r--r--subversion/svnserve/serve.c1498
-rw-r--r--subversion/svnserve/server.h145
-rw-r--r--subversion/svnserve/svnserve.c711
6 files changed, 1753 insertions, 890 deletions
diff --git a/subversion/svnserve/cyrus_auth.c b/subversion/svnserve/cyrus_auth.c
index 2d75047..40f4228 100644
--- a/subversion/svnserve/cyrus_auth.c
+++ b/subversion/svnserve/cyrus_auth.c
@@ -74,6 +74,8 @@ static int canonicalize_username(sasl_conn_t *conn,
{
/* The only valid realm is user_realm (i.e. the repository's realm).
If the user gave us another realm, complain. */
+ if (realm_len != inlen-(pos-in+1))
+ return SASL_BADPROT;
if (strncmp(pos+1, user_realm, inlen-(pos-in+1)) != 0)
return SASL_BADPROT;
}
@@ -177,16 +179,22 @@ static svn_error_t *try_auth(svn_ra_svn_conn_t *conn,
SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "w(?s)", &mech, &in));
if (strcmp(mech, "EXTERNAL") == 0 && !in)
- in = svn_string_create(b->tunnel_user, pool);
+ in = svn_string_create(b->client_info->tunnel_user, pool);
else if (in)
in = svn_base64_decode_string(in, pool);
/* For CRAM-MD5, we don't base64-encode stuff. */
use_base64 = (strcmp(mech, "CRAM-MD5") != 0);
+ /* sasl uses unsigned int for the length of strings, we use apr_size_t
+ * which may not be the same size. Deal with potential integer overflow */
+ if (in && in->len > UINT_MAX)
+ return svn_error_createf(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
+ _("Initial token is too long"));
+
result = sasl_server_start(sasl_ctx, mech,
in ? in->data : NULL,
- in ? in->len : 0, &out, &outlen);
+ in ? (unsigned int) in->len : 0, &out, &outlen);
if (result != SASL_OK && result != SASL_CONTINUE)
return fail_auth(conn, pool, sasl_ctx);
@@ -210,7 +218,13 @@ static svn_error_t *try_auth(svn_ra_svn_conn_t *conn,
in = item->u.string;
if (use_base64)
in = svn_base64_decode_string(in, pool);
- result = sasl_server_step(sasl_ctx, in->data, in->len, &out, &outlen);
+
+ if (in->len > UINT_MAX)
+ return svn_error_createf(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
+ _("Step response is too long"));
+
+ result = sasl_server_step(sasl_ctx, in->data, (unsigned int) in->len,
+ &out, &outlen);
}
if (result != SASL_OK)
@@ -246,7 +260,7 @@ svn_error_t *cyrus_auth_request(svn_ra_svn_conn_t *conn,
apr_pool_t *subpool;
apr_status_t apr_err;
const char *localaddrport = NULL, *remoteaddrport = NULL;
- const char *mechlist, *val;
+ const char *mechlist;
char hostname[APRMAXHOSTLEN + 1];
sasl_security_properties_t secprops;
svn_boolean_t success, no_anonymous;
@@ -265,7 +279,7 @@ svn_error_t *cyrus_auth_request(svn_ra_svn_conn_t *conn,
/* Create a SASL context. SASL_SUCCESS_DATA tells SASL that the protocol
supports sending data along with the final "success" message. */
result = sasl_server_new(SVN_RA_SVN_SASL_NAME,
- hostname, b->realm,
+ hostname, b->repository->realm,
localaddrport, remoteaddrport,
NULL, SASL_SUCCESS_DATA,
&sasl_ctx);
@@ -285,21 +299,12 @@ svn_error_t *cyrus_auth_request(svn_ra_svn_conn_t *conn,
svn_ra_svn__default_secprops(&secprops);
/* Don't allow ANONYMOUS if a username is required. */
- no_anonymous = needs_username || get_access(b, UNAUTHENTICATED) < required;
+ no_anonymous = needs_username || b->repository->anon_access < required;
if (no_anonymous)
secprops.security_flags |= SASL_SEC_NOANONYMOUS;
- svn_config_get(b->cfg, &val,
- SVN_CONFIG_SECTION_SASL,
- SVN_CONFIG_OPTION_MIN_SSF,
- "0");
- SVN_ERR(svn_cstring_atoui(&secprops.min_ssf, val));
-
- svn_config_get(b->cfg, &val,
- SVN_CONFIG_SECTION_SASL,
- SVN_CONFIG_OPTION_MAX_SSF,
- "256");
- SVN_ERR(svn_cstring_atoui(&secprops.max_ssf, val));
+ secprops.min_ssf = b->repository->min_ssf;
+ secprops.max_ssf = b->repository->max_ssf;
/* Set security properties. */
result = sasl_setprop(sasl_ctx, SASL_SEC_PROPS, &secprops);
@@ -307,8 +312,9 @@ svn_error_t *cyrus_auth_request(svn_ra_svn_conn_t *conn,
return fail_cmd(conn, pool, sasl_ctx);
/* SASL needs to know if we are externally authenticated. */
- if (b->tunnel_user)
- result = sasl_setprop(sasl_ctx, SASL_AUTH_EXTERNAL, b->tunnel_user);
+ if (b->client_info->tunnel_user)
+ result = sasl_setprop(sasl_ctx, SASL_AUTH_EXTERNAL,
+ b->client_info->tunnel_user);
if (result != SASL_OK)
return fail_cmd(conn, pool, sasl_ctx);
@@ -330,7 +336,7 @@ svn_error_t *cyrus_auth_request(svn_ra_svn_conn_t *conn,
/* Send the list of mechanisms and the realm to the client. */
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "(w)c",
- mechlist, b->realm));
+ mechlist, b->repository->realm));
/* The main authentication loop. */
subpool = svn_pool_create(pool);
@@ -358,7 +364,8 @@ svn_error_t *cyrus_auth_request(svn_ra_svn_conn_t *conn,
if ((p = strchr(user, '@')) != NULL)
{
/* Drop the realm part. */
- b->user = apr_pstrndup(b->pool, user, p - (const char *)user);
+ b->client_info->user = apr_pstrndup(b->pool, user,
+ p - (const char *)user);
}
else
{
diff --git a/subversion/svnserve/logger.c b/subversion/svnserve/logger.c
new file mode 100644
index 0000000..19b6bd4
--- /dev/null
+++ b/subversion/svnserve/logger.c
@@ -0,0 +1,161 @@
+/*
+ * logger.c : Implementation of the SvnServe logger API
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+
+
+#define APR_WANT_STRFUNC
+#include <apr_want.h>
+
+#include "svn_error.h"
+#include "svn_io.h"
+#include "svn_pools.h"
+#include "svn_time.h"
+
+#include "private/svn_mutex.h"
+
+#include "svn_private_config.h"
+#include "logger.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* For getpid() */
+#endif
+
+struct logger_t
+{
+ /* actual log file / stream object */
+ svn_stream_t *stream;
+
+ /* mutex used to serialize access to this structure */
+ svn_mutex__t *mutex;
+
+ /* private pool used for temporary allocations */
+ apr_pool_t *pool;
+};
+
+svn_error_t *
+logger__create_for_stderr(logger_t **logger,
+ apr_pool_t *pool)
+{
+ logger_t *result = apr_pcalloc(pool, sizeof(*result));
+ result->pool = svn_pool_create(pool);
+
+ SVN_ERR(svn_stream_for_stderr(&result->stream, pool));
+ SVN_ERR(svn_mutex__init(&result->mutex, TRUE, pool));
+
+ *logger = result;
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+logger__create(logger_t **logger,
+ const char *filename,
+ apr_pool_t *pool)
+{
+ logger_t *result = apr_pcalloc(pool, sizeof(*result));
+ apr_file_t *file;
+
+ SVN_ERR(svn_io_file_open(&file, filename,
+ APR_WRITE | APR_CREATE | APR_APPEND,
+ APR_OS_DEFAULT, pool));
+ SVN_ERR(svn_mutex__init(&result->mutex, TRUE, pool));
+
+ result->stream = svn_stream_from_aprfile2(file, FALSE, pool);
+ result->pool = svn_pool_create(pool);
+
+ *logger = result;
+
+ return SVN_NO_ERROR;
+}
+
+void
+logger__log_error(logger_t *logger,
+ svn_error_t *err,
+ repository_t *repository,
+ client_info_t *client_info)
+{
+ if (logger && err)
+ {
+ const char *timestr, *continuation;
+ const char *user, *repos, *remote_host;
+ char errbuf[256];
+ /* 8192 from MAX_STRING_LEN in from httpd-2.2.4/include/httpd.h */
+ char errstr[8192];
+
+ svn_error_clear(svn_mutex__lock(logger->mutex));
+
+ timestr = svn_time_to_cstring(apr_time_now(), logger->pool);
+ remote_host = client_info && client_info->remote_host
+ ? client_info->remote_host
+ : "-";
+ user = client_info && client_info->user
+ ? client_info->user
+ : "-";
+ repos = repository && repository->repos_name
+ ? repository->repos_name
+ : "-";
+
+ continuation = "";
+ while (err)
+ {
+ 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);
+ }
+
+ memcpy(errstr + len, APR_EOL_STR, sizeof(APR_EOL_STR));
+ len += sizeof(APR_EOL_STR) -1; /* add NL, ex terminating NUL */
+
+ svn_error_clear(svn_stream_write(logger->stream, errstr, &len));
+
+ continuation = "-";
+ err = err->child;
+ }
+
+ svn_pool_clear(logger->pool);
+
+ svn_error_clear(svn_mutex__unlock(logger->mutex, SVN_NO_ERROR));
+ }
+}
+
+svn_error_t *
+logger__write(logger_t *logger,
+ const char *errstr,
+ apr_size_t len)
+{
+ SVN_MUTEX__WITH_LOCK(logger->mutex,
+ svn_stream_write(logger->stream, errstr, &len));
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svnserve/logger.h b/subversion/svnserve/logger.h
new file mode 100644
index 0000000..aac804b
--- /dev/null
+++ b/subversion/svnserve/logger.h
@@ -0,0 +1,79 @@
+/*
+ * logger.h : Public definitions for the Repository Cache
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+#ifndef LOGGER_H
+#define LOGGER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "server.h"
+
+
+
+/* Opaque svnserve log file writer data structure. Access to the log
+ * file will be serialized among threads within the same process.
+ */
+typedef struct logger_t logger_t;
+
+/* In POOL, create a writer object that will write log messages to stderr
+ * and return it in *LOGGER. The log file will not add any buffering
+ * on top of stderr.
+ */
+svn_error_t *
+logger__create_for_stderr(logger_t **logger,
+ apr_pool_t *pool);
+
+/* In POOL, create a writer object for log file FILENAME and return it
+ * in *LOGGER. The log file will be flushed & closed when POOL gets
+ * cleared or destroyed.
+ */
+svn_error_t *
+logger__create(logger_t **logger,
+ const char *filename,
+ apr_pool_t *pool);
+
+/* Write the first LEN bytes from ERRSTR to the log file managed by LOGGER.
+ */
+svn_error_t *
+logger__write(logger_t *logger,
+ const char *errstr,
+ apr_size_t len);
+
+/* Write a description of ERR with additional information from REPOSITORY
+ * and CLIENT_INFO to the log file managed by LOGGER. REPOSITORY as well
+ * as CLIENT_INFO may be NULL. If either ERR or LOGGER are NULL, this
+ * becomes a no-op.
+ */
+void
+logger__log_error(logger_t *logger,
+ svn_error_t *err,
+ repository_t *repository,
+ client_info_t *client_info);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* LOGGER_H */
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);
}
diff --git a/subversion/svnserve/server.h b/subversion/svnserve/server.h
index 926a96f..d366e0c 100644
--- a/subversion/svnserve/server.h
+++ b/subversion/svnserve/server.h
@@ -36,39 +36,61 @@ extern "C" {
#include "svn_repos.h"
#include "svn_ra_svn.h"
+#include "private/svn_atomic.h"
+#include "private/svn_mutex.h"
+#include "private/svn_repos_private.h"
+#include "private/svn_subr_private.h"
+
enum username_case_type { CASE_FORCE_UPPER, CASE_FORCE_LOWER, CASE_ASIS };
-typedef struct server_baton_t {
+enum authn_type { UNAUTHENTICATED, AUTHENTICATED };
+enum access_type { NO_ACCESS, READ_ACCESS, WRITE_ACCESS };
+
+typedef struct repository_t {
svn_repos_t *repos;
const char *repos_name; /* URI-encoded name of repository (not for authz) */
+ const char *repos_root; /* Repository root directory */
svn_fs_t *fs; /* For convenience; same as svn_repos_fs(repos) */
const char *base; /* Base directory for config files */
- svn_config_t *cfg; /* Parsed repository svnserve.conf */
svn_config_t *pwdb; /* Parsed password database */
svn_authz_t *authzdb; /* Parsed authz rules */
const char *authz_repos_name; /* The name of the repository for authz */
const char *realm; /* Authentication realm */
const char *repos_url; /* URL to base of repository */
+ const char *hooks_env; /* Path to the hooks environment file or NULL */
+ const char *uuid; /* Repository ID */
+ apr_array_header_t *capabilities;
+ /* Client capabilities (SVN_RA_CAPABILITY_*) */
svn_stringbuf_t *fs_path;/* Decoded base in-repos path (w/ leading slash) */
- apr_hash_t *fs_config; /* Additional FS configuration parameters */
- const char *user; /* Authenticated username of the user */
enum username_case_type username_case; /* Case-normalize the username? */
+ svn_boolean_t use_sasl; /* Use Cyrus SASL for authentication;
+ always false if SVN_HAVE_SASL not defined */
+ unsigned min_ssf; /* min-encryption SASL parameter */
+ unsigned max_ssf; /* max-encryption SASL parameter */
+
+ enum access_type auth_access; /* access granted to authenticated users */
+ enum access_type anon_access; /* access granted to annonymous users */
+
+} repository_t;
+
+typedef struct client_info_t {
+ const char *user; /* Authenticated username of the user */
+ const char *remote_host; /* IP of the client that contacted the server */
const char *authz_user; /* Username for authz ('user' + 'username_case') */
svn_boolean_t tunnel; /* Tunneled through login agent */
const char *tunnel_user; /* Allow EXTERNAL to authenticate as this */
+} client_info_t;
+
+typedef struct server_baton_t {
+ repository_t *repository; /* repository-specific data to use */
+ client_info_t *client_info; /* client-specific data to use */
+ struct logger_t *logger; /* Log file data structure.
+ May be NULL even if log_file is not. */
svn_boolean_t read_only; /* Disallow write access (global flag) */
- svn_boolean_t use_sasl; /* Use Cyrus SASL for authentication;
- always false if SVN_HAVE_SASL not defined */
- apr_file_t *log_file; /* Log filehandle. */
svn_boolean_t vhost; /* Use virtual-host-based path to repo. */
apr_pool_t *pool;
} server_baton_t;
-enum authn_type { UNAUTHENTICATED, AUTHENTICATED };
-enum access_type { NO_ACCESS, READ_ACCESS, WRITE_ACCESS };
-
-enum access_type get_access(server_baton_t *b, enum authn_type auth);
-
typedef struct serve_params_t {
/* The virtual root of the repositories to serve. The client URL
path is interpreted relative to this root and is not allowed to
@@ -97,20 +119,21 @@ typedef struct serve_params_t {
per-repository svnserve.conf are not read. */
svn_config_t *cfg;
- /* A filehandle open for writing logs to; possibly NULL. */
- apr_file_t *log_file;
+ /* logging data structure; possibly NULL. */
+ struct logger_t *logger;
- /* Username case normalization style. */
- enum username_case_type username_case;
+ /* all configurations should be opened through this factory */
+ svn_repos__config_pool_t *config_pool;
- /* Enable text delta caching for all FSFS repositories. */
- svn_boolean_t cache_txdeltas;
+ /* all authz data should be opened through this factory */
+ svn_repos__authz_pool_t *authz_pool;
- /* Enable full-text caching for all FSFS repositories. */
- svn_boolean_t cache_fulltexts;
+ /* The FS configuration to be applied to all repositories.
+ It mainly contains things like cache settings. */
+ apr_hash_t *fs_config;
- /* Enable revprop caching for all FSFS repositories. */
- svn_boolean_t cache_revprops;
+ /* Username case normalization style. */
+ enum username_case_type username_case;
/* Size of the in-memory cache (used by FSFS only). */
apr_uint64_t memory_cache_size;
@@ -133,28 +156,65 @@ typedef struct serve_params_t {
svn_boolean_t vhost;
} serve_params_t;
+/* This structure contains all data that describes a client / server
+ connection. Their lifetime is separated from the thread-local
+ serving pools. */
+typedef struct connection_t
+{
+ /* socket return by accept() */
+ apr_socket_t *usock;
+
+ /* server-global parameters */
+ serve_params_t *params;
+
+ /* connection-specific objects */
+ server_baton_t *baton;
+
+ /* buffered connection object used by the marshaller */
+ svn_ra_svn_conn_t *conn;
+
+ /* memory pool for objects with connection lifetime */
+ apr_pool_t *pool;
+
+ /* Number of threads using the pool.
+ The pool passed to apr_thread_create can only be released when both
+
+ A: the call to apr_thread_create has returned to the calling thread
+ B: the new thread has started running and reached apr_thread_start_t
+
+ So we set the atomic counter to 2 then both the calling thread and
+ the new thread decrease it and when it reaches 0 the pool can be
+ released. */
+ svn_atomic_t ref_count;
+
+} connection_t;
+
+/* Return a client_info_t structure allocated in POOL and initialize it
+ * with data from CONN. */
+client_info_t * get_client_info(svn_ra_svn_conn_t *conn,
+ serve_params_t *params,
+ apr_pool_t *pool);
+
/* Serve the connection CONN according to the parameters PARAMS. */
svn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params,
apr_pool_t *pool);
-/* Load the password database for the listening server based on the
- entries in the SERVER struct.
+/* Serve the connection CONNECTION for as long as IS_BUSY does not
+ return TRUE. If IS_BUSY is NULL, serve the connection until it
+ either gets terminated or there is an error. If TERMINATE_P is
+ not NULL, set *TERMINATE_P to TRUE if the connection got
+ terminated.
- SERVER and CONN must not be NULL. The real errors will be logged with
- SERVER and CONN but return generic errors to the client. */
-svn_error_t *load_pwdb_config(server_baton_t *server,
- svn_ra_svn_conn_t *conn,
- apr_pool_t *pool);
-
-/* Load the authz database for the listening server based on the
- entries in the SERVER struct.
-
- SERVER and CONN must not be NULL. The real errors will be logged with
- SERVER and CONN but return generic errors to the client. */
-svn_error_t *load_authz_config(server_baton_t *server,
- svn_ra_svn_conn_t *conn,
- const char *repos_root,
- apr_pool_t *pool);
+ For the first call, CONNECTION->CONN may be NULL in which case we
+ will create an ra_svn connection object. Subsequent calls will
+ check for an open repository and automatically re-open the repo
+ in pool if necessary.
+ */
+svn_error_t *
+serve_interruptable(svn_boolean_t *terminate_p,
+ connection_t *connection,
+ svn_boolean_t (* is_busy)(connection_t *),
+ apr_pool_t *pool);
/* Initialize the Cyrus SASL library. POOL is used for allocations. */
svn_error_t *cyrus_init(apr_pool_t *pool);
@@ -172,13 +232,6 @@ svn_error_t *cyrus_auth_request(svn_ra_svn_conn_t *conn,
apr_size_t escape_errorlog_item(char *dest, const char *source,
apr_size_t buflen);
-/* Log ERR to LOG_FILE if LOG_FILE is not NULL. Include REMOTE_HOST,
- USER, and REPOS in the log if they are not NULL. Allocate temporary
- char buffers in POOL (which caller can then clear or dispose of). */
-void
-log_error(svn_error_t *err, apr_file_t *log_file, const char *remote_host,
- const char *user, const char *repos, apr_pool_t *pool);
-
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/subversion/svnserve/svnserve.c b/subversion/svnserve/svnserve.c
index 3d83323..f3a483d 100644
--- a/subversion/svnserve/svnserve.c
+++ b/subversion/svnserve/svnserve.c
@@ -48,14 +48,20 @@
#include "svn_cache_config.h"
#include "svn_version.h"
#include "svn_io.h"
+#include "svn_hash.h"
#include "svn_private_config.h"
#include "private/svn_dep_compat.h"
#include "private/svn_cmdline_private.h"
#include "private/svn_atomic.h"
+#include "private/svn_mutex.h"
#include "private/svn_subr_private.h"
+#if APR_HAS_THREADS
+# include <apr_thread_pool.h>
+#endif
+
#include "winservice.h"
#ifdef HAVE_UNISTD_H
@@ -63,6 +69,7 @@
#endif
#include "server.h"
+#include "logger.h"
/* The strategy for handling incoming connections. Some of these may be
unavailable due to platform limitations. */
@@ -103,6 +110,50 @@ enum run_mode {
#endif
+/* Parameters for the worker thread pool used in threaded mode. */
+
+/* Have at least this many worker threads (even if there are no requests
+ * to handle).
+ *
+ * A 0 value is legal but increases the latency for the next incoming
+ * request. Higher values may be useful for servers that experience short
+ * bursts of concurrent requests followed by longer idle periods.
+ */
+#define THREADPOOL_MIN_SIZE 1
+
+/* Maximum number of worker threads. If there are more concurrent requests
+ * than worker threads, the extra requests get queued.
+ *
+ * Since very slow connections will hog a full thread for a potentially
+ * long time before timing out, be sure to not set this limit too low.
+ *
+ * On the other hand, keep in mind that every thread will allocate up to
+ * 4MB of unused RAM in the APR allocator of its root pool. 32 bit servers
+ * must hence do with fewer threads.
+ */
+#if (APR_SIZEOF_VOIDP <= 4)
+#define THREADPOOL_MAX_SIZE 64
+#else
+#define THREADPOOL_MAX_SIZE 256
+#endif
+
+/* Number of microseconds that an unused thread remains in the pool before
+ * being terminated.
+ *
+ * Higher values are useful if clients frequently send small requests and
+ * you want to minimize the latency for those.
+ */
+#define THREADPOOL_THREAD_IDLE_LIMIT 1000000
+
+/* Number of client to server connections that may concurrently in the
+ * TCP 3-way handshake state, i.e. are in the process of being created.
+ *
+ * Larger values improve scalability with lots of small requests coming
+ * on over long latency networks.
+ *
+ * The OS may actually use a lower limit than specified here.
+ */
+#define ACCEPT_BACKLOG 128
#ifdef WIN32
static apr_os_sock_t winservice_svnserve_accept_socket = INVALID_SOCKET;
@@ -156,6 +207,9 @@ void winservice_notify_stop(void)
#define SVNSERVE_OPT_SINGLE_CONN 268
#define SVNSERVE_OPT_CLIENT_SPEED 269
#define SVNSERVE_OPT_VIRTUAL_HOST 270
+#define SVNSERVE_OPT_MIN_THREADS 271
+#define SVNSERVE_OPT_MAX_THREADS 272
+#define SVNSERVE_OPT_BLOCK_READ 273
static const apr_getopt_option_t svnserve__options[] =
{
@@ -217,21 +271,23 @@ static const apr_getopt_option_t svnserve__options[] =
" "
"Default is 16.\n"
" "
- "[used for FSFS repositories only]")},
+ "0 switches to dynamically sized caches.\n"
+ " "
+ "[used for FSFS and FSX repositories only]")},
{"cache-txdeltas", SVNSERVE_OPT_CACHE_TXDELTAS, 1,
N_("enable or disable caching of deltas between older\n"
" "
"revisions.\n"
" "
- "Default is no.\n"
+ "Default is yes.\n"
" "
- "[used for FSFS repositories only]")},
+ "[used for FSFS and FSX repositories only]")},
{"cache-fulltexts", SVNSERVE_OPT_CACHE_FULLTEXTS, 1,
N_("enable or disable caching of file contents\n"
" "
"Default is yes.\n"
" "
- "[used for FSFS repositories only]")},
+ "[used for FSFS and FSX repositories only]")},
{"cache-revprops", SVNSERVE_OPT_CACHE_REVPROPS, 1,
N_("enable or disable caching of revision properties.\n"
" "
@@ -239,7 +295,7 @@ static const apr_getopt_option_t svnserve__options[] =
" "
"Default is no.\n"
" "
- "[used for FSFS repositories only]")},
+ "[used for FSFS and FSX repositories only]")},
{"client-speed", SVNSERVE_OPT_CLIENT_SPEED, 1,
N_("Optimize network handling based on the assumption\n"
" "
@@ -248,20 +304,55 @@ static const apr_getopt_option_t svnserve__options[] =
"ARG Mbit/s.\n"
" "
"Default is 0 (optimizations disabled).")},
+ {"block-read", SVNSERVE_OPT_BLOCK_READ, 1,
+ N_("Parse and cache all data found in block instead\n"
+ " "
+ "of just the requested item.\n"
+ " "
+ "Default is no.\n"
+ " "
+ "[used for FSFS repositories in 1.9 format only]")},
#ifdef CONNECTION_HAVE_THREAD_OPTION
/* ### Making the assumption here that WIN32 never has fork and so
* ### this option never exists when --service exists. */
{"threads", 'T', 0, N_("use threads instead of fork "
"[mode: daemon]")},
+ {"min-threads", SVNSERVE_OPT_MIN_THREADS, 1,
+ N_("Minimum number of server threads, even if idle.\n"
+ " "
+ "Capped to max-threads; minimum value is 0.\n"
+ " "
+ "Default is 1.\n"
+ " "
+ "[used only with --threads]")},
+#if (APR_SIZEOF_VOIDP <= 4)
+ {"max-threads", SVNSERVE_OPT_MAX_THREADS, 1,
+ N_("Maximum number of server threads, even if there\n"
+ " "
+ "are more connections. Minimum value is 1.\n"
+ " "
+ "Default is 64.\n"
+ " "
+ "[used only with --threads]")},
+#else
+ {"max-threads", SVNSERVE_OPT_MAX_THREADS, 1,
+ N_("Maximum number of server threads, even if there\n"
+ " "
+ "are more connections. Minimum value is 1.\n"
+ " "
+ "Default is 256.\n"
+ " "
+ "[used only with --threads]")},
+#endif
#endif
{"foreground", SVNSERVE_OPT_FOREGROUND, 0,
N_("run in foreground (useful for debugging)\n"
" "
"[mode: daemon]")},
{"single-thread", SVNSERVE_OPT_SINGLE_CONN, 0,
- N_("handle one connection at a time in the parent process\n"
+ N_("handle one connection at a time in the parent\n"
" "
- "(useful for debugging)")},
+ "process (useful for debugging)")},
{"log-file", SVNSERVE_OPT_LOG_FILE, 1,
N_("svnserve log file")},
{"pid-file", SVNSERVE_OPT_PID_FILE, 1,
@@ -290,7 +381,6 @@ static const apr_getopt_option_t svnserve__options[] =
{0, 0, 0, 0}
};
-
static void usage(const char *progname, apr_pool_t *pool)
{
if (!progname)
@@ -299,7 +389,6 @@ static void usage(const char *progname, apr_pool_t *pool)
svn_error_clear(svn_cmdline_fprintf(stderr, pool,
_("Type '%s --help' for usage.\n"),
progname));
- exit(1);
}
static void help(apr_pool_t *pool)
@@ -309,15 +398,21 @@ static void help(apr_pool_t *pool)
#ifdef WIN32
svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X "
"| --service] [options]\n"
+ "Subversion repository server.\n"
+ "Type 'svnserve --version' to see the "
+ "program version.\n"
"\n"
"Valid options:\n"),
- stdout, pool));
+ stdout, pool));
#else
svn_error_clear(svn_cmdline_fputs(_("usage: svnserve [-d | -i | -t | -X] "
"[options]\n"
+ "Subversion repository server.\n"
+ "Type 'svnserve --version' to see the "
+ "program version.\n"
"\n"
"Valid options:\n"),
- stdout, pool));
+ stdout, pool));
#endif
for (i = 0; svnserve__options[i].name && svnserve__options[i].optch; i++)
{
@@ -326,7 +421,6 @@ static void help(apr_pool_t *pool)
svn_error_clear(svn_cmdline_fprintf(stdout, pool, " %s\n", optstr));
}
svn_error_clear(svn_cmdline_fprintf(stdout, pool, "\n"));
- exit(0);
}
static svn_error_t * version(svn_boolean_t quiet, apr_pool_t *pool)
@@ -378,56 +472,149 @@ static apr_status_t redirect_stdout(void *arg)
return apr_file_dup2(out_file, err_file, pool);
}
-#if APR_HAS_THREADS
-/* The pool passed to apr_thread_create can only be released when both
+/* Wait for the next client connection to come in from SOCK. Allocate
+ * the connection in a root pool from CONNECTION_POOLS and assign PARAMS.
+ * Return the connection object in *CONNECTION.
+ *
+ * Use HANDLING_MODE for proper internal cleanup.
+ */
+static svn_error_t *
+accept_connection(connection_t **connection,
+ apr_socket_t *sock,
+ serve_params_t *params,
+ enum connection_handling_mode handling_mode,
+ apr_pool_t *pool)
+{
+ apr_status_t status;
- A: the call to apr_thread_create has returned to the calling thread
- B: the new thread has started running and reached apr_thread_start_t
+ /* Non-standard pool handling. The main thread never blocks to join
+ * the connection threads so it cannot clean up after each one. So
+ * separate pools that can be cleared at thread exit are used. */
- So we set the atomic counter to 2 then both the calling thread and
- the new thread decrease it and when it reaches 0 the pool can be
- released. */
-struct shared_pool_t {
- svn_atomic_t count;
- apr_pool_t *pool;
-};
+ apr_pool_t *connection_pool = svn_pool_create(pool);
+ *connection = apr_pcalloc(connection_pool, sizeof(**connection));
+ (*connection)->pool = connection_pool;
+ (*connection)->params = params;
+ (*connection)->ref_count = 1;
-static struct shared_pool_t *
-attach_shared_pool(apr_pool_t *pool)
-{
- struct shared_pool_t *shared = apr_palloc(pool, sizeof(struct shared_pool_t));
+ do
+ {
+ #ifdef WIN32
+ if (winservice_is_stopping())
+ exit(0);
+ #endif
- shared->pool = pool;
- svn_atomic_set(&shared->count, 2);
+ status = apr_socket_accept(&(*connection)->usock, sock,
+ connection_pool);
+ if (handling_mode == connection_mode_fork)
+ {
+ apr_proc_t proc;
+
+ /* Collect any zombie child processes. */
+ while (apr_proc_wait_all_procs(&proc, NULL, NULL, APR_NOWAIT,
+ connection_pool) == APR_CHILD_DONE)
+ ;
+ }
+ }
+ while (APR_STATUS_IS_EINTR(status)
+ || APR_STATUS_IS_ECONNABORTED(status)
+ || APR_STATUS_IS_ECONNRESET(status));
- return shared;
+ return status
+ ? svn_error_wrap_apr(status, _("Can't accept client connection"))
+ : SVN_NO_ERROR;
}
+/* Add a reference to CONNECTION, i.e. keep it and it's pool valid unless
+ * that reference gets released using release_shared_pool().
+ */
static void
-release_shared_pool(struct shared_pool_t *shared)
+attach_connection(connection_t *connection)
{
- if (svn_atomic_dec(&shared->count) == 0)
- svn_pool_destroy(shared->pool);
+ svn_atomic_inc(&connection->ref_count);
}
-#endif
-/* "Arguments" passed from the main thread to the connection thread */
-struct serve_thread_t {
- svn_ra_svn_conn_t *conn;
- serve_params_t *params;
- struct shared_pool_t *shared_pool;
-};
+/* Release a reference to CONNECTION. If there are no more references,
+ * the connection will be
+ */
+static void
+close_connection(connection_t *connection)
+{
+ /* this will automatically close USOCK */
+ if (svn_atomic_dec(&connection->ref_count) == 0)
+ svn_pool_destroy(connection->pool);
+}
+
+/* Wrapper around serve() that takes a socket instead of a connection.
+ * This is to off-load work from the main thread in threaded and fork modes.
+ *
+ * If an error occurs, log it and also return it.
+ */
+static svn_error_t *
+serve_socket(connection_t *connection,
+ apr_pool_t *pool)
+{
+ /* process the actual request and log errors */
+ svn_error_t *err = serve_interruptable(NULL, connection, NULL, pool);
+ if (err)
+ logger__log_error(connection->params->logger, err, NULL,
+ get_client_info(connection->conn, connection->params,
+ pool));
+
+ return svn_error_trace(err);
+}
#if APR_HAS_THREADS
+
+/* allocate and recycle root pools for connection objects.
+ There should be at most THREADPOOL_MAX_SIZE such pools. */
+static svn_root_pools__t *connection_pools;
+
+/* The global thread pool serving all connections. */
+static apr_thread_pool_t *threads;
+
+/* Very simple load determination callback for serve_interruptable:
+ With less than half the threads in THREADS in use, we can afford to
+ wait in the socket read() function. Otherwise, poll them round-robin. */
+static svn_boolean_t
+is_busy(connection_t *connection)
+{
+ return apr_thread_pool_threads_count(threads) * 2
+ > apr_thread_pool_thread_max_get(threads);
+}
+
+/* Serve the connection given by DATA. Under high load, serve only
+ the current command (if any) and then put the connection back into
+ THREAD's task pool. */
static void * APR_THREAD_FUNC serve_thread(apr_thread_t *tid, void *data)
{
- struct serve_thread_t *d = data;
+ svn_boolean_t done;
+ connection_t *connection = data;
+ svn_error_t *err;
+
+ apr_pool_t *pool = svn_root_pools__acquire_pool(connection_pools);
+
+ /* process the actual request and log errors */
+ err = serve_interruptable(&done, connection, is_busy, pool);
+ if (err)
+ {
+ logger__log_error(connection->params->logger, err, NULL,
+ get_client_info(connection->conn, connection->params,
+ pool));
+ svn_error_clear(err);
+ done = TRUE;
+ }
+ svn_root_pools__release_pool(pool, connection_pools);
- svn_error_clear(serve(d->conn, d->params, d->shared_pool->pool));
- release_shared_pool(d->shared_pool);
+ /* Close or re-schedule connection. */
+ if (done)
+ close_connection(connection);
+ else
+ apr_thread_pool_push(threads, serve_thread, connection, 0, NULL);
return NULL;
}
+
#endif
/* Write the PID of the current process as a decimal number, followed by a
@@ -469,31 +656,33 @@ check_lib_versions(void)
}
-int main(int argc, const char *argv[])
+/*
+ * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error,
+ * either return an error to be displayed, or set *EXIT_CODE to non-zero and
+ * return SVN_NO_ERROR.
+ */
+static svn_error_t *
+sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
{
enum run_mode run_mode = run_mode_unspecified;
svn_boolean_t foreground = FALSE;
- apr_socket_t *sock, *usock;
- apr_file_t *in_file, *out_file;
+ apr_socket_t *sock;
apr_sockaddr_t *sa;
- apr_pool_t *pool;
- apr_pool_t *connection_pool;
svn_error_t *err;
apr_getopt_t *os;
int opt;
serve_params_t params;
const char *arg;
apr_status_t status;
- svn_ra_svn_conn_t *conn;
+#ifndef WIN32
apr_proc_t proc;
-#if APR_HAS_THREADS
- apr_threadattr_t *tattr;
- apr_thread_t *tid;
- struct shared_pool_t *shared_pool;
-
- struct serve_thread_t *thread_data;
#endif
+ svn_boolean_t is_multi_threaded;
enum connection_handling_mode handling_mode = CONNECTION_DEFAULT;
+ svn_boolean_t cache_fulltexts = TRUE;
+ svn_boolean_t cache_txdeltas = TRUE;
+ svn_boolean_t cache_revprops = FALSE;
+ svn_boolean_t use_block_read = FALSE;
apr_uint16_t port = SVN_RA_SVN_PORT;
const char *host = NULL;
int family = APR_INET;
@@ -509,31 +698,19 @@ int main(int argc, const char *argv[])
const char *pid_filename = NULL;
const char *log_filename = NULL;
svn_node_kind_t kind;
-
- /* Initialize the app. */
- if (svn_cmdline_init("svnserve", stderr) != EXIT_SUCCESS)
- return EXIT_FAILURE;
-
- /* Create our top-level pool. */
- pool = svn_pool_create(NULL);
-
+ apr_size_t min_thread_count = THREADPOOL_MIN_SIZE;
+ apr_size_t max_thread_count = THREADPOOL_MAX_SIZE;
#ifdef SVN_HAVE_SASL
- SVN_INT_ERR(cyrus_init(pool));
+ SVN_ERR(cyrus_init(pool));
#endif
/* Check library versions */
- err = check_lib_versions();
- if (err)
- return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
+ SVN_ERR(check_lib_versions());
/* Initialize the FS library. */
- err = svn_fs_initialize(pool);
- if (err)
- return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
+ SVN_ERR(svn_fs_initialize(pool));
- err = svn_cmdline__getopt_init(&os, argc, argv, pool);
- if (err)
- return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
+ SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
params.root = "/";
params.tunnel = FALSE;
@@ -542,13 +719,13 @@ int main(int argc, const char *argv[])
params.base = NULL;
params.cfg = NULL;
params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT;
- params.log_file = NULL;
+ params.logger = NULL;
+ params.config_pool = NULL;
+ params.authz_pool = NULL;
+ params.fs_config = NULL;
params.vhost = FALSE;
params.username_case = CASE_ASIS;
params.memory_cache_size = (apr_uint64_t)-1;
- params.cache_fulltexts = TRUE;
- params.cache_txdeltas = FALSE;
- params.cache_revprops = FALSE;
params.zero_copy_limit = 0;
params.error_check_interval = 4096;
@@ -558,7 +735,11 @@ int main(int argc, const char *argv[])
if (APR_STATUS_IS_EOF(status))
break;
if (status != APR_SUCCESS)
- usage(argv[0], pool);
+ {
+ usage(argv[0], pool);
+ *exit_code = EXIT_FAILURE;
+ return SVN_NO_ERROR;
+ }
switch (opt)
{
case '6':
@@ -570,7 +751,7 @@ int main(int argc, const char *argv[])
case 'h':
help(pool);
- break;
+ return SVN_NO_ERROR;
case 'q':
quiet = TRUE;
@@ -611,10 +792,8 @@ int main(int argc, const char *argv[])
err = svn_cstring_strtoui64(&val, arg, 0, APR_UINT16_MAX, 10);
if (err)
- return svn_cmdline_handle_exit_error(
- svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err,
- _("Invalid port '%s'"), arg),
- pool, "svnserve: ");
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err,
+ _("Invalid port '%s'"), arg);
port = (apr_uint16_t)val;
}
break;
@@ -644,23 +823,18 @@ int main(int argc, const char *argv[])
break;
case 'r':
- SVN_INT_ERR(svn_utf_cstring_to_utf8(&params.root, arg, pool));
+ SVN_ERR(svn_utf_cstring_to_utf8(&params.root, arg, pool));
- err = svn_io_check_resolved_path(params.root, &kind, pool);
- if (err)
- return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
+ SVN_ERR(svn_io_check_resolved_path(params.root, &kind, pool));
if (kind != svn_node_dir)
{
- svn_error_clear
- (svn_cmdline_fprintf
- (stderr, pool,
- _("svnserve: Root path '%s' does not exist "
- "or is not a directory.\n"), params.root));
- return EXIT_FAILURE;
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Root path '%s' does not exist "
+ "or is not a directory"), params.root);
}
params.root = svn_dirent_internal_style(params.root, pool);
- SVN_INT_ERR(svn_dirent_get_absolute(&params.root, params.root, pool));
+ SVN_ERR(svn_dirent_get_absolute(&params.root, params.root, pool));
break;
case 'R':
@@ -685,18 +859,19 @@ int main(int argc, const char *argv[])
break;
case SVNSERVE_OPT_CACHE_TXDELTAS:
- params.cache_txdeltas
- = svn_tristate__from_word(arg) == svn_tristate_true;
+ cache_txdeltas = svn_tristate__from_word(arg) == svn_tristate_true;
break;
case SVNSERVE_OPT_CACHE_FULLTEXTS:
- params.cache_fulltexts
- = svn_tristate__from_word(arg) == svn_tristate_true;
+ cache_fulltexts = svn_tristate__from_word(arg) == svn_tristate_true;
break;
case SVNSERVE_OPT_CACHE_REVPROPS:
- params.cache_revprops
- = svn_tristate__from_word(arg) == svn_tristate_true;
+ cache_revprops = svn_tristate__from_word(arg) == svn_tristate_true;
+ break;
+
+ case SVNSERVE_OPT_BLOCK_READ:
+ use_block_read = svn_tristate__from_word(arg) == svn_tristate_true;
break;
case SVNSERVE_OPT_CLIENT_SPEED:
@@ -716,6 +891,14 @@ int main(int argc, const char *argv[])
}
break;
+ case SVNSERVE_OPT_MIN_THREADS:
+ min_thread_count = (apr_size_t)apr_strtoi64(arg, NULL, 0);
+ break;
+
+ case SVNSERVE_OPT_MAX_THREADS:
+ max_thread_count = (apr_size_t)apr_strtoi64(arg, NULL, 0);
+ break;
+
#ifdef WIN32
case SVNSERVE_OPT_SERVICE:
if (run_mode != run_mode_service)
@@ -727,17 +910,16 @@ int main(int argc, const char *argv[])
#endif
case SVNSERVE_OPT_CONFIG_FILE:
- SVN_INT_ERR(svn_utf_cstring_to_utf8(&config_filename, arg, pool));
+ SVN_ERR(svn_utf_cstring_to_utf8(&config_filename, arg, pool));
config_filename = svn_dirent_internal_style(config_filename, pool);
- SVN_INT_ERR(svn_dirent_get_absolute(&config_filename, config_filename,
- pool));
+ SVN_ERR(svn_dirent_get_absolute(&config_filename, config_filename,
+ pool));
break;
case SVNSERVE_OPT_PID_FILE:
- SVN_INT_ERR(svn_utf_cstring_to_utf8(&pid_filename, arg, pool));
+ SVN_ERR(svn_utf_cstring_to_utf8(&pid_filename, arg, pool));
pid_filename = svn_dirent_internal_style(pid_filename, pool);
- SVN_INT_ERR(svn_dirent_get_absolute(&pid_filename, pid_filename,
- pool));
+ SVN_ERR(svn_dirent_get_absolute(&pid_filename, pid_filename, pool));
break;
case SVNSERVE_OPT_VIRTUAL_HOST:
@@ -745,10 +927,9 @@ int main(int argc, const char *argv[])
break;
case SVNSERVE_OPT_LOG_FILE:
- SVN_INT_ERR(svn_utf_cstring_to_utf8(&log_filename, arg, pool));
+ SVN_ERR(svn_utf_cstring_to_utf8(&log_filename, arg, pool));
log_filename = svn_dirent_internal_style(log_filename, pool);
- SVN_INT_ERR(svn_dirent_get_absolute(&log_filename, log_filename,
- pool));
+ SVN_ERR(svn_dirent_get_absolute(&log_filename, log_filename, pool));
break;
}
@@ -756,12 +937,16 @@ int main(int argc, const char *argv[])
if (is_version)
{
- SVN_INT_ERR(version(quiet, pool));
- exit(0);
+ SVN_ERR(version(quiet, pool));
+ return SVN_NO_ERROR;
}
if (os->ind != argc)
- usage(argv[0], pool);
+ {
+ usage(argv[0], pool);
+ *exit_code = EXIT_FAILURE;
+ return SVN_NO_ERROR;
+ }
if (mode_opt_count != 1)
{
@@ -774,6 +959,8 @@ int main(int argc, const char *argv[])
#endif
stderr, pool));
usage(argv[0], pool);
+ *exit_code = EXIT_FAILURE;
+ return SVN_NO_ERROR;
}
if (handling_opt_count > 1)
@@ -782,65 +969,83 @@ int main(int argc, const char *argv[])
_("You may only specify one of -T or --single-thread\n"),
stderr, pool));
usage(argv[0], pool);
+ *exit_code = EXIT_FAILURE;
+ return SVN_NO_ERROR;
}
+ /* construct object pools */
+ is_multi_threaded = handling_mode == connection_mode_thread;
+ params.fs_config = apr_hash_make(pool);
+ svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS,
+ cache_txdeltas ? "1" :"0");
+ svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS,
+ cache_fulltexts ? "1" :"0");
+ svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
+ cache_revprops ? "2" :"0");
+ svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_BLOCK_READ,
+ use_block_read ? "1" :"0");
+
+ SVN_ERR(svn_repos__config_pool_create(&params.config_pool,
+ is_multi_threaded,
+ pool));
+ SVN_ERR(svn_repos__authz_pool_create(&params.authz_pool,
+ params.config_pool,
+ is_multi_threaded,
+ pool));
+
/* If a configuration file is specified, load it and any referenced
* password and authorization files. */
if (config_filename)
{
params.base = svn_dirent_dirname(config_filename, pool);
- SVN_INT_ERR(svn_config_read3(&params.cfg, config_filename,
- TRUE, /* must_exist */
- FALSE, /* section_names_case_sensitive */
- FALSE, /* option_names_case_sensitive */
- pool));
+ SVN_ERR(svn_repos__config_pool_get(&params.cfg, NULL,
+ params.config_pool,
+ config_filename,
+ TRUE, /* must_exist */
+ FALSE, /* names_case_sensitive */
+ NULL,
+ pool));
}
if (log_filename)
- SVN_INT_ERR(svn_io_file_open(&params.log_file, log_filename,
- APR_WRITE | APR_CREATE | APR_APPEND,
- APR_OS_DEFAULT, pool));
+ SVN_ERR(logger__create(&params.logger, log_filename, pool));
+ else if (run_mode == run_mode_listen_once)
+ SVN_ERR(logger__create_for_stderr(&params.logger, pool));
if (params.tunnel_user && run_mode != run_mode_tunnel)
{
- svn_error_clear
- (svn_cmdline_fprintf
- (stderr, pool,
- _("Option --tunnel-user is only valid in tunnel mode.\n")));
- exit(1);
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("Option --tunnel-user is only valid in tunnel mode"));
}
if (run_mode == run_mode_inetd || run_mode == run_mode_tunnel)
{
+ apr_pool_t *connection_pool;
+ svn_ra_svn_conn_t *conn;
+ svn_stream_t *stdin_stream;
+ svn_stream_t *stdout_stream;
+
params.tunnel = (run_mode == run_mode_tunnel);
apr_pool_cleanup_register(pool, pool, apr_pool_cleanup_null,
redirect_stdout);
- status = apr_file_open_stdin(&in_file, pool);
- if (status)
- {
- err = svn_error_wrap_apr(status, _("Can't open stdin"));
- return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
- }
- status = apr_file_open_stdout(&out_file, pool);
- if (status)
- {
- err = svn_error_wrap_apr(status, _("Can't open stdout"));
- return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
- }
+ SVN_ERR(svn_stream_for_stdin(&stdin_stream, pool));
+ SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool));
/* Use a subpool for the connection to ensure that if SASL is used
* the pool cleanup handlers that call sasl_dispose() (connection_pool)
* and sasl_done() (pool) are run in the right order. See issue #3664. */
connection_pool = svn_pool_create(pool);
- conn = svn_ra_svn_create_conn3(NULL, in_file, out_file,
+ conn = svn_ra_svn_create_conn4(NULL, stdin_stream, stdout_stream,
params.compression_level,
params.zero_copy_limit,
params.error_check_interval,
connection_pool);
- svn_error_clear(serve(conn, &params, connection_pool));
- exit(0);
+ err = serve(conn, &params, connection_pool);
+ svn_pool_destroy(connection_pool);
+
+ return err;
}
#ifdef WIN32
@@ -885,7 +1090,8 @@ int main(int argc, const char *argv[])
}
svn_error_clear(err);
- exit(1);
+ *exit_code = EXIT_FAILURE;
+ return SVN_NO_ERROR;
}
/* The service is now in the "starting" state. Before the SCM will
@@ -930,8 +1136,7 @@ int main(int argc, const char *argv[])
sockaddr_info_flags, pool);
if (status)
{
- err = svn_error_wrap_apr(status, _("Can't get address info"));
- return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
+ return svn_error_wrap_apr(status, _("Can't get address info"));
}
@@ -944,25 +1149,32 @@ int main(int argc, const char *argv[])
#endif
if (status)
{
- err = svn_error_wrap_apr(status, _("Can't create server socket"));
- return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
+ return svn_error_wrap_apr(status, _("Can't create server socket"));
}
/* Prevents "socket in use" errors when server is killed and quickly
* restarted. */
- apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1);
+ status = apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1);
+ if (status)
+ {
+ return svn_error_wrap_apr(status, _("Can't set options on server socket"));
+ }
status = apr_socket_bind(sock, sa);
if (status)
{
- err = svn_error_wrap_apr(status, _("Can't bind server socket"));
- return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
+ return svn_error_wrap_apr(status, _("Can't bind server socket"));
}
- apr_socket_listen(sock, 7);
+ status = apr_socket_listen(sock, ACCEPT_BACKLOG);
+ if (status)
+ {
+ return svn_error_wrap_apr(status, _("Can't listen on server socket"));
+ }
#if APR_HAS_FORK
if (run_mode != run_mode_listen_once && !foreground)
+ /* ### ignoring errors... */
apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
apr_signal(SIGCHLD, sigchld_handler);
@@ -981,7 +1193,7 @@ int main(int argc, const char *argv[])
#endif
if (pid_filename)
- SVN_INT_ERR(write_pid_file(pid_filename, pool));
+ SVN_ERR(write_pid_file(pid_filename, pool));
#ifdef WIN32
status = apr_os_sock_get(&winservice_svnserve_accept_socket, sock);
@@ -1021,107 +1233,72 @@ int main(int argc, const char *argv[])
svn_cache_config_set(&settings);
}
- while (1)
- {
-#ifdef WIN32
- if (winservice_is_stopping())
- return ERROR_SUCCESS;
-#endif
-
- /* Non-standard pool handling. The main thread never blocks to join
- the connection threads so it cannot clean up after each one. So
- separate pools that can be cleared at thread exit are used. */
-
- connection_pool
- = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
+#if APR_HAS_THREADS
+ SVN_ERR(svn_root_pools__create(&connection_pools));
- status = apr_socket_accept(&usock, sock, connection_pool);
- if (handling_mode == connection_mode_fork)
- {
- /* Collect any zombie child processes. */
- while (apr_proc_wait_all_procs(&proc, NULL, NULL, APR_NOWAIT,
- connection_pool) == APR_CHILD_DONE)
- ;
- }
- if (APR_STATUS_IS_EINTR(status)
- || APR_STATUS_IS_ECONNABORTED(status)
- || APR_STATUS_IS_ECONNRESET(status))
- {
- svn_pool_destroy(connection_pool);
- continue;
- }
+ if (handling_mode == connection_mode_thread)
+ {
+ /* create the thread pool with a valid range of threads */
+ if (max_thread_count < 1)
+ max_thread_count = 1;
+ if (min_thread_count > max_thread_count)
+ min_thread_count = max_thread_count;
+
+ status = apr_thread_pool_create(&threads,
+ min_thread_count,
+ max_thread_count,
+ pool);
if (status)
{
- err = svn_error_wrap_apr
- (status, _("Can't accept client connection"));
- return svn_cmdline_handle_exit_error(err, pool, "svnserve: ");
+ return svn_error_wrap_apr(status, _("Can't create thread pool"));
}
- /* 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. */
- status = apr_socket_opt_set(usock, APR_SO_KEEPALIVE, 1);
- if (status)
- {
- /* It's not a fatal error if we cannot enable keep-alives. */
- }
+ /* let idle threads linger for a while in case more requests are
+ coming in */
+ apr_thread_pool_idle_wait_set(threads, THREADPOOL_THREAD_IDLE_LIMIT);
- conn = svn_ra_svn_create_conn3(usock, NULL, NULL,
- params.compression_level,
- params.zero_copy_limit,
- params.error_check_interval,
- connection_pool);
+ /* don't queue requests unless we reached the worker thread limit */
+ apr_thread_pool_threshold_set(threads, 0);
+ }
+ else
+ {
+ threads = NULL;
+ }
+#endif
+ while (1)
+ {
+ connection_t *connection = NULL;
+ SVN_ERR(accept_connection(&connection, sock, &params, handling_mode,
+ pool));
if (run_mode == run_mode_listen_once)
{
- err = serve(conn, &params, connection_pool);
-
- if (err)
- svn_handle_error2(err, stdout, FALSE, "svnserve: ");
- svn_error_clear(err);
-
- apr_socket_close(usock);
- apr_socket_close(sock);
- exit(0);
+ err = serve_socket(connection, connection->pool);
+ close_connection(connection);
+ return err;
}
switch (handling_mode)
{
case connection_mode_fork:
#if APR_HAS_FORK
- status = apr_proc_fork(&proc, connection_pool);
+ status = apr_proc_fork(&proc, connection->pool);
if (status == APR_INCHILD)
{
+ /* the child would't listen to the main server's socket */
apr_socket_close(sock);
- err = serve(conn, &params, connection_pool);
- log_error(err, params.log_file,
- svn_ra_svn_conn_remote_host(conn),
- NULL, NULL, /* user, repos */
- connection_pool);
- svn_error_clear(err);
- apr_socket_close(usock);
- exit(0);
- }
- else if (status == APR_INPARENT)
- {
- apr_socket_close(usock);
+
+ /* serve_socket() logs any error it returns, so ignore it. */
+ svn_error_clear(serve_socket(connection, connection->pool));
+ close_connection(connection);
+ return SVN_NO_ERROR;
}
- else
+ else if (status != APR_INPARENT)
{
err = svn_error_wrap_apr(status, "apr_proc_fork");
- log_error(err, params.log_file,
- svn_ra_svn_conn_remote_host(conn),
- NULL, NULL, /* user, repos */
- connection_pool);
+ logger__log_error(params.logger, err, NULL, NULL);
svn_error_clear(err);
- apr_socket_close(usock);
}
- svn_pool_destroy(connection_pool);
#endif
break;
@@ -1130,46 +1307,64 @@ int main(int argc, const char *argv[])
particularly sophisticated strategy for a threaded server, it's
little different from forking one process per connection. */
#if APR_HAS_THREADS
- shared_pool = attach_shared_pool(connection_pool);
- status = apr_threadattr_create(&tattr, connection_pool);
- if (status)
- {
- err = svn_error_wrap_apr(status, _("Can't create threadattr"));
- svn_handle_error2(err, stderr, FALSE, "svnserve: ");
- svn_error_clear(err);
- exit(1);
- }
- status = apr_threadattr_detach_set(tattr, 1);
- if (status)
- {
- err = svn_error_wrap_apr(status, _("Can't set detached state"));
- svn_handle_error2(err, stderr, FALSE, "svnserve: ");
- svn_error_clear(err);
- exit(1);
- }
- thread_data = apr_palloc(connection_pool, sizeof(*thread_data));
- thread_data->conn = conn;
- thread_data->params = &params;
- thread_data->shared_pool = shared_pool;
- status = apr_thread_create(&tid, tattr, serve_thread, thread_data,
- shared_pool->pool);
+ attach_connection(connection);
+
+ status = apr_thread_pool_push(threads, serve_thread, connection,
+ 0, NULL);
if (status)
{
- err = svn_error_wrap_apr(status, _("Can't create thread"));
- svn_handle_error2(err, stderr, FALSE, "svnserve: ");
- svn_error_clear(err);
- exit(1);
+ return svn_error_wrap_apr(status, _("Can't push task"));
}
- release_shared_pool(shared_pool);
#endif
break;
case connection_mode_single:
/* Serve one connection at a time. */
- svn_error_clear(serve(conn, &params, connection_pool));
- svn_pool_destroy(connection_pool);
+ /* serve_socket() logs any error it returns, so ignore it. */
+ svn_error_clear(serve_socket(connection, connection->pool));
}
+
+ close_connection(connection);
}
/* NOTREACHED */
}
+
+int
+main(int argc, const char *argv[])
+{
+ apr_pool_t *pool;
+ int exit_code = EXIT_SUCCESS;
+ svn_error_t *err;
+
+ /* Initialize the app. */
+ if (svn_cmdline_init("svnserve", stderr) != EXIT_SUCCESS)
+ return EXIT_FAILURE;
+
+ /* Create our top-level pool. */
+ pool = apr_allocator_owner_get(svn_pool_create_allocator(TRUE));
+
+ err = sub_main(&exit_code, argc, argv, pool);
+
+ /* Flush stdout and report if it fails. It would be flushed on exit anyway
+ but this makes sure that output is not silently lost if it fails. */
+ err = svn_error_compose_create(err, svn_cmdline_fflush(stdout));
+
+ if (err)
+ {
+ exit_code = EXIT_FAILURE;
+ svn_cmdline_handle_exit_error(err, NULL, "svnserve: ");
+ }
+
+#if APR_HAS_THREADS
+ /* Explicitly wait for all threads to exit. As we found out with similar
+ code in our C test framework, the memory pool cleanup below cannot be
+ trusted to do the right thing. */
+ if (threads)
+ apr_thread_pool_destroy(threads);
+#endif
+
+ /* this will also close the server's socket */
+ svn_pool_destroy(pool);
+ return exit_code;
+}