diff options
Diffstat (limited to 'subversion/mod_dav_svn/mod_dav_svn.c')
-rw-r--r-- | subversion/mod_dav_svn/mod_dav_svn.c | 348 |
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); } |