summaryrefslogtreecommitdiff
path: root/subversion/mod_dav_svn/mod_dav_svn.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/mod_dav_svn/mod_dav_svn.c')
-rw-r--r--subversion/mod_dav_svn/mod_dav_svn.c348
1 files changed, 322 insertions, 26 deletions
diff --git a/subversion/mod_dav_svn/mod_dav_svn.c b/subversion/mod_dav_svn/mod_dav_svn.c
index 5bdb322..a97d307 100644
--- a/subversion/mod_dav_svn/mod_dav_svn.c
+++ b/subversion/mod_dav_svn/mod_dav_svn.c
@@ -22,7 +22,10 @@
* ====================================================================
*/
+#include <stdlib.h>
+
#include <apr_strings.h>
+#include <apr_hash.h>
#include <httpd.h>
#include <http_config.h>
@@ -31,6 +34,7 @@
#include <ap_provider.h>
#include <mod_dav.h>
+#include "svn_hash.h"
#include "svn_version.h"
#include "svn_cache_config.h"
#include "svn_utf.h"
@@ -39,6 +43,7 @@
#include "mod_dav_svn.h"
#include "private/svn_fspath.h"
+#include "private/svn_subr_private.h"
#include "dav_svn.h"
#include "mod_authz_svn.h"
@@ -55,6 +60,13 @@
/* per-server configuration */
typedef struct server_conf_t {
const char *special_uri;
+ svn_boolean_t use_utf8;
+
+ /* The compression level we will pass to svn_txdelta_to_svndiff3()
+ * for wire-compression. Negative value used to specify default
+ compression level. */
+ int compression_level;
+
} server_conf_t;
@@ -82,15 +94,18 @@ typedef struct dir_conf_t {
const char *xslt_uri; /* XSL transform URI */
const char *fs_parent_path; /* path to parent of SVN FS'es */
enum conf_flag autoversioning; /* whether autoversioning is active */
- enum conf_flag bulk_updates; /* whether bulk updates are allowed */
+ dav_svn__bulk_upd_conf bulk_updates; /* whether bulk updates are allowed */
enum conf_flag v2_protocol; /* whether HTTP v2 is advertised */
enum path_authz_conf path_authz_method; /* how GET subrequests are handled */
enum conf_flag list_parentpath; /* whether to allow GET of parentpath */
const char *root_dir; /* our top-level directory */
const char *master_uri; /* URI to the master SVN repos */
+ svn_version_t *master_version; /* version of master server */
const char *activities_db; /* path to activities database(s) */
enum conf_flag txdelta_cache; /* whether to enable txdelta caching */
enum conf_flag fulltext_cache; /* whether to enable fulltext caching */
+ enum conf_flag revprop_cache; /* whether to enable revprop caching */
+ const char *hooks_env; /* path to hook script env config file */
} dir_conf_t;
@@ -103,14 +118,12 @@ extern module AP_MODULE_DECLARE_DATA dav_svn_module;
/* The authz_svn provider for bypassing path authz. */
static authz_svn__subreq_bypass_func_t pathauthz_bypass_func = NULL;
-/* The compression level we will pass to svn_txdelta_to_svndiff3()
- * for wire-compression */
-static int svn__compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT;
-
static int
init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
{
svn_error_t *serr;
+ server_conf_t *conf;
+
ap_add_version_component(p, "SVN/" SVN_VER_NUMBER);
serr = svn_fs_initialize(p);
@@ -123,7 +136,8 @@ init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
}
/* This returns void, so we can't check for error. */
- svn_utf_initialize(p);
+ conf = ap_get_module_config(s->module_config, &dav_svn_module);
+ svn_utf_initialize2(conf->use_utf8, p);
return OK;
}
@@ -154,7 +168,11 @@ init_dso(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
static void *
create_server_config(apr_pool_t *p, server_rec *s)
{
- return apr_pcalloc(p, sizeof(server_conf_t));
+ server_conf_t *conf = apr_pcalloc(p, sizeof(server_conf_t));
+
+ conf->compression_level = -1;
+
+ return conf;
}
@@ -170,6 +188,17 @@ merge_server_config(apr_pool_t *p, void *base, void *overrides)
newconf->special_uri = INHERIT_VALUE(parent, child, special_uri);
+ if (child->compression_level < 0)
+ {
+ /* Inherit compression level from parent if not configured for this
+ VirtualHost. */
+ newconf->compression_level = parent->compression_level;
+ }
+ else
+ {
+ newconf->compression_level = child->compression_level;
+ }
+
return newconf;
}
@@ -185,8 +214,9 @@ create_dir_config(apr_pool_t *p, char *dir)
<Location /blah> directive. So we treat it as a urlpath. */
if (dir)
conf->root_dir = svn_urlpath__canonicalize(dir, p);
- conf->bulk_updates = CONF_FLAG_ON;
+ conf->bulk_updates = CONF_BULKUPD_DEFAULT;
conf->v2_protocol = CONF_FLAG_ON;
+ conf->hooks_env = NULL;
return conf;
}
@@ -204,6 +234,7 @@ merge_dir_config(apr_pool_t *p, void *base, void *overrides)
newconf->fs_path = INHERIT_VALUE(parent, child, fs_path);
newconf->master_uri = INHERIT_VALUE(parent, child, master_uri);
+ newconf->master_version = INHERIT_VALUE(parent, child, master_version);
newconf->activities_db = INHERIT_VALUE(parent, child, activities_db);
newconf->repo_name = INHERIT_VALUE(parent, child, repo_name);
newconf->xslt_uri = INHERIT_VALUE(parent, child, xslt_uri);
@@ -215,14 +246,16 @@ merge_dir_config(apr_pool_t *p, void *base, void *overrides)
newconf->list_parentpath = INHERIT_VALUE(parent, child, list_parentpath);
newconf->txdelta_cache = INHERIT_VALUE(parent, child, txdelta_cache);
newconf->fulltext_cache = INHERIT_VALUE(parent, child, fulltext_cache);
+ newconf->revprop_cache = INHERIT_VALUE(parent, child, revprop_cache);
newconf->root_dir = INHERIT_VALUE(parent, child, root_dir);
+ newconf->hooks_env = INHERIT_VALUE(parent, child, hooks_env);
if (parent->fs_path)
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL,
"mod_dav_svn: nested Location '%s' hinders access to '%s' "
"in SVNPath Location '%s'",
child->root_dir,
- svn_fspath__skip_ancestor(parent->root_dir, child->root_dir),
+ svn_urlpath__skip_ancestor(parent->root_dir, child->root_dir),
parent->root_dir);
return newconf;
@@ -270,6 +303,25 @@ SVNMasterURI_cmd(cmd_parms *cmd, void *config, const char *arg1)
static const char *
+SVNMasterVersion_cmd(cmd_parms *cmd, void *config, const char *arg1)
+{
+ dir_conf_t *conf = config;
+ svn_error_t *err;
+ svn_version_t *version;
+
+ err = svn_version__parse_version_string(&version, arg1, cmd->pool);
+ if (err)
+ {
+ svn_error_clear(err);
+ return "Malformed master server version string.";
+ }
+
+ conf->master_version = version;
+ return NULL;
+}
+
+
+static const char *
SVNActivitiesDB_cmd(cmd_parms *cmd, void *config, const char *arg1)
{
dir_conf_t *conf = config;
@@ -306,14 +358,26 @@ SVNAutoversioning_cmd(cmd_parms *cmd, void *config, int arg)
static const char *
-SVNAllowBulkUpdates_cmd(cmd_parms *cmd, void *config, int arg)
+SVNAllowBulkUpdates_cmd(cmd_parms *cmd, void *config, const char *arg1)
{
dir_conf_t *conf = config;
- if (arg)
- conf->bulk_updates = CONF_FLAG_ON;
+ if (apr_strnatcasecmp("on", arg1) == 0)
+ {
+ conf->bulk_updates = CONF_BULKUPD_ON;
+ }
+ else if (apr_strnatcasecmp("off", arg1) == 0)
+ {
+ conf->bulk_updates = CONF_BULKUPD_OFF;
+ }
+ else if (apr_strnatcasecmp("prefer", arg1) == 0)
+ {
+ conf->bulk_updates = CONF_BULKUPD_PREFER;
+ }
else
- conf->bulk_updates = CONF_FLAG_OFF;
+ {
+ return "Unrecognized value for SVNAllowBulkUpdates directive";
+ }
return NULL;
}
@@ -466,6 +530,19 @@ SVNCacheFullTexts_cmd(cmd_parms *cmd, void *config, int arg)
}
static const char *
+SVNCacheRevProps_cmd(cmd_parms *cmd, void *config, int arg)
+{
+ dir_conf_t *conf = config;
+
+ if (arg)
+ conf->revprop_cache = CONF_FLAG_ON;
+ else
+ conf->revprop_cache = CONF_FLAG_OFF;
+
+ return NULL;
+}
+
+static const char *
SVNInMemoryCacheSize_cmd(cmd_parms *cmd, void *config, const char *arg1)
{
svn_cache_config_t settings = *svn_cache_config_get();
@@ -488,6 +565,7 @@ SVNInMemoryCacheSize_cmd(cmd_parms *cmd, void *config, const char *arg1)
static const char *
SVNCompressionLevel_cmd(cmd_parms *cmd, void *config, const char *arg1)
{
+ server_conf_t *conf;
int value = 0;
svn_error_t *err = svn_cstring_atoi(&value, arg1);
if (err)
@@ -505,7 +583,31 @@ SVNCompressionLevel_cmd(cmd_parms *cmd, void *config, const char *arg1)
(int)SVN_DELTA_COMPRESSION_LEVEL_NONE,
(int)SVN_DELTA_COMPRESSION_LEVEL_MAX);
- svn__compression_level = value;
+ conf = ap_get_module_config(cmd->server->module_config,
+ &dav_svn_module);
+ conf->compression_level = value;
+
+ return NULL;
+}
+
+static const char *
+SVNUseUTF8_cmd(cmd_parms *cmd, void *config, int arg)
+{
+ server_conf_t *conf;
+
+ conf = ap_get_module_config(cmd->server->module_config,
+ &dav_svn_module);
+ conf->use_utf8 = arg;
+
+ return NULL;
+}
+
+static const char *
+SVNHooksEnv_cmd(cmd_parms *cmd, void *config, const char *arg1)
+{
+ dir_conf_t *conf = config;
+
+ conf->hooks_env = svn_dirent_internal_style(arg1, cmd->pool);
return NULL;
}
@@ -573,7 +675,7 @@ dav_svn_get_repos_path(request_rec *r,
/* Construct the full path from the parent path base directory
and the repository name. */
- *repos_path = svn_urlpath__join(fs_parent_path, repos_name, r->pool);
+ *repos_path = svn_dirent_join(fs_parent_path, repos_name, r->pool);
return NULL;
}
@@ -608,6 +710,16 @@ dav_svn__get_master_uri(request_rec *r)
}
+svn_version_t *
+dav_svn__get_master_version(request_rec *r)
+{
+ dir_conf_t *conf;
+
+ conf = ap_get_module_config(r->per_dir_config, &dav_svn_module);
+ return conf->master_uri ? conf->master_version : NULL;
+}
+
+
const char *
dav_svn__get_xslt_uri(request_rec *r)
{
@@ -694,23 +806,55 @@ dav_svn__get_autoversioning_flag(request_rec *r)
}
-svn_boolean_t
+dav_svn__bulk_upd_conf
dav_svn__get_bulk_updates_flag(request_rec *r)
{
dir_conf_t *conf;
conf = ap_get_module_config(r->per_dir_config, &dav_svn_module);
- return conf->bulk_updates == CONF_FLAG_ON;
+
+ /* SVNAllowBulkUpdates is 'on' by default. */
+ if (conf->bulk_updates == CONF_BULKUPD_DEFAULT)
+ return CONF_BULKUPD_ON;
+ else
+ return conf->bulk_updates;
}
svn_boolean_t
-dav_svn__get_v2_protocol_flag(request_rec *r)
+dav_svn__check_httpv2_support(request_rec *r)
{
dir_conf_t *conf;
+ svn_boolean_t available;
conf = ap_get_module_config(r->per_dir_config, &dav_svn_module);
- return conf->v2_protocol == CONF_FLAG_ON;
+ available = conf->v2_protocol == CONF_FLAG_ON;
+
+ /* If our configuration says that HTTPv2 is available, but we are
+ proxying requests to a master Subversion server which lacks
+ support for HTTPv2, we dumb ourselves down. */
+ if (available)
+ {
+ svn_version_t *version = dav_svn__get_master_version(r);
+ if (version && (! svn_version__at_least(version, 1, 7, 0)))
+ available = FALSE;
+ }
+ return available;
+}
+
+
+svn_boolean_t
+dav_svn__check_ephemeral_txnprops_support(request_rec *r)
+{
+ svn_version_t *version = dav_svn__get_master_version(r);
+
+ /* We know this server supports ephemeral txnprops. But if we're
+ proxying requests to a master server, we need to see if it
+ supports them, too. */
+ if (version && (! svn_version__at_least(version, 1, 8, 0)))
+ return FALSE;
+
+ return TRUE;
}
@@ -781,10 +925,41 @@ dav_svn__get_fulltext_cache_flag(request_rec *r)
}
+svn_boolean_t
+dav_svn__get_revprop_cache_flag(request_rec *r)
+{
+ dir_conf_t *conf;
+
+ conf = ap_get_module_config(r->per_dir_config, &dav_svn_module);
+ return conf->revprop_cache == CONF_FLAG_ON;
+}
+
+
int
-dav_svn__get_compression_level(void)
+dav_svn__get_compression_level(request_rec *r)
{
- return svn__compression_level;
+ server_conf_t *conf;
+
+ conf = ap_get_module_config(r->server->module_config,
+ &dav_svn_module);
+
+ if (conf->compression_level < 0)
+ {
+ return SVN_DELTA_COMPRESSION_LEVEL_DEFAULT;
+ }
+ else
+ {
+ return conf->compression_level;
+ }
+}
+
+const char *
+dav_svn__get_hooks_env(request_rec *r)
+{
+ dir_conf_t *conf;
+
+ conf = ap_get_module_config(r->per_dir_config, &dav_svn_module);
+ return conf->hooks_env;
}
static void
@@ -924,7 +1099,95 @@ static int dav_svn__handler(request_rec *r)
return DECLINED;
}
+#define NO_MAP_TO_STORAGE_NOTE "dav_svn-no-map-to-storage"
+/* Fill the filename on the request with a bogus path since we aren't serving
+ * a file off the disk. This means that <Directory> blocks will not match and
+ * that %f in logging formats will show as "svn:/path/to/repo/path/in/repo". */
+static int dav_svn__translate_name(request_rec *r)
+{
+ const char *fs_path, *repos_basename, *repos_path, *slash;
+ const char *ignore_cleaned_uri, *ignore_relative_path;
+ int ignore_had_slash;
+ dir_conf_t *conf = ap_get_module_config(r->per_dir_config, &dav_svn_module);
+
+ /* module is not configured, bail out early */
+ if (!conf->fs_path && !conf->fs_parent_path)
+ return DECLINED;
+
+ if (dav_svn__is_parentpath_list(r))
+ {
+ /* SVNListParentPath is on and the request is for the conf->root_dir,
+ * so just set the repos_basename to an empty string and the repos_path
+ * to NULL so we end up just reporting our parent path as the bogus
+ * path. */
+ repos_basename = "";
+ repos_path = NULL;
+ }
+ else
+ {
+ /* Retrieve path to repo and within repo for the request */
+ dav_error *err = dav_svn_split_uri(r, r->uri, conf->root_dir,
+ &ignore_cleaned_uri,
+ &ignore_had_slash, &repos_basename,
+ &ignore_relative_path, &repos_path);
+ if (err)
+ {
+ dav_svn__log_err(r, err, APLOG_ERR);
+ return err->status;
+ }
+ }
+
+ if (conf->fs_parent_path)
+ {
+ fs_path = svn_dirent_join(conf->fs_parent_path, repos_basename,
+ r->pool);
+ }
+ else
+ {
+ fs_path = conf->fs_path;
+ }
+
+ /* Avoid a trailing slash on the bogus path when repos_path is just "/" and
+ * ensure that there is always a slash between fs_path and repos_path as
+ * long as the repos_path is not an empty path. */
+ slash = "";
+ if (repos_path)
+ {
+ if ('/' == repos_path[0] && '\0' == repos_path[1])
+ repos_path = NULL;
+ else if ('/' != repos_path[0] && '\0' != repos_path[0])
+ slash = "/";
+ }
+
+ /* Combine 'svn:', fs_path and repos_path to produce the bogus path we're
+ * placing in r->filename. We can't use our standard join helpers such
+ * as svn_dirent_join. fs_path is a dirent and repos_path is a fspath
+ * (that can be trivially converted to a relpath by skipping the leading
+ * slash). In general it is safe to join these, but when a path in a
+ * repository is 'trunk/c:hi' this results in a non canonical dirent on
+ * Windows. Instead we just cat them together. */
+ r->filename = apr_pstrcat(r->pool,
+ "svn:", fs_path, slash, repos_path, NULL);
+
+ /* Leave a note to ourselves so that we know not to decline in the
+ * map_to_storage hook. */
+ apr_table_setn(r->notes, NO_MAP_TO_STORAGE_NOTE, (const char*)1);
+ return OK;
+}
+
+/* Prevent core_map_to_storage from running if we prevented the r->filename
+ * from being set since core_map_to_storage doesn't like r->filename being
+ * bogus. */
+static int dav_svn__map_to_storage(request_rec *r)
+{
+ /* Check a note we left in translate_name since map_to_storage doesn't
+ * have access to our configuration. */
+ if (apr_table_get(r->notes, NO_MAP_TO_STORAGE_NOTE))
+ return OK;
+
+ return DECLINED;
+}
@@ -977,16 +1240,22 @@ static const command_rec cmds[] =
"specifies a URI to access a master Subversion repository"),
/* per directory/location */
+ AP_INIT_TAKE1("SVNMasterVersion", SVNMasterVersion_cmd, NULL, ACCESS_CONF,
+ "specifies the Subversion release version of a master "
+ "Subversion server "),
+
+ /* per directory/location */
AP_INIT_TAKE1("SVNActivitiesDB", SVNActivitiesDB_cmd, NULL, ACCESS_CONF,
"specifies the location in the filesystem in which the "
"activities database(s) should be stored"),
/* per directory/location */
- AP_INIT_FLAG("SVNAllowBulkUpdates", SVNAllowBulkUpdates_cmd, NULL,
- ACCESS_CONF|RSRC_CONF,
- "enables support for bulk update-style requests (as opposed to "
- "only skeletal reports that require additional per-file "
- "downloads."),
+ AP_INIT_TAKE1("SVNAllowBulkUpdates", SVNAllowBulkUpdates_cmd, NULL,
+ ACCESS_CONF|RSRC_CONF,
+ "enables support for bulk update-style requests (On, default), "
+ "as opposed to only skeletal reports that require additional "
+ "per-file downloads (Off). Use Prefer to tell the svn client "
+ "to always use bulk update requests, if supported."),
/* per directory/location */
AP_INIT_FLAG("SVNAdvertiseV2Protocol", SVNAdvertiseV2Protocol_cmd, NULL,
@@ -1008,6 +1277,14 @@ static const command_rec cmds[] =
"if sufficient in-memory cache is available "
"(default is Off)."),
+ /* per directory/location */
+ AP_INIT_FLAG("SVNCacheRevProps", SVNCacheRevProps_cmd, NULL,
+ ACCESS_CONF|RSRC_CONF,
+ "speeds up 'svn ls -v', export and checkout operations"
+ "but should only be enabled under the conditions described"
+ "in the documentation"
+ "(default is Off)."),
+
/* per server */
AP_INIT_TAKE1("SVNInMemoryCacheSize", SVNInMemoryCacheSize_cmd, NULL,
RSRC_CONF,
@@ -1021,6 +1298,19 @@ static const command_rec cmds[] =
"content over the network (0 for no compression, 9 for "
"maximum, 5 is default)."),
+ /* per server */
+ AP_INIT_FLAG("SVNUseUTF8",
+ SVNUseUTF8_cmd, NULL,
+ RSRC_CONF,
+ "use UTF-8 as native character encoding (default is ASCII)."),
+
+ /* per directory/location */
+ AP_INIT_TAKE1("SVNHooksEnv", SVNHooksEnv_cmd, NULL,
+ ACCESS_CONF|RSRC_CONF,
+ "Sets the path to the configuration file for the environment "
+ "of hook scripts. If not absolute, the path is relative to "
+ "the repository's conf directory (by default the hooks-env "
+ "file in the repository is used)."),
{ NULL }
};
@@ -1071,6 +1361,12 @@ register_hooks(apr_pool_t *pconf)
ap_register_input_filter("IncomingRewrite", dav_svn__location_in_filter,
NULL, AP_FTYPE_CONTENT_SET);
ap_hook_fixups(dav_svn__proxy_request_fixup, NULL, NULL, APR_HOOK_MIDDLE);
+ /* translate_name hook is LAST so that it doesn't interfere with modules
+ * like mod_alias that are MIDDLE. */
+ ap_hook_translate_name(dav_svn__translate_name, NULL, NULL, APR_HOOK_LAST);
+ /* map_to_storage hook is LAST to avoid interferring with mod_http's
+ * handling of OPTIONS and TRACE. */
+ ap_hook_map_to_storage(dav_svn__map_to_storage, NULL, NULL, APR_HOOK_LAST);
}