diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
commit | cf46733632c7279a9fd0fe6ce26f9185a4ae82a9 (patch) | |
tree | da27775a2161723ef342e91af41a8b51fedef405 /subversion/mod_dav_svn | |
parent | bb0ef45f7c46b0ae221b26265ef98a768c33f820 (diff) | |
download | subversion-tarball-master.tar.gz |
subversion-1.9.7HEADsubversion-1.9.7master
Diffstat (limited to 'subversion/mod_dav_svn')
25 files changed, 1546 insertions, 1007 deletions
diff --git a/subversion/mod_dav_svn/activity.c b/subversion/mod_dav_svn/activity.c index 895c4cf..1c779d2 100644 --- a/subversion/mod_dav_svn/activity.c +++ b/subversion/mod_dav_svn/activity.c @@ -193,7 +193,6 @@ dav_svn__store_activity(const dav_svn_repos *repos, const char *txn_name) { const char *final_path; - const char *tmp_path; const char *activity_contents; svn_error_t *err; @@ -209,11 +208,9 @@ dav_svn__store_activity(const dav_svn_repos *repos, activity_contents = apr_psprintf(repos->pool, "%s\n%s\n", txn_name, activity_id); - /* ### is there another directory we already have and can write to? */ - err = svn_io_write_unique(&tmp_path, - svn_dirent_dirname(final_path, repos->pool), + err = svn_io_write_atomic(final_path, activity_contents, strlen(activity_contents), - svn_io_file_del_none, repos->pool); + NULL /* copy_perms path */, repos->pool); if (err) { svn_error_t *serr = svn_error_quick_wrap(err, @@ -225,15 +222,6 @@ dav_svn__store_activity(const dav_svn_repos *repos, repos->pool); } - err = svn_io_file_rename(tmp_path, final_path, repos->pool); - if (err) - { - svn_error_clear(svn_io_remove_file2(tmp_path, TRUE, repos->pool)); - return dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR, - "could not replace files.", - repos->pool); - } - return NULL; } diff --git a/subversion/mod_dav_svn/authz.c b/subversion/mod_dav_svn/authz.c index 27763b0..9b5c7d6 100644 --- a/subversion/mod_dav_svn/authz.c +++ b/subversion/mod_dav_svn/authz.c @@ -57,7 +57,7 @@ dav_svn__allow_read(request_rec *r, /* Sometimes we get paths that do not start with '/' and hence below uri concatenation would lead to wrong uris .*/ if (path && path[0] != '/') - path = apr_pstrcat(pool, "/", path, NULL); + path = apr_pstrcat(pool, "/", path, SVN_VA_NULL); /* If bypass is specified and authz has exported the provider. Otherwise, we fall through to the full version. This should be @@ -80,7 +80,8 @@ dav_svn__allow_read(request_rec *r, uri_type = DAV_SVN__BUILD_URI_PUBLIC; /* Build a Version Resource uri representing (rev, path). */ - uri = dav_svn__build_uri(repos, uri_type, rev, path, FALSE, pool); + uri = dav_svn__build_uri(repos, uri_type, rev, path, FALSE /* add_href */, + pool); /* Check if GET would work against this uri. */ subreq = ap_sub_req_method_uri("GET", uri, r, r->output_filters); diff --git a/subversion/mod_dav_svn/dav_svn.h b/subversion/mod_dav_svn/dav_svn.h index 8786518..9d11a0a 100644 --- a/subversion/mod_dav_svn/dav_svn.h +++ b/subversion/mod_dav_svn/dav_svn.h @@ -54,7 +54,7 @@ extern "C" { /* Option values for SVNAllowBulkUpdates. Note that it's important that CONF_BULKUPD_DEFAULT is 0 to make - dav_svn_merge_dir_config do the right thing. */ + merge_dir_config in mod_dav_svn do the right thing. */ typedef enum dav_svn__bulk_upd_conf { CONF_BULKUPD_DEFAULT, CONF_BULKUPD_ON, @@ -329,6 +329,10 @@ svn_boolean_t dav_svn__get_fulltext_cache_flag(request_rec *r); /* for the repository referred to by this request, is revprop caching active? */ svn_boolean_t dav_svn__get_revprop_cache_flag(request_rec *r); +/* has block read mode been enabled for the repository referred to by this + * request? */ +svn_boolean_t dav_svn__get_block_read_flag(request_rec *r); + /* for the repository referred to by this request, are subrequests bypassed? * A function pointer if yes, NULL if not. */ @@ -442,6 +446,40 @@ const char *dav_svn__get_vtxn_stub(request_rec *r); /* For accessing transaction properties (typically "!svn/vtxr") */ const char *dav_svn__get_vtxn_root_stub(request_rec *r); + +/*** Output helpers ***/ + +/* An opaque type which represents an output for a particular request. + + All writes should target a dav_svn__output object by either using + the dav_svn__brigade functions or by preparing a bucket brigade and + passing it to the output with dav_svn__output_pass_brigade(). + + IMPORTANT: Don't write to an ap_filter_t coming from mod_dav, and + use this wrapper and the corresponding private API instead. Using + the ap_filter_t can cause unbounded memory usage with self-removing + output filters (e.g., with the filters installed by mod_headers or + mod_deflate). + + See https://mail-archives.apache.org/mod_mbox/httpd-dev/201608.mbox/%3C20160822151917.GA22369%40redhat.com%3E +*/ +typedef struct dav_svn__output dav_svn__output; + +/* Create the output wrapper for request R, allocated in POOL. */ +dav_svn__output * +dav_svn__output_create(request_rec *r, + apr_pool_t *pool); + +/* Get a bucket allocator to use for all bucket/brigade creations + when writing to OUTPUT. */ +apr_bucket_alloc_t * +dav_svn__output_get_bucket_alloc(dav_svn__output *output); + +/* Pass the bucket brigade BB down to the OUTPUT's filter stack. */ +svn_error_t * +dav_svn__output_pass_brigade(dav_svn__output *output, + apr_bucket_brigade *bb); + /*** activity.c ***/ @@ -482,6 +520,8 @@ dav_svn__store_activity(const dav_svn_repos *repos, /* POST request handler. (Used by HTTP protocol v2 clients only.) */ int dav_svn__method_post(request_rec *r); +/* Request handler to GET Subversion internal status (FSFS cache). */ +int dav_svn__status(request_rec *r); /*** repos.c ***/ @@ -628,7 +668,7 @@ dav_svn__insert_all_liveprops(request_rec *r, /* Generate the HTTP response body for a successful MERGE. */ /* ### more docco */ dav_error * -dav_svn__merge_response(ap_filter_t *output, +dav_svn__merge_response(dav_svn__output *output, const dav_svn_repos *repos, svn_revnum_t new_rev, const char *post_commit_err, @@ -662,49 +702,49 @@ static const dav_report_elem dav_svn__reports_list[] = { dav_error * dav_svn__update_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output); + dav_svn__output *output); dav_error * dav_svn__log_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output); + dav_svn__output *output); dav_error * dav_svn__dated_rev_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output); + dav_svn__output *output); dav_error * dav_svn__get_locations_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output); + dav_svn__output *output); dav_error * dav_svn__get_location_segments_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output); + dav_svn__output *output); dav_error * dav_svn__file_revs_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output); + dav_svn__output *output); dav_error * dav_svn__replay_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output); + dav_svn__output *output); dav_error * dav_svn__get_mergeinfo_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output); + dav_svn__output *output); dav_error * dav_svn__get_locks_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output); + dav_svn__output *output); dav_error * dav_svn__get_deleted_rev_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output); + dav_svn__output *output); dav_error * dav_svn__get_inherited_props_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output); + dav_svn__output *output); /*** posts/ ***/ @@ -712,11 +752,11 @@ dav_svn__get_inherited_props_report(const dav_resource *resource, dav_error * dav_svn__post_create_txn(const dav_resource *resource, svn_skel_t *request_skel, - ap_filter_t *output); + dav_svn__output *output); dav_error * dav_svn__post_create_txn_with_props(const dav_resource *resource, svn_skel_t *request_skel, - ap_filter_t *output); + dav_svn__output *output); /*** authz.c ***/ @@ -792,12 +832,10 @@ dav_svn__authz_read_func(dav_svn__authz_read_baton *baton); default value for the error code. */ dav_error * -dav_svn__new_error_tag(apr_pool_t *pool, +dav_svn__new_error_svn(apr_pool_t *pool, int status, int error_id, - const char *desc, - const char *namespace, - const char *tagname); + const char *desc); /* A wrapper around mod_dav's dav_new_error, mod_dav_svn uses this @@ -878,7 +916,7 @@ dav_svn__build_uri(const dav_svn_repos *repos, enum dav_svn__build_what what, svn_revnum_t revision, const char *path, - int add_href, + svn_boolean_t add_href, apr_pool_t *pool); @@ -918,23 +956,28 @@ int dav_svn__find_ns(const apr_array_header_t *namespaces, const char *uri); /* Write LEN bytes from DATA to OUTPUT using BB. */ svn_error_t *dav_svn__brigade_write(apr_bucket_brigade *bb, - ap_filter_t *output, + dav_svn__output *output, const char *buf, apr_size_t len); /* Write NULL-terminated string STR to OUTPUT using BB. */ svn_error_t *dav_svn__brigade_puts(apr_bucket_brigade *bb, - ap_filter_t *output, + dav_svn__output *output, const char *str); /* Write data to OUTPUT using BB, using FMT as the output format string. */ svn_error_t *dav_svn__brigade_printf(apr_bucket_brigade *bb, - ap_filter_t *output, + dav_svn__output *output, const char *fmt, ...) __attribute__((format(printf, 3, 4))); +/* Write an unspecified number of strings to OUTPUT using BB. */ +svn_error_t *dav_svn__brigade_putstrs(apr_bucket_brigade *bb, + dav_svn__output *output, + ...) SVN_NEEDS_SENTINEL_NULL; + @@ -968,11 +1011,10 @@ dav_svn__sanitize_error(svn_error_t *serr, /* Return a writable generic stream that will encode its output to base64 - and send it to the Apache filter OUTPUT using BB. Allocate the stream in - POOL. */ + and send it to OUTPUT using BB. Allocate the stream in POOL. */ svn_stream_t * dav_svn__make_base64_output_stream(apr_bucket_brigade *bb, - ap_filter_t *output, + dav_svn__output *output, apr_pool_t *pool); /* In INFO->r->subprocess_env set "SVN-ACTION" to LINE, "SVN-REPOS" to @@ -1014,7 +1056,8 @@ dav_svn__operational_log(struct dav_resource_private *info, const char *line); */ dav_error * dav_svn__final_flush_or_error(request_rec *r, apr_bucket_brigade *bb, - ap_filter_t *output, dav_error *preferred_err, + dav_svn__output *output, + dav_error *preferred_err, apr_pool_t *pool); /* Log a DAV error response. diff --git a/subversion/mod_dav_svn/deadprops.c b/subversion/mod_dav_svn/deadprops.c index 5d228a4..94e1a69 100644 --- a/subversion/mod_dav_svn/deadprops.c +++ b/subversion/mod_dav_svn/deadprops.c @@ -339,7 +339,7 @@ db_open(apr_pool_t *p, db->p = svn_pool_create(p); /* ### temp hack */ - db->work = svn_stringbuf_ncreate("", 0, db->p); + db->work = svn_stringbuf_create_empty(db->p); /* make our path-based authz callback available to svn_repos_* funcs. */ arb = apr_pcalloc(p, sizeof(*arb)); @@ -654,16 +654,14 @@ static void get_name(dav_db *db, dav_prop_name *pname) } else { - const void *name; - - apr_hash_this(db->hi, &name, NULL, NULL); + const char *name = apr_hash_this_key(db->hi); #define PREFIX_LEN (sizeof(SVN_PROP_PREFIX) - 1) if (strncmp(name, SVN_PROP_PREFIX, PREFIX_LEN) == 0) #undef PREFIX_LEN { pname->ns = SVN_DAV_PROP_NS_SVN; - pname->name = (const char *)name + 4; + pname->name = name + 4; } else { diff --git a/subversion/mod_dav_svn/liveprops.c b/subversion/mod_dav_svn/liveprops.c index 725ee92..384a810 100644 --- a/subversion/mod_dav_svn/liveprops.c +++ b/subversion/mod_dav_svn/liveprops.c @@ -36,6 +36,7 @@ #include "svn_time.h" #include "svn_dav.h" #include "svn_props.h" +#include "svn_ctype.h" #include "private/svn_dav_protocol.h" @@ -422,7 +423,43 @@ insert_prop_internal(const dav_resource *resource, if (last_author == NULL) return DAV_PROP_INSERT_NOTDEF; - value = apr_xml_quote_string(scratch_pool, last_author->data, 1); + if (svn_xml_is_xml_safe(last_author->data, last_author->len) + || !resource->info->repos->is_svn_client) + value = apr_xml_quote_string(scratch_pool, last_author->data, 1); + else + { + /* We are talking to a Subversion client, which will (like any proper + xml parser) error out if we produce control characters in XML. + + However Subversion clients process both the generic + <creator-displayname /> as the custom element for svn:author. + + Let's skip outputting the invalid characters here to make the XML + valid, so clients can see the custom element. + + Subversion Clients will then either use a slightly invalid + author (unlikely) or more likely use the second result, which + will be transferred with full escaping capabilities. + + We have tests in place to assert proper behavior over the RA layer. + */ + apr_size_t i; + svn_stringbuf_t *buf; + + buf = svn_stringbuf_create_from_string(last_author, scratch_pool); + + for (i = 0; i < buf->len; i++) + { + char c = buf->data[i]; + + if (svn_ctype_iscntrl(c)) + { + svn_stringbuf_remove(buf, i--, 1); + } + } + + value = apr_xml_quote_string(scratch_pool, buf->data, 1); + } break; } @@ -549,7 +586,7 @@ insert_prop_internal(const dav_resource *resource, return DAV_PROP_INSERT_NOTSUPP; value = dav_svn__build_uri(resource->info->repos, DAV_SVN__BUILD_URI_BC, resource->info->root.rev, NULL, - 1 /* add_href */, scratch_pool); + TRUE /* add_href */, scratch_pool); break; case DAV_PROPID_checked_in: @@ -578,7 +615,8 @@ insert_prop_internal(const dav_resource *resource, } s = dav_svn__build_uri(resource->info->repos, DAV_SVN__BUILD_URI_BASELINE, - revnum, NULL, 0 /* add_href */, scratch_pool); + revnum, NULL, FALSE /* add_href */, + scratch_pool); value = apr_psprintf(scratch_pool, "<D:href>%s</D:href>", apr_xml_quote_string(scratch_pool, s, 1)); } @@ -596,7 +634,7 @@ insert_prop_internal(const dav_resource *resource, s = dav_svn__build_uri(resource->info->repos, DAV_SVN__BUILD_URI_VERSION, rev_to_use, resource->info->repos_path, - 0 /* add_href */, scratch_pool); + FALSE /* add_href */, scratch_pool); value = apr_psprintf(scratch_pool, "<D:href>%s</D:href>", apr_xml_quote_string(scratch_pool, s, 1)); } @@ -610,7 +648,7 @@ insert_prop_internal(const dav_resource *resource, return DAV_PROP_INSERT_NOTSUPP; value = dav_svn__build_uri(resource->info->repos, DAV_SVN__BUILD_URI_VCC, SVN_IGNORED_REVNUM, NULL, - 1 /* add_href */, scratch_pool); + TRUE /* add_href */, scratch_pool); break; case DAV_PROPID_version_name: @@ -749,20 +787,30 @@ insert_prop_internal(const dav_resource *resource, case SVN_PROPID_deadprop_count: { - unsigned int propcount; - apr_hash_t *proplist; + svn_boolean_t has_props; if (resource->type != DAV_RESOURCE_TYPE_REGULAR) return DAV_PROP_INSERT_NOTSUPP; - serr = svn_fs_node_proplist(&proplist, - resource->info->root.root, - resource->info->repos_path, scratch_pool); + /* Retrieving the actual properties is quite expensive while + svn clients only want to know if there are properties, by + using this svn defined property. + + Our and and SvnKit's implementation of the ra layer check + for '> 0' to provide the boolean if the node has custom + properties or not, so starting with 1.9 we just provide + "1" or "0". + */ + serr = svn_fs_node_has_props(&has_props, + resource->info->root.root, + resource->info->repos_path, + scratch_pool); + if (serr != NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, serr->apr_err, resource->info->r, - "Can't fetch proplist of '%s': " + "Can't fetch has properties on '%s': " "%s", resource->info->repos_path, serr->message); @@ -771,8 +819,7 @@ insert_prop_internal(const dav_resource *resource, break; } - propcount = apr_hash_count(proplist); - value = apr_psprintf(scratch_pool, "%u", propcount); + value = has_props ? "1" : "0"; break; } diff --git a/subversion/mod_dav_svn/lock.c b/subversion/mod_dav_svn/lock.c index 68d6de5..06c038a 100644 --- a/subversion/mod_dav_svn/lock.c +++ b/subversion/mod_dav_svn/lock.c @@ -92,7 +92,7 @@ svn_lock_to_dav_lock(dav_lock **dlock, "<D:owner xmlns:D=\"DAV:\">", apr_xml_quote_string(pool, slock->comment, 1), - "</D:owner>", (char *)NULL); + "</D:owner>", SVN_VA_NULL); } else { @@ -134,7 +134,7 @@ unescape_xml(const char **output, apr_xml_doc *xml_doc; apr_status_t apr_err; const char *xml_input = apr_pstrcat - (pool, "<?xml version=\"1.0\" encoding=\"utf-8\"?>", input, (char *)NULL); + (pool, "<?xml version=\"1.0\" encoding=\"utf-8\"?>", input, SVN_VA_NULL); apr_err = apr_xml_parser_feed(xml_parser, xml_input, strlen(xml_input)); if (!apr_err) @@ -274,8 +274,13 @@ parse_locktoken(apr_pool_t *pool, static const char * format_locktoken(apr_pool_t *p, const dav_locktoken *locktoken) { - /* libsvn_fs already produces a valid locktoken URI. */ - return apr_pstrdup(p, locktoken->uuid_str); + svn_stringbuf_t *formatted + = svn_stringbuf_create_ensure(strlen(locktoken->uuid_str), p); + + /* libsvn_fs produces a locktoken URI that will be valid XML when + escaped. */ + svn_xml_escape_cdata_cstring(&formatted, locktoken->uuid_str, p); + return formatted->data; } @@ -782,7 +787,31 @@ append_locks(dav_lockdb *lockdb, DAV_ERR_LOCK_SAVE_LOCK, "Anonymous lock creation is not allowed."); } - else if (serr && (serr->apr_err == SVN_ERR_REPOS_HOOK_FAILURE || + else if (serr && serr->apr_err == SVN_ERR_REPOS_POST_LOCK_HOOK_FAILED) + { + /* The lock was created in the repository, so we should report the node + as locked to the client */ + + /* First log the hook failure, for diagnostics. This clears serr */ + dav_svn__log_err(info->r, + dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, + "Post lock hook failure.", + resource->pool), + APLOG_WARNING); + + /* How can we report the error to the client? + + We can't return an error code, as that would make it impossible + to return the lock details? + + Add yet another custom header? + Just an header doesn't handle a full error chain... + + ### Current behavior: we don't report an error. + */ + + } + else if (serr && (svn_error_find_cause(serr, SVN_ERR_REPOS_HOOK_FAILURE) || serr->apr_err == SVN_ERR_FS_NO_SUCH_LOCK || serr->apr_err == SVN_ERR_FS_LOCK_EXPIRED || SVN_ERR_IS_LOCK_ERROR(serr))) @@ -838,14 +867,14 @@ remove_lock(dav_lockdb *lockdb, /* Sanity check: if the resource has no associated path in the fs, then there's nothing to do. */ if (! resource->info->repos_path) - return 0; + return NULL; /* Another easy out: if an svn client sent a 'keep_locks' header (typically in a DELETE request, as part of 'svn commit --no-unlock'), then ignore dav_method_delete()'s attempt to unconditionally remove the lock. */ if (info->keep_locks) - return 0; + return NULL; /* If the resource's fs path is unreadable, we don't allow a lock to be removed from it. */ @@ -892,6 +921,22 @@ remove_lock(dav_lockdb *lockdb, DAV_ERR_LOCK_SAVE_LOCK, "Anonymous lock removal is not allowed."); } + else if (serr && serr->apr_err == SVN_ERR_REPOS_POST_UNLOCK_HOOK_FAILED + && !resource->info->repos->is_svn_client) + { + /* Generic DAV clients don't understand the specific error code we + would produce here as being just a warning, so lets produce a + success result. We removed the lock anyway. */ + + /* First log the hook failure, for diagnostics. This clears serr */ + dav_svn__log_err(info->r, + dav_svn__convert_err(serr, + HTTP_INTERNAL_SERVER_ERROR, + "Post unlock hook failure.", + resource->pool), + APLOG_WARNING); + + } else if (serr) return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Failed to remove a lock.", @@ -905,10 +950,41 @@ remove_lock(dav_lockdb *lockdb, resource->info->r->pool)); } - return 0; + return NULL; +} + +static dav_error * +remove_lock_svn_output(dav_lockdb *lockdb, + const dav_resource *resource, + const dav_locktoken *locktoken) +{ + dav_error *derr = remove_lock(lockdb, resource, locktoken); + int status; + + if (!derr + || !resource->info->repos + || !resource->info->repos->is_svn_client + || (strcmp(lockdb->info->r->method, "UNLOCK") != 0)) + return derr; + + /* Ok, we have a nice error chain but mod_dav doesn't offer us a way to + present it to the client as it will only use the status code for + generating a standard error... + + Luckily the unlock processing for the "UNLOCK" method is very simple: + call this function and return the result. + + That allows us to just force a response and tell httpd that we are done */ + status = dav_svn__error_response_tag(lockdb->info->r, derr); + + /* status = DONE */ + + /* And push an error that will make mod_dav just report that it is done */ + return dav_push_error(resource->pool, status, derr->error_id, NULL, derr); } + /* ** Refresh all locks, found on the specified resource, which has a ** locktoken in the provided list. @@ -980,7 +1056,7 @@ refresh_locks(dav_lockdb *lockdb, DAV_ERR_LOCK_SAVE_LOCK, "Anonymous lock refreshing is not allowed."); } - else if (serr && (serr->apr_err == SVN_ERR_REPOS_HOOK_FAILURE || + else if (serr && (svn_error_find_cause(serr, SVN_ERR_REPOS_HOOK_FAILURE) || serr->apr_err == SVN_ERR_FS_NO_SUCH_LOCK || serr->apr_err == SVN_ERR_FS_LOCK_EXPIRED || SVN_ERR_IS_LOCK_ERROR(serr))) @@ -1014,7 +1090,7 @@ const dav_hooks_locks dav_svn__hooks_locks = { find_lock, has_locks, append_locks, - remove_lock, + remove_lock_svn_output, refresh_locks, NULL, NULL /* hook structure context */ diff --git a/subversion/mod_dav_svn/merge.c b/subversion/mod_dav_svn/merge.c index 3d6d80b..5407a0f 100644 --- a/subversion/mod_dav_svn/merge.c +++ b/subversion/mod_dav_svn/merge.c @@ -72,21 +72,20 @@ send_response(const dav_svn_repos *repos, svn_fs_root_t *root, const char *path, svn_boolean_t is_dir, - ap_filter_t *output, + dav_svn__output *output, apr_bucket_brigade *bb, apr_pool_t *pool) { const char *href; const char *vsn_url; - apr_status_t status; svn_revnum_t rev_to_use; href = dav_svn__build_uri(repos, DAV_SVN__BUILD_URI_PUBLIC, SVN_IGNORED_REVNUM, path, 0 /* add_href */, pool); rev_to_use = dav_svn__get_safe_cr(root, path, pool); vsn_url = dav_svn__build_uri(repos, DAV_SVN__BUILD_URI_VERSION, - rev_to_use, path, 0 /* add_href */, pool); - status = ap_fputstrs(output, bb, + rev_to_use, path, FALSE /* add_href */, pool); + SVN_ERR(dav_svn__brigade_putstrs(bb, output, "<D:response>" DEBUG_CR "<D:href>", apr_xml_quote_string(pool, href, 1), @@ -103,9 +102,7 @@ send_response(const dav_svn_repos *repos, "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR "</D:propstat>" DEBUG_CR "</D:response>" DEBUG_CR, - NULL); - if (status != APR_SUCCESS) - return svn_error_wrap_apr(status, "Can't write response to output"); + SVN_VA_NULL)); return SVN_NO_ERROR; } @@ -115,7 +112,7 @@ static svn_error_t * do_resources(const dav_svn_repos *repos, svn_fs_root_t *root, svn_revnum_t revision, - ap_filter_t *output, + dav_svn__output *output, apr_bucket_brigade *bb, apr_pool_t *pool) { @@ -136,12 +133,13 @@ do_resources(const dav_svn_repos *repos, const void *key; void *val; const char *path; + apr_ssize_t path_len; svn_fs_path_change2_t *change; svn_boolean_t send_self; svn_boolean_t send_parent; svn_pool_clear(subpool); - apr_hash_this(hi, &key, NULL, &val); + apr_hash_this(hi, &key, &path_len, &val); path = key; change = val; @@ -170,14 +168,14 @@ do_resources(const dav_svn_repos *repos, { /* If we haven't already sent this path, send it (and then remember that we sent it). */ - if (! svn_hash_gets(sent, path)) + if (! apr_hash_get(sent, path, path_len)) { svn_node_kind_t kind; SVN_ERR(svn_fs_check_path(&kind, root, path, subpool)); SVN_ERR(send_response(repos, root, path, kind == svn_node_dir, output, bb, subpool)); - svn_hash_sets(sent, path, (void *)1); + apr_hash_set(sent, path, path_len, (void *)1); } } if (send_parent) @@ -208,7 +206,7 @@ do_resources(const dav_svn_repos *repos, */ dav_error * -dav_svn__merge_response(ap_filter_t *output, +dav_svn__merge_response(dav_svn__output *output, const dav_svn_repos *repos, svn_revnum_t new_rev, const char *post_commit_err, @@ -224,7 +222,6 @@ dav_svn__merge_response(ap_filter_t *output, svn_string_t *creationdate, *creator_displayname; const char *post_commit_err_elem = NULL, *post_commit_header_info = NULL; - apr_status_t status; serr = svn_fs_revision_root(&root, repos->fs, new_rev, pool); if (serr != NULL) @@ -235,13 +232,14 @@ dav_svn__merge_response(ap_filter_t *output, repos->pool); } - bb = apr_brigade_create(pool, output->c->bucket_alloc); + bb = apr_brigade_create(pool, + dav_svn__output_get_bucket_alloc(output)); /* prep some strings */ /* the HREF for the baseline is actually the VCC */ vcc = dav_svn__build_uri(repos, DAV_SVN__BUILD_URI_VCC, SVN_IGNORED_REVNUM, - NULL, 0 /* add_href */, pool); + NULL, FALSE /* add_href */, pool); /* the version-name of the baseline is the revision number */ rev = apr_psprintf(pool, "%ld", new_rev); @@ -285,7 +283,7 @@ dav_svn__merge_response(ap_filter_t *output, } - status = ap_fputstrs(output, bb, + serr = dav_svn__brigade_putstrs(bb, output, DAV_XML_HEADER DEBUG_CR "<D:merge-response xmlns:D=\"DAV:\"", post_commit_header_info, @@ -304,44 +302,47 @@ dav_svn__merge_response(ap_filter_t *output, "<D:resourcetype><D:baseline/></D:resourcetype>" DEBUG_CR, post_commit_err_elem, DEBUG_CR "<D:version-name>", rev, "</D:version-name>" DEBUG_CR, - NULL); - if (status != APR_SUCCESS) - return dav_svn__new_error(repos->pool, HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not write output"); + SVN_VA_NULL); + if (serr != NULL) + return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, + "Could not write output", + repos->pool); if (creationdate) { - status = ap_fputstrs(output, bb, + serr = dav_svn__brigade_putstrs(bb, output, "<D:creationdate>", apr_xml_quote_string(pool, creationdate->data, 1), "</D:creationdate>" DEBUG_CR, - NULL); - if (status != APR_SUCCESS) - return dav_svn__new_error(repos->pool, HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not write output"); + SVN_VA_NULL); + if (serr != NULL) + return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, + "Could not write output", + repos->pool); } if (creator_displayname) { - status = ap_fputstrs(output, bb, + serr = dav_svn__brigade_putstrs(bb, output, "<D:creator-displayname>", apr_xml_quote_string(pool, creator_displayname->data, 1), "</D:creator-displayname>" DEBUG_CR, - NULL); - if (status != APR_SUCCESS) - return dav_svn__new_error(repos->pool, HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not write output"); + SVN_VA_NULL); + if (serr != NULL) + return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, + "Could not write output", + repos->pool); } - status = ap_fputstrs(output, bb, + serr = dav_svn__brigade_putstrs(bb, output, "</D:prop>" DEBUG_CR "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR "</D:propstat>" DEBUG_CR "</D:response>" DEBUG_CR, - - NULL); - if (status != APR_SUCCESS) - return dav_svn__new_error(repos->pool, HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not write output"); + SVN_VA_NULL); + if (serr != NULL) + return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, + "Could not write output", + repos->pool); /* ONLY have dir_delta drive the editor if the caller asked us to generate a full MERGE response. svn clients can ask us to @@ -370,18 +371,20 @@ dav_svn__merge_response(ap_filter_t *output, } /* wrap up the merge response */ - status = ap_fputs(output, bb, - "</D:updated-set>" DEBUG_CR - "</D:merge-response>" DEBUG_CR); - if (status != APR_SUCCESS) - return dav_svn__new_error(repos->pool, HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not write output"); + serr = dav_svn__brigade_puts(bb, output, + "</D:updated-set>" DEBUG_CR + "</D:merge-response>" DEBUG_CR); + if (serr != NULL) + return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, + "Could not write output", + repos->pool); /* send whatever is left in the brigade */ - status = ap_pass_brigade(output, bb); - if (status != APR_SUCCESS) - return dav_svn__new_error(repos->pool, HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not write output"); + serr = dav_svn__output_pass_brigade(output, bb); + if (serr != NULL) + return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, + "Could not write output", + repos->pool); return NULL; } diff --git a/subversion/mod_dav_svn/mirror.c b/subversion/mod_dav_svn/mirror.c index e8b19a8..5b0f1f5 100644 --- a/subversion/mod_dav_svn/mirror.c +++ b/subversion/mod_dav_svn/mirror.c @@ -56,7 +56,7 @@ static int proxy_request_fixup(request_rec *r, r->filename = (char *) svn_path_uri_encode(apr_pstrcat(r->pool, "proxy:", master_uri, uri_segment, - (char *)NULL), + SVN_VA_NULL), r->pool); r->handler = "proxy-server"; @@ -102,11 +102,11 @@ int dav_svn__proxy_request_fixup(request_rec *r) r->method_number == M_GET) { if ((seg = ap_strstr(r->uri, root_dir))) { if (ap_strstr_c(seg, apr_pstrcat(r->pool, special_uri, - "/wrk/", (char *)NULL)) + "/wrk/", SVN_VA_NULL)) || ap_strstr_c(seg, apr_pstrcat(r->pool, special_uri, - "/txn/", (char *)NULL)) + "/txn/", SVN_VA_NULL)) || ap_strstr_c(seg, apr_pstrcat(r->pool, special_uri, - "/txr/", (char *)NULL))) { + "/txr/", SVN_VA_NULL))) { int rv; seg += strlen(root_dir); rv = proxy_request_fixup(r, master_uri, seg); @@ -259,7 +259,7 @@ apr_status_t dav_svn__location_header_filter(ap_filter_t *f, new_uri = ap_construct_url(r->pool, apr_pstrcat(r->pool, dav_svn__get_root_dir(r), "/", - start_foo, (char *)NULL), + start_foo, SVN_VA_NULL), r); apr_table_set(r->headers_out, "Location", new_uri); } diff --git a/subversion/mod_dav_svn/mod_dav_svn.c b/subversion/mod_dav_svn/mod_dav_svn.c index a97d307..a6988fd 100644 --- a/subversion/mod_dav_svn/mod_dav_svn.c +++ b/subversion/mod_dav_svn/mod_dav_svn.c @@ -72,7 +72,7 @@ typedef struct server_conf_t { /* A tri-state enum used for per directory on/off flags. Note that it's important that CONF_FLAG_DEFAULT is 0 to make - dav_svn_merge_dir_config do the right thing. */ + merge_dir_config in mod_dav_svn do the right thing. */ enum conf_flag { CONF_FLAG_DEFAULT, CONF_FLAG_ON, @@ -105,6 +105,7 @@ typedef struct dir_conf_t { 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 */ + enum conf_flag block_read; /* whether to enable block read mode */ const char *hooks_env; /* path to hook script env config file */ } dir_conf_t; @@ -142,6 +143,25 @@ init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) return OK; } +static svn_error_t * +malfunction_handler(svn_boolean_t can_return, + const char *file, int line, + const char *expr) +{ + if (expr) + ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL, + "mod_dav_svn: file '%s', line %d, assertion \"%s\" failed", + file, line, expr); + else + ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL, + "mod_dav_svn: file '%s', line %d, internal malfunction", + file, line); + abort(); + + /* Should not be reached. */ + return SVN_NO_ERROR; +} + static int init_dso(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp) { @@ -161,6 +181,8 @@ init_dso(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp) return HTTP_INTERNAL_SERVER_ERROR; } + svn_error_set_malfunction_handler(malfunction_handler); + return OK; } @@ -215,8 +237,9 @@ create_dir_config(apr_pool_t *p, char *dir) if (dir) conf->root_dir = svn_urlpath__canonicalize(dir, p); conf->bulk_updates = CONF_BULKUPD_DEFAULT; - conf->v2_protocol = CONF_FLAG_ON; + conf->v2_protocol = CONF_FLAG_DEFAULT; conf->hooks_env = NULL; + conf->txdelta_cache = CONF_FLAG_DEFAULT; return conf; } @@ -247,6 +270,7 @@ merge_dir_config(apr_pool_t *p, void *base, void *overrides) 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->block_read = INHERIT_VALUE(parent, child, block_read); newconf->root_dir = INHERIT_VALUE(parent, child, root_dir); newconf->hooks_env = INHERIT_VALUE(parent, child, hooks_env); @@ -543,6 +567,19 @@ SVNCacheRevProps_cmd(cmd_parms *cmd, void *config, int arg) } static const char * +SVNBlockRead_cmd(cmd_parms *cmd, void *config, int arg) +{ + dir_conf_t *conf = config; + + if (arg) + conf->block_read = CONF_FLAG_ON; + else + conf->block_read = 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(); @@ -612,6 +649,16 @@ SVNHooksEnv_cmd(cmd_parms *cmd, void *config, const char *arg1) return NULL; } +static svn_boolean_t +get_conf_flag(enum conf_flag flag, svn_boolean_t default_value) +{ + if (flag == CONF_FLAG_ON) + return TRUE; + else if (flag == CONF_FLAG_OFF) + return FALSE; + else /* CONF_FLAG_DEFAULT*/ + return default_value; +} /** Accessor functions for the module's configuration state **/ @@ -636,9 +683,10 @@ dav_svn__get_fs_parent_path(request_rec *r) AP_MODULE_DECLARE(dav_error *) -dav_svn_get_repos_path(request_rec *r, - const char *root_path, - const char **repos_path) +dav_svn_get_repos_path2(request_rec *r, + const char *root_path, + const char **repos_path, + apr_pool_t *pool) { const char *fs_path; @@ -666,19 +714,26 @@ dav_svn_get_repos_path(request_rec *r, /* Split the svn URI to get the name of the repository below the parent path. */ - derr = dav_svn_split_uri(r, r->uri, root_path, - &ignored_cleaned_uri, &ignored_had_slash, - &repos_name, - &ignored_relative, &ignored_path_in_repos); + derr = dav_svn_split_uri2(r, r->uri, root_path, + &ignored_cleaned_uri, &ignored_had_slash, + &repos_name, + &ignored_relative, &ignored_path_in_repos, pool); if (derr) return derr; /* Construct the full path from the parent path base directory and the repository name. */ - *repos_path = svn_dirent_join(fs_parent_path, repos_name, r->pool); + *repos_path = svn_dirent_join(fs_parent_path, repos_name, pool); return NULL; } +AP_MODULE_DECLARE(dav_error *) +dav_svn_get_repos_path(request_rec *r, + const char *root_path, + const char **repos_path) +{ + return dav_svn_get_repos_path2(r, root_path, repos_path, r->pool); +} const char * dav_svn__get_repo_name(request_rec *r) @@ -745,7 +800,7 @@ const char * dav_svn__get_me_resource_uri(request_rec *r) { return apr_pstrcat(r->pool, dav_svn__get_special_uri(r), "/me", - (char *)NULL); + SVN_VA_NULL); } @@ -753,7 +808,7 @@ const char * dav_svn__get_rev_stub(request_rec *r) { return apr_pstrcat(r->pool, dav_svn__get_special_uri(r), "/rev", - (char *)NULL); + SVN_VA_NULL); } @@ -761,7 +816,7 @@ const char * dav_svn__get_rev_root_stub(request_rec *r) { return apr_pstrcat(r->pool, dav_svn__get_special_uri(r), "/rvr", - (char *)NULL); + SVN_VA_NULL); } @@ -769,14 +824,14 @@ const char * dav_svn__get_txn_stub(request_rec *r) { return apr_pstrcat(r->pool, dav_svn__get_special_uri(r), "/txn", - (char *)NULL); + SVN_VA_NULL); } const char * dav_svn__get_txn_root_stub(request_rec *r) { - return apr_pstrcat(r->pool, dav_svn__get_special_uri(r), "/txr", (char *)NULL); + return apr_pstrcat(r->pool, dav_svn__get_special_uri(r), "/txr", SVN_VA_NULL); } @@ -784,7 +839,7 @@ const char * dav_svn__get_vtxn_stub(request_rec *r) { return apr_pstrcat(r->pool, dav_svn__get_special_uri(r), "/vtxn", - (char *)NULL); + SVN_VA_NULL); } @@ -792,7 +847,7 @@ const char * dav_svn__get_vtxn_root_stub(request_rec *r) { return apr_pstrcat(r->pool, dav_svn__get_special_uri(r), "/vtxr", - (char *)NULL); + SVN_VA_NULL); } @@ -828,7 +883,7 @@ dav_svn__check_httpv2_support(request_rec *r) svn_boolean_t available; conf = ap_get_module_config(r->per_dir_config, &dav_svn_module); - available = conf->v2_protocol == CONF_FLAG_ON; + available = get_conf_flag(conf->v2_protocol, TRUE); /* If our configuration says that HTTPv2 is available, but we are proxying requests to a master Subversion server which lacks @@ -911,7 +966,9 @@ dav_svn__get_txdelta_cache_flag(request_rec *r) dir_conf_t *conf; conf = ap_get_module_config(r->per_dir_config, &dav_svn_module); - return conf->txdelta_cache == CONF_FLAG_ON; + + /* txdelta caching is enabled by default. */ + return get_conf_flag(conf->txdelta_cache, TRUE); } @@ -935,6 +992,15 @@ dav_svn__get_revprop_cache_flag(request_rec *r) } +svn_boolean_t +dav_svn__get_block_read_flag(request_rec *r) +{ + dir_conf_t *conf; + + conf = ap_get_module_config(r->per_dir_config, &dav_svn_module); + return conf->block_read == CONF_FLAG_ON; +} + int dav_svn__get_compression_level(request_rec *r) { @@ -984,7 +1050,6 @@ merge_xml_filter_insert(request_rec *r) typedef struct merge_ctx_t { apr_bucket_brigade *bb; apr_xml_parser *parser; - apr_pool_t *pool; } merge_ctx_t; @@ -1014,7 +1079,6 @@ merge_xml_in_filter(ap_filter_t *f, f->ctx = ctx = apr_palloc(r->pool, sizeof(*ctx)); ctx->parser = apr_xml_parser_create(r->pool); ctx->bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); - apr_pool_create(&ctx->pool, r->pool); } rv = ap_get_brigade(f->next, ctx->bb, mode, block, readbytes); @@ -1103,10 +1167,11 @@ static int dav_svn__handler(request_rec *r) /* 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". */ + * %f in logging formats will show as "dav_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 *fs_path, *repos_basename, *repos_path; 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); @@ -1148,19 +1213,11 @@ static int dav_svn__translate_name(request_rec *r) 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 = "/"; - } + /* Avoid a trailing slash on the bogus path when repos_path is just "/" */ + if (repos_path && '/' == repos_path[0] && '\0' == repos_path[1]) + repos_path = NULL; - /* Combine 'svn:', fs_path and repos_path to produce the bogus path we're + /* Combine 'dav_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 @@ -1168,7 +1225,7 @@ static int dav_svn__translate_name(request_rec *r) * 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); + "dav_svn:", fs_path, repos_path, SVN_VA_NULL); /* Leave a note to ourselves so that we know not to decline in the * map_to_storage hook. */ @@ -1268,7 +1325,7 @@ static const command_rec cmds[] = ACCESS_CONF|RSRC_CONF, "speeds up data access to older revisions by caching " "delta information if sufficient in-memory cache is " - "available (default is Off)."), + "available (default is On)."), /* per directory/location */ AP_INIT_FLAG("SVNCacheFullTexts", SVNCacheFullTexts_cmd, NULL, @@ -1285,12 +1342,19 @@ static const command_rec cmds[] = "in the documentation" "(default is Off)."), + /* per directory/location */ + AP_INIT_FLAG("SVNBlockRead", SVNBlockRead_cmd, NULL, + ACCESS_CONF|RSRC_CONF, + "speeds up operations of FSFS 1.9+ repositories if large" + "caches (see SVNInMemoryCacheSize) have been configured." + "(default is Off)."), + /* per server */ AP_INIT_TAKE1("SVNInMemoryCacheSize", SVNInMemoryCacheSize_cmd, NULL, RSRC_CONF, "specifies the maximum size in kB per process of Subversion's " - "in-memory object cache (default value is 16384; 0 deactivates " - "the cache)."), + "in-memory object cache (default value is 16384; 0 switches " + "to dynamically sized caches)."), /* per server */ AP_INIT_TAKE1("SVNCompressionLevel", SVNCompressionLevel_cmd, NULL, RSRC_CONF, @@ -1345,6 +1409,9 @@ register_hooks(apr_pool_t *pconf) /* general request handler for methods which mod_dav DECLINEs. */ ap_hook_handler(dav_svn__handler, NULL, NULL, APR_HOOK_LAST); + /* Handler to GET Subversion's FSFS cache stats, a bit like mod_status. */ + ap_hook_handler(dav_svn__status, NULL, NULL, APR_HOOK_MIDDLE); + /* live property handling */ dav_hook_gather_propsets(dav_svn__gather_propsets, NULL, NULL, APR_HOOK_MIDDLE); diff --git a/subversion/mod_dav_svn/posts/create_txn.c b/subversion/mod_dav_svn/posts/create_txn.c index 4775749..b5e4e99 100644 --- a/subversion/mod_dav_svn/posts/create_txn.c +++ b/subversion/mod_dav_svn/posts/create_txn.c @@ -34,7 +34,7 @@ dav_error * dav_svn__post_create_txn(const dav_resource *resource, svn_skel_t *request_skel, - ap_filter_t *output) + dav_svn__output *output) { const char *txn_name; const char *vtxn_name; @@ -75,7 +75,7 @@ dav_svn__post_create_txn(const dav_resource *resource, dav_error * dav_svn__post_create_txn_with_props(const dav_resource *resource, svn_skel_t *request_skel, - ap_filter_t *output) + dav_svn__output *output) { const char *txn_name; const char *vtxn_name; diff --git a/subversion/mod_dav_svn/reports/dated-rev.c b/subversion/mod_dav_svn/reports/dated-rev.c index 61d26f5..5ba607b 100644 --- a/subversion/mod_dav_svn/reports/dated-rev.c +++ b/subversion/mod_dav_svn/reports/dated-rev.c @@ -50,7 +50,7 @@ dav_error * dav_svn__dated_rev_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output) + dav_svn__output *output) { apr_xml_elem *child; int ns; @@ -58,7 +58,6 @@ dav_svn__dated_rev_report(const dav_resource *resource, svn_revnum_t rev; apr_bucket_brigade *bb; svn_error_t *err; - apr_status_t apr_err; dav_error *derr = NULL; /* Find the DAV:creationdate element and get the requested time from it. */ @@ -94,15 +93,16 @@ dav_svn__dated_rev_report(const dav_resource *resource, "Could not access revision times."); } - bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); - apr_err = ap_fprintf(output, bb, + bb = apr_brigade_create(resource->pool, + dav_svn__output_get_bucket_alloc(output)); + err = dav_svn__brigade_printf(bb, output, DAV_XML_HEADER DEBUG_CR "<S:dated-rev-report xmlns:S=\"" SVN_XML_NAMESPACE "\" " "xmlns:D=\"DAV:\">" DEBUG_CR "<D:" SVN_DAV__VERSION_NAME ">%ld</D:" SVN_DAV__VERSION_NAME ">""</S:dated-rev-report>", rev); - if (apr_err) - derr = dav_svn__convert_err(svn_error_create(apr_err, 0, NULL), + if (err) + derr = dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR, "Error writing REPORT response.", resource->pool); diff --git a/subversion/mod_dav_svn/reports/deleted-rev.c b/subversion/mod_dav_svn/reports/deleted-rev.c index 66d0192..a21aaec 100644 --- a/subversion/mod_dav_svn/reports/deleted-rev.c +++ b/subversion/mod_dav_svn/reports/deleted-rev.c @@ -41,7 +41,7 @@ dav_error * dav_svn__get_deleted_rev_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output) + dav_svn__output *output) { apr_xml_elem *child; int ns; @@ -52,7 +52,6 @@ dav_svn__get_deleted_rev_report(const dav_resource *resource, svn_revnum_t deleted_rev; apr_bucket_brigade *bb; svn_error_t *err; - apr_status_t apr_err; dav_error *derr = NULL; /* Sanity check. */ @@ -61,12 +60,10 @@ dav_svn__get_deleted_rev_report(const dav_resource *resource, "The request does not specify a repository path"); ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE); if (ns == -1) - return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0, + return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0, "The request does not contain the 'svn:' " "namespace, so it is not going to have " - "certain required elements.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "certain required elements"); for (child = doc->root->first_child; child != NULL; child = child->next) { @@ -104,10 +101,8 @@ dav_svn__get_deleted_rev_report(const dav_resource *resource, && SVN_IS_VALID_REVNUM(peg_rev) && SVN_IS_VALID_REVNUM(end_rev))) { - return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0, - "Not all parameters passed.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0, + "Not all parameters passed"); } /* Do what we actually came here for: Find the rev abs_path was deleted. */ @@ -121,16 +116,17 @@ dav_svn__get_deleted_rev_report(const dav_resource *resource, "Could not find revision path was deleted."); } - bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); - apr_err = ap_fprintf(output, bb, + bb = apr_brigade_create(resource->pool, + dav_svn__output_get_bucket_alloc(output)); + err = dav_svn__brigade_printf(bb, output, DAV_XML_HEADER DEBUG_CR "<S:get-deleted-rev-report xmlns:S=\"" SVN_XML_NAMESPACE "\" xmlns:D=\"DAV:\">" DEBUG_CR "<D:" SVN_DAV__VERSION_NAME ">%ld</D:" SVN_DAV__VERSION_NAME ">""</S:get-deleted-rev-report>", deleted_rev); - if (apr_err) - derr = dav_svn__convert_err(svn_error_create(apr_err, 0, NULL), + if (err) + derr = dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR, "Error writing REPORT response.", resource->pool); diff --git a/subversion/mod_dav_svn/reports/file-revs.c b/subversion/mod_dav_svn/reports/file-revs.c index 108fd7f..5392776 100644 --- a/subversion/mod_dav_svn/reports/file-revs.c +++ b/subversion/mod_dav_svn/reports/file-revs.c @@ -43,7 +43,7 @@ struct file_rev_baton { apr_bucket_brigade *bb; /* where to deliver the output */ - ap_filter_t *output; + dav_svn__output *output; /* Whether we've written the <S:file-revs-report> header. Allows for lazy writes to support mod_dav-based error handling. */ @@ -149,7 +149,7 @@ file_rev_handler(void *baton, apr_pool_t *pool) { struct file_rev_baton *frb = baton; - apr_pool_t *subpool = svn_pool_create(pool); + apr_pool_t *iterpool = svn_pool_create(pool); apr_hash_index_t *hi; int i; @@ -169,11 +169,11 @@ file_rev_handler(void *baton, const char *pname; const svn_string_t *pval; - svn_pool_clear(subpool); + svn_pool_clear(iterpool); apr_hash_this(hi, &key, NULL, &val); pname = key; pval = val; - SVN_ERR(send_prop(frb, "rev-prop", pname, pval, subpool)); + SVN_ERR(send_prop(frb, "rev-prop", pname, pval, iterpool)); } /* Send file prop changes. */ @@ -181,17 +181,17 @@ file_rev_handler(void *baton, { const svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t); - svn_pool_clear(subpool); + svn_pool_clear(iterpool); if (prop->value) SVN_ERR(send_prop(frb, "set-prop", prop->name, prop->value, - subpool)); + iterpool)); else { /* Property was removed. */ SVN_ERR(dav_svn__brigade_printf(frb->bb, frb->output, "<S:remove-prop name=\"%s\"/>" DEBUG_CR, - apr_xml_quote_string(subpool, + apr_xml_quote_string(iterpool, prop->name, 1))); } @@ -223,7 +223,7 @@ file_rev_handler(void *baton, SVN_ERR(dav_svn__brigade_puts(frb->bb, frb->output, "</S:file-rev>" DEBUG_CR)); - svn_pool_destroy(subpool); + svn_pool_destroy(iterpool); return SVN_NO_ERROR; } @@ -234,7 +234,7 @@ file_rev_handler(void *baton, dav_error * dav_svn__file_revs_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output) + dav_svn__output *output) { svn_error_t *serr; dav_error *derr = NULL; @@ -262,12 +262,10 @@ dav_svn__file_revs_report(const dav_resource *resource, in this namespace, so is this necessary at all? */ if (ns == -1) { - return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0, + return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0, "The request does not contain the 'svn:' " "namespace, so it is not going to have " - "certain required elements.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "certain required elements"); } /* Get request information. */ @@ -302,13 +300,11 @@ dav_svn__file_revs_report(const dav_resource *resource, /* Check that all parameters are present and valid. */ if (! abs_path) - return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0, - "Not all parameters passed.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0, + "Not all parameters passed"); frb.bb = apr_brigade_create(resource->pool, - output->c->bucket_alloc); + dav_svn__output_get_bucket_alloc(output)); frb.output = output; frb.needs_header = TRUE; frb.svndiff_version = resource->info->svndiff_version; @@ -331,7 +327,7 @@ dav_svn__file_revs_report(const dav_resource *resource, right then, so r->status remains 0, hence HTTP status 200 would be misleadingly returned. */ return (dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, - serr->message, resource->pool)); + NULL, resource->pool)); } if ((serr = maybe_send_header(&frb))) diff --git a/subversion/mod_dav_svn/reports/get-location-segments.c b/subversion/mod_dav_svn/reports/get-location-segments.c index d3e91e4..794e27f 100644 --- a/subversion/mod_dav_svn/reports/get-location-segments.c +++ b/subversion/mod_dav_svn/reports/get-location-segments.c @@ -48,7 +48,7 @@ struct location_segment_baton { svn_boolean_t sent_opener; - ap_filter_t *output; + dav_svn__output *output; apr_bucket_brigade *bb; dav_svn__authz_read_baton arb; }; @@ -79,28 +79,26 @@ location_segment_receiver(svn_location_segment_t *segment, apr_pool_t *pool) { struct location_segment_baton *b = baton; - apr_status_t apr_err; SVN_ERR(maybe_send_opener(b)); if (segment->path) { const char *path_quoted = apr_xml_quote_string(pool, segment->path, 1); - apr_err = ap_fprintf(b->output, b->bb, + + SVN_ERR(dav_svn__brigade_printf(b->bb, b->output, "<S:location-segment path=\"%s\" " "range-start=\"%ld\" range-end=\"%ld\"/>" DEBUG_CR, path_quoted, - segment->range_start, segment->range_end); + segment->range_start, segment->range_end)); } else { - apr_err = ap_fprintf(b->output, b->bb, + SVN_ERR(dav_svn__brigade_printf(b->bb, b->output, "<S:location-segment " "range-start=\"%ld\" range-end=\"%ld\"/>" DEBUG_CR, - segment->range_start, segment->range_end); + segment->range_start, segment->range_end)); } - if (apr_err) - return svn_error_create(apr_err, 0, NULL); return SVN_NO_ERROR; } @@ -108,7 +106,7 @@ location_segment_receiver(svn_location_segment_t *segment, dav_error * dav_svn__get_location_segments_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output) + dav_svn__output *output) { svn_error_t *serr; dav_error *derr = NULL; @@ -129,12 +127,10 @@ dav_svn__get_location_segments_report(const dav_resource *resource, ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE); if (ns == -1) { - return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0, + return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0, "The request does not contain the 'svn:' " "namespace, so it is not going to have " - "certain required elements.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "certain required elements"); } /* Gather the parameters. */ @@ -177,10 +173,8 @@ dav_svn__get_location_segments_report(const dav_resource *resource, /* Check that all parameters are present and valid. */ if (! abs_path) - return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0, - "Not all parameters passed.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0, + "Not all parameters passed"); /* No START_REV or PEG_REVISION? We'll use HEAD. */ if (!SVN_IS_VALID_REVNUM(start_rev) || !SVN_IS_VALID_REVNUM(peg_revision)) @@ -205,24 +199,21 @@ dav_svn__get_location_segments_report(const dav_resource *resource, end_rev = 0; if (end_rev > start_rev) - return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0, + return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0, "End revision must not be younger than " - "start revision", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "start revision"); if (start_rev > peg_revision) - return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0, + return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0, "Start revision must not be younger than " - "peg revision", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "peg revision"); /* Build an authz read baton. */ arb.r = resource->info->r; arb.repos = resource->info->repos; /* Build the bucket brigade we'll use for output. */ - bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); + bb = apr_brigade_create(resource->pool, + dav_svn__output_get_bucket_alloc(output)); /* Do what we came here for. */ location_segment_baton.sent_opener = FALSE; @@ -236,7 +227,7 @@ dav_svn__get_location_segments_report(const dav_resource *resource, dav_svn__authz_read_func(&arb), &arb, resource->pool))) { - derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, serr->message, + derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, NULL, resource->pool); goto cleanup; } diff --git a/subversion/mod_dav_svn/reports/get-locations.c b/subversion/mod_dav_svn/reports/get-locations.c index 164045f..da70300 100644 --- a/subversion/mod_dav_svn/reports/get-locations.c +++ b/subversion/mod_dav_svn/reports/get-locations.c @@ -44,23 +44,20 @@ #include "../dav_svn.h" -static apr_status_t -send_get_locations_report(ap_filter_t *output, +static svn_error_t * +send_get_locations_report(dav_svn__output *output, apr_bucket_brigade *bb, const dav_resource *resource, apr_hash_t *fs_locations) { apr_hash_index_t *hi; - apr_pool_t *pool; - apr_status_t apr_err; + apr_pool_t *pool = resource->pool; - pool = resource->pool; - - apr_err = ap_fprintf(output, bb, DAV_XML_HEADER DEBUG_CR + SVN_ERR(dav_svn__brigade_printf( + bb, output, + DAV_XML_HEADER DEBUG_CR "<S:get-locations-report xmlns:S=\"" SVN_XML_NAMESPACE - "\" xmlns:D=\"DAV:\">" DEBUG_CR); - if (apr_err) - return apr_err; + "\" xmlns:D=\"DAV:\">" DEBUG_CR)); for (hi = apr_hash_first(pool, fs_locations); hi; hi = apr_hash_next(hi)) { @@ -70,24 +67,25 @@ send_get_locations_report(ap_filter_t *output, apr_hash_this(hi, &key, NULL, &value); path_quoted = apr_xml_quote_string(pool, value, 1); - apr_err = ap_fprintf(output, bb, "<S:location " + SVN_ERR(dav_svn__brigade_printf( + bb, output, "<S:location " "rev=\"%ld\" path=\"%s\"/>" DEBUG_CR, - *(const svn_revnum_t *)key, path_quoted); - if (apr_err) - return apr_err; + *(const svn_revnum_t *)key, path_quoted)); } - return ap_fprintf(output, bb, "</S:get-locations-report>" DEBUG_CR); + + SVN_ERR(dav_svn__brigade_printf(bb, output, + "</S:get-locations-report>" DEBUG_CR)); + return SVN_NO_ERROR; } dav_error * dav_svn__get_locations_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output) + dav_svn__output *output) { svn_error_t *serr; dav_error *derr = NULL; - apr_status_t apr_err; apr_bucket_brigade *bb; dav_svn__authz_read_baton arb; @@ -112,12 +110,10 @@ dav_svn__get_locations_report(const dav_resource *resource, ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE); if (ns == -1) { - return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0, + return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0, "The request does not contain the 'svn:' " "namespace, so it is not going to have " - "certain required elements.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "certain required elements"); } /* Gather the parameters. */ @@ -154,10 +150,8 @@ dav_svn__get_locations_report(const dav_resource *resource, /* Check that all parameters are present and valid. */ if (! (abs_path && SVN_IS_VALID_REVNUM(peg_revision))) - return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0, - "Not all parameters passed.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0, + "Not all parameters passed"); /* Build an authz read baton */ arb.r = resource->info->r; @@ -171,16 +165,17 @@ dav_svn__get_locations_report(const dav_resource *resource, if (serr) { - return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, - serr->message, resource->pool); + return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, NULL, + resource->pool); } - bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); + bb = apr_brigade_create(resource->pool, + dav_svn__output_get_bucket_alloc(output)); - apr_err = send_get_locations_report(output, bb, resource, fs_locations); + serr = send_get_locations_report(output, bb, resource, fs_locations); - if (apr_err) - derr = dav_svn__convert_err(svn_error_create(apr_err, 0, NULL), + if (serr) + derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Error writing REPORT response.", resource->pool); diff --git a/subversion/mod_dav_svn/reports/get-locks.c b/subversion/mod_dav_svn/reports/get-locks.c index 06fd700..bb9c168 100644 --- a/subversion/mod_dav_svn/reports/get-locks.c +++ b/subversion/mod_dav_svn/reports/get-locks.c @@ -44,67 +44,51 @@ report in libsvn_ra_neon/get_locks.c. */ -#define SVN_APR_ERR(expr) \ - do { \ - apr_status_t apr_status__temp = (expr); \ - if (apr_status__temp) \ - return apr_status__temp; \ - } while (0) - - /* Transmit LOCKS (a hash of Subversion filesystem locks keyed by - path) across OUTPUT using BB. Use POOL for necessary allocations. - - NOTE: As written, this function currently returns one of only two - status values -- "success", and "we had trouble writing out to the - output stream". If you need to return something more interesting, - you'll probably want to generate dav_error's here instead of - passing back only apr_status_t's. */ -static apr_status_t + path) across OUTPUT using BB. Use POOL for necessary allocations. */ +static svn_error_t * send_get_lock_response(apr_hash_t *locks, - ap_filter_t *output, + dav_svn__output *output, apr_bucket_brigade *bb, apr_pool_t *pool) { - apr_pool_t *subpool; + apr_pool_t *iterpool; apr_hash_index_t *hi; /* start sending report */ - SVN_APR_ERR(ap_fprintf(output, bb, + SVN_ERR(dav_svn__brigade_printf(bb, output, DAV_XML_HEADER DEBUG_CR "<S:get-locks-report xmlns:S=\"" SVN_XML_NAMESPACE "\" xmlns:D=\"DAV:\">" DEBUG_CR)); /* stream the locks */ - subpool = svn_pool_create(pool); + iterpool = svn_pool_create(pool); for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi)) { - void *val; const svn_lock_t *lock; - svn_pool_clear(subpool); - apr_hash_this(hi, NULL, NULL, &val); - lock = val; + svn_pool_clear(iterpool); + lock = apr_hash_this_val(hi); /* Begin the <S:lock> tag, transmitting the path, token, and creation date. */ - SVN_APR_ERR(ap_fprintf(output, bb, + SVN_ERR(dav_svn__brigade_printf(bb, output, "<S:lock>" DEBUG_CR "<S:path>%s</S:path>" DEBUG_CR "<S:token>%s</S:token>" DEBUG_CR "<S:creationdate>%s</S:creationdate>" DEBUG_CR, - apr_xml_quote_string(subpool, lock->path, 1), - apr_xml_quote_string(subpool, lock->token, 1), + apr_xml_quote_string(iterpool, lock->path, 1), + apr_xml_quote_string(iterpool, lock->token, 1), svn_time_to_cstring(lock->creation_date, - subpool))); + iterpool))); /* Got expiration date? Tell the client. */ if (lock->expiration_date) - SVN_APR_ERR(ap_fprintf(output, bb, + SVN_ERR(dav_svn__brigade_printf(bb, output, "<S:expirationdate>%s</S:expirationdate>" DEBUG_CR, svn_time_to_cstring(lock->expiration_date, - subpool))); + iterpool))); /* Transmit the lock ownership information. */ if (lock->owner) @@ -114,7 +98,7 @@ send_get_lock_response(apr_hash_t *locks, if (svn_xml_is_xml_safe(lock->owner, strlen(lock->owner))) { - owner = apr_xml_quote_string(subpool, lock->owner, 1); + owner = apr_xml_quote_string(iterpool, lock->owner, 1); } else { @@ -124,11 +108,11 @@ send_get_lock_response(apr_hash_t *locks, owner_string.data = lock->owner; owner_string.len = strlen(lock->owner); encoded_owner = svn_base64_encode_string2(&owner_string, TRUE, - subpool); + iterpool); owner = encoded_owner->data; owner_base64 = TRUE; } - SVN_APR_ERR(ap_fprintf(output, bb, + SVN_ERR(dav_svn__brigade_printf(bb, output, "<S:owner %s>%s</S:owner>" DEBUG_CR, owner_base64 ? "encoding=\"base64\"" : "", owner)); @@ -142,7 +126,7 @@ send_get_lock_response(apr_hash_t *locks, if (svn_xml_is_xml_safe(lock->comment, strlen(lock->comment))) { - comment = apr_xml_quote_string(subpool, lock->comment, 1); + comment = apr_xml_quote_string(iterpool, lock->comment, 1); } else { @@ -152,38 +136,36 @@ send_get_lock_response(apr_hash_t *locks, comment_string.data = lock->comment; comment_string.len = strlen(lock->comment); encoded_comment = svn_base64_encode_string2(&comment_string, - TRUE, subpool); + TRUE, iterpool); comment = encoded_comment->data; comment_base64 = TRUE; } - SVN_APR_ERR(ap_fprintf(output, bb, + SVN_ERR(dav_svn__brigade_printf(bb, output, "<S:comment %s>%s</S:comment>" DEBUG_CR, comment_base64 ? "encoding=\"base64\"" : "", comment)); } /* Okay, finish up this lock by closing the <S:lock> tag. */ - SVN_APR_ERR(ap_fprintf(output, bb, "</S:lock>" DEBUG_CR)); + SVN_ERR(dav_svn__brigade_printf(bb, output, "</S:lock>" DEBUG_CR)); } - svn_pool_destroy(subpool); + svn_pool_destroy(iterpool); /* Finish the report */ - SVN_APR_ERR(ap_fprintf(output, bb, "</S:get-locks-report>" DEBUG_CR)); + SVN_ERR(dav_svn__brigade_printf(bb, output, + "</S:get-locks-report>" DEBUG_CR)); return APR_SUCCESS; } -#undef SVN_APR_ERR - dav_error * dav_svn__get_locks_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output) + dav_svn__output *output) { apr_bucket_brigade *bb; svn_error_t *err; dav_error *derr = NULL; - apr_status_t apr_err; apr_hash_t *locks; dav_svn__authz_read_baton arb; svn_depth_t depth = svn_depth_unknown; @@ -229,10 +211,12 @@ dav_svn__get_locks_report(const dav_resource *resource, return dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR, err->message, resource->pool); - bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); + bb = apr_brigade_create(resource->pool, + dav_svn__output_get_bucket_alloc(output)); - if ((apr_err = send_get_lock_response(locks, output, bb, resource->pool))) - derr = dav_svn__convert_err(svn_error_create(apr_err, 0, NULL), + err = send_get_lock_response(locks, output, bb, resource->pool); + if (err) + derr = dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR, "Error writing REPORT response.", resource->pool); diff --git a/subversion/mod_dav_svn/reports/inherited-props.c b/subversion/mod_dav_svn/reports/inherited-props.c index ce0f4de..bb2300f 100644 --- a/subversion/mod_dav_svn/reports/inherited-props.c +++ b/subversion/mod_dav_svn/reports/inherited-props.c @@ -47,7 +47,7 @@ dav_error * dav_svn__get_inherited_props_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output) + dav_svn__output *output) { svn_error_t *serr; dav_error *derr = NULL; @@ -69,12 +69,10 @@ dav_svn__get_inherited_props_report(const dav_resource *resource, ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE); if (ns == -1) { - return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0, + return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0, "The request does not contain the 'svn:' " "namespace, so it is not going to have " - "certain required elements.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "certain required elements"); } iterpool = svn_pool_create(resource->pool); @@ -107,7 +105,8 @@ dav_svn__get_inherited_props_report(const dav_resource *resource, arb.repos = resource->info->repos; /* Build inherited property brigade */ - bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); + bb = apr_brigade_create(resource->pool, + dav_svn__output_get_bucket_alloc(output)); serr = svn_fs_revision_root(&root, resource->info->repos->fs, rev, resource->pool); @@ -121,7 +120,7 @@ dav_svn__get_inherited_props_report(const dav_resource *resource, &arb, resource->pool, iterpool); if (serr) { - derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, serr->message, + derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, NULL, resource->pool); goto cleanup; } @@ -133,7 +132,7 @@ dav_svn__get_inherited_props_report(const dav_resource *resource, "xmlns:D=\"DAV:\">" DEBUG_CR); if (serr) { - derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, serr->message, + derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, NULL, resource->pool); goto cleanup; } @@ -161,8 +160,8 @@ dav_svn__get_inherited_props_report(const dav_resource *resource, hi; hi = apr_hash_next(hi)) { - const char *propname = svn__apr_hash_index_key(hi); - svn_string_t *propval = svn__apr_hash_index_val(hi); + const char *propname = apr_hash_this_key(hi); + svn_string_t *propval = apr_hash_this_val(hi); const char *xml_safe; serr = dav_svn__brigade_printf( diff --git a/subversion/mod_dav_svn/reports/log.c b/subversion/mod_dav_svn/reports/log.c index acd33ed..76a47c8 100644 --- a/subversion/mod_dav_svn/reports/log.c +++ b/subversion/mod_dav_svn/reports/log.c @@ -50,7 +50,7 @@ struct log_receiver_baton apr_bucket_brigade *bb; /* where to deliver the output */ - ap_filter_t *output; + dav_svn__output *output; /* Whether we've written the <S:log-report> header. Allows for lazy writes to support mod_dav-based error handling. */ @@ -65,6 +65,10 @@ struct log_receiver_baton /* whether the client can handle encoded binary property values */ svn_boolean_t encode_binary_props; + + /* Helper variables to force early bucket brigade flushes */ + int result_count; + int next_forced_flush; }; @@ -87,6 +91,43 @@ maybe_send_header(struct log_receiver_baton *lrb) return SVN_NO_ERROR; } +/* Utility for log_receiver opening a new XML element in LRB's brigade + for LOG_ITEM and return the element's name in *ELEMENT. Use POOL for + temporary allocations. + + Call this function for items that may have a copy-from */ +static svn_error_t * +start_path_with_copy_from(const char **element, + struct log_receiver_baton *lrb, + svn_log_changed_path2_t *log_item, + apr_pool_t *pool) +{ + switch (log_item->action) + { + case 'A': *element = "S:added-path"; + break; + case 'R': *element = "S:replaced-path"; + break; + default: /* Caller, you did wrong! */ + SVN_ERR_MALFUNCTION(); + } + + if (log_item->copyfrom_path + && SVN_IS_VALID_REVNUM(log_item->copyfrom_rev)) + SVN_ERR(dav_svn__brigade_printf + (lrb->bb, lrb->output, + "<%s copyfrom-path=\"%s\" copyfrom-rev=\"%ld\"", + *element, + apr_xml_quote_string(pool, + log_item->copyfrom_path, + 1), /* escape quotes */ + log_item->copyfrom_rev)); + else + SVN_ERR(dav_svn__brigade_printf(lrb->bb, lrb->output, "<%s", *element)); + + return SVN_NO_ERROR; +} + /* This implements `svn_log_entry_receiver_t'. BATON is a `struct log_receiver_baton *'. */ @@ -203,39 +244,9 @@ log_receiver(void *baton, switch (log_item->action) { case 'A': - if (log_item->copyfrom_path - && SVN_IS_VALID_REVNUM(log_item->copyfrom_rev)) - SVN_ERR(dav_svn__brigade_printf - (lrb->bb, lrb->output, - "<S:added-path copyfrom-path=\"%s\"" - " copyfrom-rev=\"%ld\"", - apr_xml_quote_string(iterpool, - log_item->copyfrom_path, - 1), /* escape quotes */ - log_item->copyfrom_rev)); - else - SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, - "<S:added-path")); - - close_element = "S:added-path"; - break; - case 'R': - if (log_item->copyfrom_path - && SVN_IS_VALID_REVNUM(log_item->copyfrom_rev)) - SVN_ERR(dav_svn__brigade_printf - (lrb->bb, lrb->output, - "<S:replaced-path copyfrom-path=\"%s\"" - " copyfrom-rev=\"%ld\"", - apr_xml_quote_string(iterpool, - log_item->copyfrom_path, - 1), /* escape quotes */ - log_item->copyfrom_rev)); - else - SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, - "<S:replaced-path")); - - close_element = "S:replaced-path"; + SVN_ERR(start_path_with_copy_from(&close_element, lrb, + log_item, iterpool)); break; case 'D': @@ -275,6 +286,33 @@ log_receiver(void *baton, SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, "</S:log-item>" DEBUG_CR)); + /* In general APR will flush the brigade every 8000 bytes through the filter + stack, but log items may not be generated that fast, especially in + combination with authz and busy servers. We now explictly flush after + log-item 4, 16, 64 and 256 to produce a few results fast. + + This introduces 4 full flushes of our brigade and the installed output + filters at growing intervals and then falls back to the standard + buffering of 8000 bytes + whatever buffers are added in output filters. */ + lrb->result_count++; + if (lrb->result_count == lrb->next_forced_flush) + { + apr_bucket *bkt; + + /* Compared to using ap_filter_flush(), which we use in other place + this adds a flush frame before flushing the brigade, to make output + filters perform a flush as well */ + + /* No brigade empty check. We want output filters to flush anyway */ + bkt = apr_bucket_flush_create( + dav_svn__output_get_bucket_alloc(lrb->output)); + APR_BRIGADE_INSERT_TAIL(lrb->bb, bkt); + SVN_ERR(dav_svn__output_pass_brigade(lrb->output, lrb->bb)); + + if (lrb->result_count < 256) + lrb->next_forced_flush = lrb->next_forced_flush * 4; + } + return SVN_NO_ERROR; } @@ -282,7 +320,7 @@ log_receiver(void *baton, dav_error * dav_svn__log_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output) + dav_svn__output *output) { svn_error_t *serr; dav_error *derr = NULL; @@ -301,6 +339,7 @@ dav_svn__log_report(const dav_resource *resource, svn_boolean_t discover_changed_paths = FALSE; /* off by default */ svn_boolean_t strict_node_history = FALSE; /* off by default */ svn_boolean_t include_merged_revisions = FALSE; /* off by default */ + apr_array_header_t *revprops = apr_array_make(resource->pool, 3, sizeof(const char *)); apr_array_header_t *paths @@ -313,12 +352,10 @@ dav_svn__log_report(const dav_resource *resource, ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE); if (ns == -1) { - return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0, + return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0, "The request does not contain the 'svn:' " "namespace, so it is not going to have " - "certain required elements.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "certain required elements"); } /* If this is still FALSE after the loop, we haven't seen either of @@ -415,12 +452,15 @@ dav_svn__log_report(const dav_resource *resource, /* Build log receiver baton */ lrb.bb = apr_brigade_create(resource->pool, /* not the subpool! */ - output->c->bucket_alloc); + dav_svn__output_get_bucket_alloc(output)); lrb.output = output; lrb.needs_header = TRUE; lrb.stack_depth = 0; /* lrb.requested_custom_revprops set above */ + lrb.result_count = 0; + lrb.next_forced_flush = 4; + /* Our svn_log_entry_receiver_t sends the <S:log-report> header in a lazy fashion. Before writing the first log message, it assures that the header has already been sent (checking the needs_header @@ -443,7 +483,7 @@ dav_svn__log_report(const dav_resource *resource, resource->pool); if (serr) { - derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, serr->message, + derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, NULL, resource->pool); goto cleanup; } diff --git a/subversion/mod_dav_svn/reports/mergeinfo.c b/subversion/mod_dav_svn/reports/mergeinfo.c index 15c3071..107c7a1 100644 --- a/subversion/mod_dav_svn/reports/mergeinfo.c +++ b/subversion/mod_dav_svn/reports/mergeinfo.c @@ -46,7 +46,7 @@ dav_error * dav_svn__get_mergeinfo_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output) + dav_svn__output *output) { svn_error_t *serr; dav_error *derr = NULL; @@ -73,12 +73,10 @@ dav_svn__get_mergeinfo_report(const dav_resource *resource, ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE); if (ns == -1) { - return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0, + return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0, "The request does not contain the 'svn:' " "namespace, so it is not going to have " - "certain required elements.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "certain required elements"); } for (child = doc->root->first_child; child != NULL; child = child->next) @@ -126,7 +124,8 @@ dav_svn__get_mergeinfo_report(const dav_resource *resource, arb.repos = resource->info->repos; /* Build mergeinfo brigade */ - bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); + bb = apr_brigade_create(resource->pool, + dav_svn__output_get_bucket_alloc(output)); serr = svn_repos_fs_get_mergeinfo(&catalog, repos->repos, paths, rev, inherit, include_descendants, @@ -134,7 +133,7 @@ dav_svn__get_mergeinfo_report(const dav_resource *resource, &arb, resource->pool); if (serr) { - derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, serr->message, + derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, NULL, resource->pool); goto cleanup; } @@ -144,7 +143,7 @@ dav_svn__get_mergeinfo_report(const dav_resource *resource, resource->pool); if (serr) { - derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, serr->message, + derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, NULL, resource->pool); goto cleanup; } @@ -162,7 +161,7 @@ dav_svn__get_mergeinfo_report(const dav_resource *resource, "xmlns:D=\"DAV:\">" DEBUG_CR); if (serr) { - derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, serr->message, + derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, NULL, resource->pool); goto cleanup; } diff --git a/subversion/mod_dav_svn/reports/replay.c b/subversion/mod_dav_svn/reports/replay.c index 0d57f32..886dbba 100644 --- a/subversion/mod_dav_svn/reports/replay.c +++ b/subversion/mod_dav_svn/reports/replay.c @@ -44,7 +44,7 @@ typedef struct edit_baton_t { apr_bucket_brigade *bb; - ap_filter_t *output; + dav_svn__output *output; svn_boolean_t started; svn_boolean_t sending_textdelta; int compression_level; @@ -108,7 +108,7 @@ add_file_or_directory(const char *file_or_directory, SVN_ERR(maybe_close_textdelta(eb)); - *added_baton = (void *)eb; + *added_baton = eb; if (! copyfrom_path) SVN_ERR(dav_svn__brigade_printf(eb->bb, eb->output, @@ -135,7 +135,7 @@ open_file_or_directory(const char *file_or_directory, { const char *qname = apr_xml_quote_string(pool, path, 1); SVN_ERR(maybe_close_textdelta(eb)); - *opened_baton = (void *)eb; + *opened_baton = eb; return dav_svn__brigade_printf(eb->bb, eb->output, "<S:open-%s name=\"%s\" rev=\"%ld\"/>" DEBUG_CR, @@ -367,7 +367,7 @@ static void make_editor(const svn_delta_editor_t **editor, void **edit_baton, apr_bucket_brigade *bb, - ap_filter_t *output, + dav_svn__output *output, int compression_level, apr_pool_t *pool) { @@ -401,20 +401,19 @@ make_editor(const svn_delta_editor_t **editor, static dav_error * malformed_element_error(const char *tagname, apr_pool_t *pool) { - return dav_svn__new_error_tag(pool, HTTP_BAD_REQUEST, 0, + return dav_svn__new_error_svn(pool, HTTP_BAD_REQUEST, 0, apr_pstrcat(pool, "The request's '", tagname, "' element is malformed; there " "is a problem with the client.", - (char *)NULL), - SVN_DAV_ERROR_NAMESPACE, SVN_DAV_ERROR_TAG); + SVN_VA_NULL)); } dav_error * dav_svn__replay_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output) + dav_svn__output *output) { dav_error *derr = NULL; svn_revnum_t low_water_mark = SVN_INVALID_REVNUM; @@ -456,13 +455,11 @@ dav_svn__replay_report(const dav_resource *resource, ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE); if (ns == -1) - return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0, + return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0, "The request does not contain the 'svn:' " "namespace, so it is not going to have an " "svn:revision element. That element is " - "required.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "required"); for (child = doc->root->first_child; child != NULL; child = child->next) { @@ -521,21 +518,20 @@ dav_svn__replay_report(const dav_resource *resource, } if (! SVN_IS_VALID_REVNUM(rev)) - return dav_svn__new_error_tag + return dav_svn__new_error_svn (resource->pool, HTTP_BAD_REQUEST, 0, - "Request was missing the revision argument.", - SVN_DAV_ERROR_NAMESPACE, SVN_DAV_ERROR_TAG); + "Request was missing the revision argument"); if (! SVN_IS_VALID_REVNUM(low_water_mark)) - return dav_svn__new_error_tag + return dav_svn__new_error_svn (resource->pool, HTTP_BAD_REQUEST, 0, - "Request was missing the low-water-mark argument.", - SVN_DAV_ERROR_NAMESPACE, SVN_DAV_ERROR_TAG); + "Request was missing the low-water-mark argument"); if (! base_dir) base_dir = ""; - bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); + bb = apr_brigade_create(resource->pool, + dav_svn__output_get_bucket_alloc(output)); if ((err = svn_fs_revision_root(&root, resource->info->repos->fs, rev, resource->pool))) diff --git a/subversion/mod_dav_svn/reports/update.c b/subversion/mod_dav_svn/reports/update.c index 541d551..70be92e 100644 --- a/subversion/mod_dav_svn/reports/update.c +++ b/subversion/mod_dav_svn/reports/update.c @@ -68,7 +68,7 @@ typedef struct update_ctx_t { apr_bucket_brigade *bb; /* where to deliver the output */ - ap_filter_t *output; + dav_svn__output *output; /* where do these editor paths *really* point to? */ apr_hash_t *pathmap; @@ -127,10 +127,6 @@ typedef struct item_baton_t { /* File/dir copied? */ svn_boolean_t copyfrom; - /* Does the client need to fetch additional properties for this - item? */ - svn_boolean_t fetch_props; - /* Array of const char * names of removed properties. (Used only for copied files/dirs in skelta mode.) */ apr_array_header_t *removed_props; @@ -257,13 +253,13 @@ send_vsn_url(item_baton_t *baton, apr_pool_t *pool) { href = dav_svn__build_uri(baton->uc->resource->info->repos, DAV_SVN__BUILD_URI_REVROOT, - revision, path, 0 /* add_href */, pool); + revision, path, FALSE /* add_href */, pool); } else { href = dav_svn__build_uri(baton->uc->resource->info->repos, DAV_SVN__BUILD_URI_VERSION, - revision, path, 0 /* add_href */, pool); + revision, path, FALSE /* add_href */, pool); } return dav_svn__brigade_printf(baton->uc->bb, baton->uc->output, @@ -321,7 +317,6 @@ add_helper(svn_boolean_t is_dir, { item_baton_t *child; update_ctx_t *uc = parent->uc; - const char *bc_url = NULL; child = make_child_baton(parent, path, pool); child->added = TRUE; @@ -345,12 +340,14 @@ add_helper(svn_boolean_t is_dir, { /* we send baseline-collection urls when we add a directory */ svn_revnum_t revision; + const char *bc_url; + revision = dav_svn__get_safe_cr(child->uc->rev_root, real_path, pool); bc_url = dav_svn__build_uri(child->uc->resource->info->repos, DAV_SVN__BUILD_URI_BC, revision, real_path, - 0 /* add_href */, pool); + FALSE /* add_href */, pool); bc_url = svn_urlpath__canonicalize(bc_url, pool); /* ugh, build_uri ignores the path and just builds the root @@ -364,6 +361,8 @@ add_helper(svn_boolean_t is_dir, /* make sure that the BC_URL is xml attribute safe. */ bc_url = apr_xml_quote_string(pool, bc_url, 1); + + bc_url_str = apr_psprintf(pool, " bc-url=\"%s\"", bc_url); } else { @@ -377,9 +376,6 @@ add_helper(svn_boolean_t is_dir, svn_checksum_to_cstring(sha1_checksum, pool)); } - if (bc_url) - bc_url_str = apr_psprintf(pool, " bc-url=\"%s\"", bc_url); - if (copyfrom_path == NULL) { elt = apr_psprintf(pool, @@ -466,12 +462,6 @@ close_helper(svn_boolean_t is_dir, item_baton_t *baton, apr_pool_t *pool) } } - /* If our client need to fetch properties, let it know. */ - if (baton->fetch_props) - SVN_ERR(dav_svn__brigade_printf(baton->uc->bb, baton->uc->output, - "<S:fetch-props/>" DEBUG_CR)); - - /* Let's tie it off, nurse. */ if (baton->added) SVN_ERR(dav_svn__brigade_printf(baton->uc->bb, baton->uc->output, @@ -913,9 +903,8 @@ malformed_element_error(const char *tagname, apr_pool_t *pool) const char *errstr = apr_pstrcat(pool, "The request's '", tagname, "' element is malformed; there " "is a problem with the client.", - (char *)NULL); - return dav_svn__new_error_tag(pool, HTTP_BAD_REQUEST, 0, errstr, - SVN_DAV_ERROR_NAMESPACE, SVN_DAV_ERROR_TAG); + SVN_VA_NULL); + return dav_svn__new_error_svn(pool, HTTP_BAD_REQUEST, 0, errstr); } @@ -934,7 +923,7 @@ validate_input_revision(svn_revnum_t revision, const dav_resource *resource) { if (! SVN_IS_VALID_REVNUM(revision)) - return SVN_NO_ERROR; + return NULL; if (revision > youngest) { @@ -958,14 +947,14 @@ validate_input_revision(svn_revnum_t revision, "Invalid revision found in update report " "request.", resource->pool); } - return SVN_NO_ERROR; + return NULL; } dav_error * dav_svn__update_report(const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output) + dav_svn__output *output) { svn_delta_editor_t *editor; apr_xml_elem *child; @@ -999,22 +988,18 @@ dav_svn__update_report(const dav_resource *resource, if ((resource->info->restype != DAV_SVN_RESTYPE_VCC) && (resource->info->restype != DAV_SVN_RESTYPE_ME)) - return dav_svn__new_error_tag(resource->pool, HTTP_CONFLICT, 0, + return dav_svn__new_error_svn(resource->pool, HTTP_CONFLICT, 0, "This report can only be run against " - "a VCC or root-stub URI.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "a VCC or root-stub URI"); ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE); if (ns == -1) { - return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0, + return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0, "The request does not contain the 'svn:' " "namespace, so it is not going to have an " "svn:target-revision element. That element " - "is required.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "is required"); } /* SVNAllowBulkUpdates On/Prefer: server configuration permits bulk updates @@ -1152,6 +1137,11 @@ dav_svn__update_report(const dav_resource *resource, } if (child->ns == ns && strcmp(child->name, "resource-walk") == 0) { + /* This flag is not used since Subversion 1.1.x + There are some remains in libsvn_ra_neon, where it can + be enabled via a static function flag. + Disabled since r852220 (aka r12146) + "Prefer correctness over efficiency." */ cdata = dav_xml_get_cdata(child, resource->pool, 1); if (! *cdata) return malformed_element_error(child->name, resource->pool); @@ -1200,12 +1190,10 @@ dav_svn__update_report(const dav_resource *resource, sending a style of report that we no longer allow. */ if (! src_path) { - return dav_svn__new_error_tag + return dav_svn__new_error_svn (resource->pool, HTTP_BAD_REQUEST, 0, "The request did not contain the '<src-path>' element.\n" - "This may indicate that your client is too old.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "This may indicate that your client is too old"); } uc.svndiff_version = resource->info->svndiff_version; @@ -1214,7 +1202,8 @@ dav_svn__update_report(const dav_resource *resource, uc.output = output; uc.anchor = src_path; uc.target = target; - uc.bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); + uc.bb = apr_brigade_create(resource->pool, + dav_svn__output_get_bucket_alloc(output)); uc.pathmap = NULL; uc.enable_v2_response = ((resource->info->restype == DAV_SVN_RESTYPE_ME) && (resource->info->repos->v2_protocol)); diff --git a/subversion/mod_dav_svn/repos.c b/subversion/mod_dav_svn/repos.c index 590cca9..9bf5ea0 100644 --- a/subversion/mod_dav_svn/repos.c +++ b/subversion/mod_dav_svn/repos.c @@ -56,6 +56,7 @@ #include "private/svn_log.h" #include "private/svn_fspath.h" #include "private/svn_repos_private.h" +#include "private/svn_sorts_private.h" #include "dav_svn.h" @@ -899,7 +900,7 @@ prep_version(dav_resource_combined *comb) comb->res.uri = dav_svn__build_uri(comb->priv.repos, DAV_SVN__BUILD_URI_BASELINE, comb->priv.root.rev, NULL, - 0 /* add_href */, + FALSE /* add_href */, pool); return NULL; @@ -1034,6 +1035,28 @@ prep_working(dav_resource_combined *comb) comb->res.exists = (kind != svn_node_none); comb->res.collection = (kind == svn_node_dir); + if (comb->res.exists + && comb->priv.r->method_number == M_MKCOL + && comb->priv.repos->is_svn_client) + { + /* mod_dav will now continue returning a generic HTTP_METHOD_NOT_ALLOWED + error, which doesn't produce nice output on SVN, nor gives any details + on why the operation failed. + + Let's error out a bit earlier and produce an error message that is + easier to understand for both clients and users. */ + + /* It would be nice if we could error out a bit later (see issue #2295), + like in create_collection(), but mod_dav outsmarts us by just + returning the error when the node exists. */ + + return dav_svn__convert_err( + svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL, + "Path already exists, path '%s'", + comb->priv.repos_path), + HTTP_METHOD_NOT_ALLOWED, NULL, pool); + } + return NULL; } @@ -1168,7 +1191,7 @@ create_private_resource(const dav_resource *base, if (base->info->repos->root_path[1]) comb->res.uri = apr_pstrcat(base->pool, base->info->repos->root_path, - path->data, (char *)NULL); + path->data, SVN_VA_NULL); else comb->res.uri = path->data; comb->res.info = &comb->priv; @@ -1208,14 +1231,15 @@ static void log_warning(void *baton, svn_error_t *err) AP_MODULE_DECLARE(dav_error *) -dav_svn_split_uri(request_rec *r, - const char *uri_to_split, - const char *root_path, - const char **cleaned_uri, - int *trailing_slash, - const char **repos_basename, - const char **relative_path, - const char **repos_path) +dav_svn_split_uri2(request_rec *r, + const char *uri_to_split, + const char *root_path, + const char **cleaned_uri, + int *trailing_slash, + const char **repos_basename, + const char **relative_path, + const char **repos_path, + apr_pool_t *pool) { apr_size_t len1; int had_slash; @@ -1231,7 +1255,7 @@ dav_svn_split_uri(request_rec *r, if ((fs_path == NULL) && (fs_parent_path == NULL)) { /* ### are SVN_ERR_APMOD codes within the right numeric space? */ - return dav_svn__new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, + return dav_svn__new_error(pool, HTTP_INTERNAL_SERVER_ERROR, SVN_ERR_APMOD_MISSING_PATH_TO_FS, "The server is misconfigured: " "either an SVNPath or SVNParentPath " @@ -1240,7 +1264,7 @@ dav_svn_split_uri(request_rec *r, } /* make a copy so that we can do some work on it */ - uri = apr_pstrdup(r->pool, uri_to_split); + uri = apr_pstrdup(pool, uri_to_split); /* remove duplicate slashes, and make sure URI has no trailing '/' */ ap_no2slash(uri); @@ -1255,7 +1279,7 @@ dav_svn_split_uri(request_rec *r, *trailing_slash = FALSE; /* return the first item. */ - *cleaned_uri = apr_pstrdup(r->pool, uri); + *cleaned_uri = apr_pstrdup(pool, uri); /* The URL space defined by the SVN provider is always a virtual space. Construct the path relative to the configured Location @@ -1296,7 +1320,7 @@ dav_svn_split_uri(request_rec *r, if (fs_path != NULL) { /* the repos_basename is the last component of root_path. */ - *repos_basename = svn_dirent_basename(root_path, r->pool); + *repos_basename = svn_dirent_basename(root_path, pool); /* 'relative' is already correct for SVNPath; the root_path already contains the name of the repository, so relative is @@ -1314,7 +1338,7 @@ dav_svn_split_uri(request_rec *r, if (relative[1] == '\0') { /* ### are SVN_ERR_APMOD codes within the right numeric space? */ - return dav_svn__new_error(r->pool, HTTP_FORBIDDEN, + return dav_svn__new_error(pool, HTTP_FORBIDDEN, SVN_ERR_APMOD_MALFORMED_URI, "The URI does not contain the name " "of a repository."); @@ -1331,7 +1355,7 @@ dav_svn_split_uri(request_rec *r, } else { - magic_component = apr_pstrndup(r->pool, relative + 1, + magic_component = apr_pstrndup(pool, relative + 1, magic_end - relative - 1); relative = magic_end; } @@ -1341,7 +1365,7 @@ dav_svn_split_uri(request_rec *r, } /* We can return 'relative' at this point too. */ - *relative_path = apr_pstrdup(r->pool, relative); + *relative_path = apr_pstrdup(pool, relative); /* Code to remove the !svn junk from the front of the relative path, mainly stolen from parse_uri(). This code assumes that @@ -1362,7 +1386,7 @@ dav_svn_split_uri(request_rec *r, if (ch == '\0') { /* relative is just "!svn", which is malformed. */ - return dav_svn__new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, + return dav_svn__new_error(pool, HTTP_NOT_FOUND, SVN_ERR_APMOD_MALFORMED_URI, "Nothing follows the svn special_uri."); } @@ -1389,7 +1413,7 @@ dav_svn_split_uri(request_rec *r, *repos_path = NULL; else return dav_svn__new_error( - r->pool, HTTP_INTERNAL_SERVER_ERROR, + pool, HTTP_NOT_FOUND, SVN_ERR_APMOD_MALFORMED_URI, "Missing info after special_uri."); } @@ -1413,7 +1437,7 @@ dav_svn_split_uri(request_rec *r, /* Did we break from the loop prematurely? */ if (j != (defn->numcomponents - 1)) return dav_svn__new_error( - r->pool, HTTP_INTERNAL_SERVER_ERROR, + pool, HTTP_NOT_FOUND, SVN_ERR_APMOD_MALFORMED_URI, "Not enough components after " "special_uri."); @@ -1427,13 +1451,13 @@ dav_svn_split_uri(request_rec *r, else { /* Found a slash after the special components. */ - *repos_path = apr_pstrdup(r->pool, start); + *repos_path = apr_pstrdup(pool, start - 1); } } else { return - dav_svn__new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, + dav_svn__new_error(pool, HTTP_NOT_FOUND, SVN_ERR_APMOD_MALFORMED_URI, "Unknown data after special_uri."); } @@ -1444,7 +1468,7 @@ dav_svn_split_uri(request_rec *r, if (defn->name == NULL) return - dav_svn__new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, + dav_svn__new_error(pool, HTTP_NOT_FOUND, SVN_ERR_APMOD_MALFORMED_URI, "Couldn't match subdir after special_uri."); } @@ -1453,13 +1477,27 @@ dav_svn_split_uri(request_rec *r, { /* There's no "!svn/" at all, so the relative path is already a valid path within the repository. */ - *repos_path = apr_pstrdup(r->pool, relative); + *repos_path = apr_pstrdup(pool, relative - 1); } } return NULL; } +AP_MODULE_DECLARE(dav_error *) +dav_svn_split_uri(request_rec *r, + const char *uri_to_split, + const char *root_path, + const char **cleaned_uri, + int *trailing_slash, + const char **repos_basename, + const char **relative_path, + const char **repos_path) +{ + return dav_svn_split_uri2(r, uri_to_split, root_path, cleaned_uri, + trailing_slash, repos_basename, relative_path, + repos_path, r->pool); +} /* Context for cleanup handler. */ struct cleanup_fs_access_baton @@ -1530,7 +1568,7 @@ get_parentpath_resource(request_rec *r, if (r->uri[len-1] != '/') { new_uri = apr_pstrcat(r->pool, ap_escape_uri(r->pool, r->uri), - "/", (char *)NULL); + "/", SVN_VA_NULL); apr_table_setn(r->headers_out, "Location", ap_construct_url(r->pool, new_uri, r)); return dav_svn__new_error(r->pool, HTTP_MOVED_PERMANENTLY, 0, @@ -1820,9 +1858,24 @@ do_out_of_date_check(dav_resource_combined *comb, request_rec *r) "Attempting to modify out-of-date resource.", r->pool); } + else if (comb->priv.version_name > created_rev) + { + svn_revnum_t txn_base_rev; + + txn_base_rev = svn_fs_txn_base_revision(comb->res.info->root.txn); + if (comb->priv.version_name > txn_base_rev) + { + serr = svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, + "No such revision %ld", + comb->priv.version_name); + + return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, + "Unknown base revision", + r->pool); + } + } } - else if (SVN_IS_VALID_REVNUM(comb->priv.version_name) - && comb->res.collection) + else if (comb->res.collection) { /* Issue #4480: With HTTPv2 we can receive the first change for a directory after it has been made mutable, because one of its @@ -1837,7 +1890,7 @@ do_out_of_date_check(dav_resource_combined *comb, request_rec *r) properties changed since the BASE version. ### I think svn_fs_node_relation() checks for more changes than we - should check for here. Needs further review. But it looks like\ + should check for here. Needs further review. But it looks like this check matches the checks in the libsvn_fs commit editor. For now I would say reporting out of date in a few too many @@ -1846,8 +1899,7 @@ do_out_of_date_check(dav_resource_combined *comb, request_rec *r) svn_revnum_t txn_base_rev; svn_fs_root_t *txn_base_root; svn_fs_root_t *rev_root; - const svn_fs_id_t *txn_base_id; - const svn_fs_id_t *rev_id; + svn_fs_node_relation_t node_relation; txn_base_rev = svn_fs_txn_base_revision(comb->res.info->root.txn); @@ -1856,15 +1908,11 @@ do_out_of_date_check(dav_resource_combined *comb, request_rec *r) serr = svn_fs_revision_root(&txn_base_root, comb->res.info->repos->fs, txn_base_rev, r->pool); - - if (!serr) - serr = svn_fs_node_id(&txn_base_id, txn_base_root, - comb->priv.repos_path, r->pool); if (serr != NULL) { return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, - "Could not open youngest revision root " + "Could not open the transaction revision " "for verification against the base " "revision", r->pool); } @@ -1872,22 +1920,35 @@ do_out_of_date_check(dav_resource_combined *comb, request_rec *r) serr = svn_fs_revision_root(&rev_root, comb->res.info->repos->fs, comb->priv.version_name, r->pool); - if (!serr) - serr = svn_fs_node_id(&rev_id, rev_root, - comb->priv.repos_path, r->pool); - if (serr != NULL) { + svn_fs_close_root(txn_base_root); return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, - "Could not open the base revision" - "for verification against the youngest " - "revision", r->pool); + "Could not open the base revision " + "for verification against the " + "transaction revision", r->pool); } + serr = svn_fs_node_relation(&node_relation, rev_root, + comb->priv.repos_path, + txn_base_root, + comb->priv.repos_path, + r->pool); + svn_fs_close_root(rev_root); svn_fs_close_root(txn_base_root); - if (0 != svn_fs_compare_ids(txn_base_id, rev_id)) + if (serr != NULL) + { + /* ### correct HTTP error? */ + return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, + "Unable to fetch the node revision id " + "of the version resource within the " + "revision", + r->pool); + } + + if (node_relation != svn_fs_node_unchanged) { serr = svn_error_createf(SVN_ERR_RA_OUT_OF_DATE, NULL, "Directory '%s' is out of date", @@ -2065,6 +2126,16 @@ get_resource(request_rec *r, xslt_uri = dav_svn__get_xslt_uri(r); fs_parent_path = dav_svn__get_fs_parent_path(r); + if (r->method_number == M_COPY) + { + /* Workaround for issue #4531: Avoid a depth-infinity walk on + the copy source by overriding the Depth header here. + mod_dav defaults to infinite depth if this header is not set + which makes copies O(size of source) rather than the desired O(1). + ### Should be fixed by an explicit provider API feature in mod_dav. */ + apr_table_setn(r->headers_in, "Depth", "0"); + } + /* Special case: detect and build the SVNParentPath as a unique type of private resource, iff the SVNListParentPath directive is 'on'. */ if (dav_svn__is_parentpath_list(r)) @@ -2261,7 +2332,7 @@ get_resource(request_rec *r, } /* Retrieve/cache open repository */ - repos_key = apr_pstrcat(r->pool, "mod_dav_svn:", fs_path, (char *)NULL); + repos_key = apr_pstrcat(r->pool, "mod_dav_svn:", fs_path, SVN_VA_NULL); apr_pool_userdata_get(&userdata, repos_key, r->connection->pool); repos->repos = userdata; if (repos->repos == NULL) @@ -2275,7 +2346,9 @@ get_resource(request_rec *r, svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS, dav_svn__get_fulltext_cache_flag(r) ? "1" :"0"); svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, - dav_svn__get_revprop_cache_flag(r) ? "1" :"0"); + dav_svn__get_revprop_cache_flag(r) ? "2" :"0"); + svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_BLOCK_READ, + dav_svn__get_block_read_flag(r) ? "1" :"0"); /* Disallow BDB/event until issue 4157 is fixed. */ if (!strcmp(ap_show_mpm(), "event")) @@ -2298,8 +2371,8 @@ get_resource(request_rec *r, /* open the FS */ if (!serr) - serr = svn_repos_open2(&(repos->repos), fs_path, fs_config, - r->connection->pool); + serr = svn_repos_open3(&(repos->repos), fs_path, fs_config, + r->connection->pool, r->pool); if (serr != NULL) { /* The error returned by svn_repos_open2 might contain the @@ -2307,9 +2380,16 @@ get_resource(request_rec *r, leak that path back to the client, because that would be a security risk, but we do want to log the real error on the server side. */ - return dav_svn__sanitize_error(serr, "Could not open the requested " - "SVN filesystem", - HTTP_INTERNAL_SERVER_ERROR, r); + + apr_status_t cause = svn_error_root_cause(serr)->apr_err; + if (APR_STATUS_IS_ENOENT(cause) || APR_STATUS_IS_ENOTDIR(cause)) + return dav_svn__sanitize_error( + serr, "Could not find the requested SVN filesystem", + HTTP_NOT_FOUND, r); + else + return dav_svn__sanitize_error( + serr, "Could not open the requested SVN filesystem", + HTTP_INTERNAL_SERVER_ERROR, r); } /* Cache the open repos for the next request on this connection */ @@ -2467,7 +2547,7 @@ get_resource(request_rec *r, "/", r->args ? "?" : "", r->args ? r->args : "", - (char *)NULL); + SVN_VA_NULL); apr_table_setn(r->headers_out, "Location", ap_construct_url(r->pool, new_path, r)); return dav_svn__new_error(r->pool, HTTP_MOVED_PERMANENTLY, 0, @@ -3161,8 +3241,8 @@ set_headers(request_rec *r, const dav_resource *resource) typedef struct diff_ctx_t { - ap_filter_t *output; - apr_pool_t *pool; + dav_svn__output *output; + apr_bucket_brigade *bb; } diff_ctx_t; @@ -3170,18 +3250,9 @@ static svn_error_t * __attribute__((warn_unused_result)) write_to_filter(void *baton, const char *buffer, apr_size_t *len) { diff_ctx_t *dc = baton; - apr_bucket_brigade *bb; - apr_bucket *bkt; - apr_status_t status; /* take the current data and shove it into the filter */ - bb = apr_brigade_create(dc->pool, dc->output->c->bucket_alloc); - bkt = apr_bucket_transient_create(buffer, *len, dc->output->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb, bkt); - if ((status = ap_pass_brigade(dc->output, bb)) != APR_SUCCESS) { - return svn_error_create(status, NULL, - "Could not write data to filter"); - } + SVN_ERR(dav_svn__brigade_write(dc->bb, dc->output, buffer, *len)); return SVN_NO_ERROR; } @@ -3191,28 +3262,270 @@ static svn_error_t * __attribute__((warn_unused_result)) close_filter(void *baton) { diff_ctx_t *dc = baton; - apr_bucket_brigade *bb; apr_bucket *bkt; - apr_status_t status; /* done with the file. write an EOS bucket now. */ - bb = apr_brigade_create(dc->pool, dc->output->c->bucket_alloc); - bkt = apr_bucket_eos_create(dc->output->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb, bkt); - if ((status = ap_pass_brigade(dc->output, bb)) != APR_SUCCESS) - return svn_error_create(status, NULL, "Could not write EOS to filter"); + bkt = apr_bucket_eos_create(dav_svn__output_get_bucket_alloc(dc->output)); + APR_BRIGADE_INSERT_TAIL(dc->bb, bkt); + SVN_ERR(dav_svn__output_pass_brigade(dc->output, dc->bb)); + + return SVN_NO_ERROR; +} + + +static svn_error_t * +emit_collection_head(const dav_resource *resource, + apr_bucket_brigade *bb, + dav_svn__output *output, + svn_boolean_t gen_html, + apr_pool_t *pool) +{ + /* XML schema for the directory index if xslt_uri is set: + + <?xml version="1.0"?> + <?xml-stylesheet type="text/xsl" href="[info->repos->xslt_uri]"?> */ + static const char xml_index_dtd[] = + "<!DOCTYPE svn [\n" + " <!ELEMENT svn (index)>\n" + " <!ATTLIST svn version CDATA #REQUIRED\n" + " href CDATA #REQUIRED>\n" + " <!ELEMENT index (updir?, (file | dir)*)>\n" + " <!ATTLIST index name CDATA #IMPLIED\n" + " path CDATA #IMPLIED\n" + " rev CDATA #IMPLIED\n" + " base CDATA #IMPLIED>\n" + " <!ELEMENT updir EMPTY>\n" + " <!ATTLIST updir href CDATA #REQUIRED>\n" + " <!ELEMENT file EMPTY>\n" + " <!ATTLIST file name CDATA #REQUIRED\n" + " href CDATA #REQUIRED>\n" + " <!ELEMENT dir EMPTY>\n" + " <!ATTLIST dir name CDATA #REQUIRED\n" + " href CDATA #REQUIRED>\n" + "]>\n"; + + if (gen_html) + { + const char *title; + if (resource->info->repos_path == NULL) + title = "unknown location"; + else + title = resource->info->repos_path; + + if (resource->info->restype != DAV_SVN_RESTYPE_PARENTPATH_COLLECTION) + { + if (SVN_IS_VALID_REVNUM(resource->info->root.rev)) + title = apr_psprintf(pool, + "Revision %ld: %s", + resource->info->root.rev, title); + if (resource->info->repos->repo_basename) + title = apr_psprintf(pool, "%s - %s", + resource->info->repos->repo_basename, + title); + if (resource->info->repos->repo_name) + title = apr_psprintf(pool, "%s: %s", + resource->info->repos->repo_name, + title); + } + + SVN_ERR(dav_svn__brigade_printf(bb, output, + "<html><head><title>%s</title></head>\n" + "<body>\n <h2>%s</h2>\n <ul>\n", + title, title)); + } + else + { + const char *name = resource->info->repos->repo_name; + const char *href = resource->info->repos_path; + const char *base = resource->info->repos->repo_basename; + + SVN_ERR(dav_svn__brigade_puts(bb, output, "<?xml version=\"1.0\"?>\n")); + SVN_ERR(dav_svn__brigade_printf(bb, output, + "<?xml-stylesheet type=\"text/xsl\" " + "href=\"%s\"?>\n", + resource->info->repos->xslt_uri)); + SVN_ERR(dav_svn__brigade_puts(bb, output, xml_index_dtd)); + SVN_ERR(dav_svn__brigade_puts(bb, output, + "<svn version=\"" SVN_VERSION "\"\n" + " href=\"http://subversion.apache.org/\">\n")); + SVN_ERR(dav_svn__brigade_puts(bb, output, " <index")); + + if (name) + SVN_ERR(dav_svn__brigade_printf(bb, output, + " name=\"%s\"", + apr_xml_quote_string(resource->pool, + name, 1))); + if (SVN_IS_VALID_REVNUM(resource->info->root.rev)) + SVN_ERR(dav_svn__brigade_printf(bb, output, " rev=\"%ld\"", + resource->info->root.rev)); + if (href) + SVN_ERR(dav_svn__brigade_printf(bb, output, " path=\"%s\"", + apr_xml_quote_string(resource->pool, + href, 1))); + if (base) + SVN_ERR(dav_svn__brigade_printf(bb, output, " base=\"%s\"", base)); + + SVN_ERR(dav_svn__brigade_puts(bb, output, ">\n")); + } + + if ((resource->info->restype != DAV_SVN_RESTYPE_PARENTPATH_COLLECTION) + && resource->info->repos_path + && ((resource->info->repos_path[1] != '\0') + || dav_svn__get_list_parentpath_flag(resource->info->r))) + { + const char *href; + if (resource->info->pegged) + { + href = apr_psprintf(pool, "../?p=%ld", resource->info->root.rev); + } + else + { + href = "../"; + } + + if (gen_html) + { + SVN_ERR(dav_svn__brigade_printf(bb, output, + " <li><a href=\"%s\">..</a></li>\n", + href)); + } + else + { + SVN_ERR(dav_svn__brigade_printf(bb, output, + " <updir href=\"%s\"/>\n", + href)); + } + } + + return SVN_NO_ERROR; +} + + +static svn_error_t * +emit_collection_entry(const dav_resource *resource, + apr_bucket_brigade *bb, + dav_svn__output *output, + const svn_fs_dirent_t *entry, + svn_boolean_t gen_html, + apr_pool_t *pool) +{ + const char *name = entry->name; + const char *href = name; + svn_boolean_t is_dir = (entry->kind == svn_node_dir); + + /* append a trailing slash onto the name for directories. we NEED + this for the href portion so that the relative reference will + descend properly. for the visible portion, it is just nice. */ + /* ### The xml output doesn't like to see a trailing slash on + ### the visible portion, so avoid that. */ + if (is_dir) + href = apr_pstrcat(pool, href, "/", SVN_VA_NULL); + + if (gen_html) + name = href; + + /* We quote special characters in both XML and HTML. */ + name = apr_xml_quote_string(pool, name, !gen_html); + + /* According to httpd-2.0.54/include/httpd.h, ap_os_escape_path() + behaves differently on different platforms. It claims to + "convert an OS path to a URL in an OS dependant way". + Nevertheless, there appears to be only one implementation + of the function in httpd, and the code seems completely + platform independent, so we'll assume it's appropriate for + mod_dav_svn to use it to quote outbound paths. */ + href = ap_os_escape_path(pool, href, 0); + href = apr_xml_quote_string(pool, href, 1); + + if (gen_html) + { + /* If our directory was access using the public peg-rev + CGI query interface, we'll let its dirents carry that + peg-rev, too. */ + if (resource->info->pegged) + { + SVN_ERR(dav_svn__brigade_printf(bb, output, + " <li><a href=\"%s?p=%ld\">%s</a></li>\n", + href, resource->info->root.rev, name)); + } + else + { + SVN_ERR(dav_svn__brigade_printf(bb, output, + " <li><a href=\"%s\">%s</a></li>\n", + href, name)); + } + } + else + { + const char *const tag = (is_dir ? "dir" : "file"); + + /* This is where we could search for props */ + + /* If our directory was access using the public peg-rev + CGI query interface, we'll let its dirents carry that + peg-rev, too. */ + if (resource->info->pegged) + { + SVN_ERR(dav_svn__brigade_printf(bb, output, + " <%s name=\"%s\" href=\"%s?p=%ld\" />\n", + tag, name, href, resource->info->root.rev)); + } + else + { + SVN_ERR(dav_svn__brigade_printf(bb, output, + " <%s name=\"%s\" href=\"%s\" />\n", + tag, name, href)); + } + } + + return SVN_NO_ERROR; +} + + +static svn_error_t * +emit_collection_tail(const dav_resource *resource, + apr_bucket_brigade *bb, + dav_svn__output *output, + svn_boolean_t gen_html, + apr_pool_t *pool) +{ + if (gen_html) + { + if (strcmp(ap_psignature("FOO", resource->info->r), "") != 0) + { + /* Apache's signature generation code didn't eat our prefix. + ServerSignature must be enabled. Print our version info. + + WARNING: This is a kludge!! ap_psignature() doesn't promise + to return the empty string when ServerSignature is off. We + know it does by code inspection, but this behavior is subject + to change. (Perhaps we should try to get the Apache folks to + make this promise, though. Seems harmless/useful enough...) + */ + SVN_ERR(dav_svn__brigade_puts(bb, output, + " </ul>\n <hr noshade><em>Powered by " + "<a href=\"http://subversion.apache.org/\">" + "Apache Subversion" + "</a> version " SVN_VERSION "." + "</em>\n</body></html>")); + } + else + SVN_ERR(dav_svn__brigade_puts(bb, output, " </ul>\n</body></html>")); + } + else + SVN_ERR(dav_svn__brigade_puts(bb, output, " </index>\n</svn>\n")); return SVN_NO_ERROR; } static dav_error * -deliver(const dav_resource *resource, ap_filter_t *output) +deliver(const dav_resource *resource, ap_filter_t *unused) { svn_error_t *serr; apr_bucket_brigade *bb; apr_bucket *bkt; - apr_status_t status; + dav_svn__output *output; /* Check resource type */ if (resource->baselined @@ -3225,39 +3538,17 @@ deliver(const dav_resource *resource, ap_filter_t *output) "Cannot GET this type of resource."); } + output = dav_svn__output_create(resource->info->r, resource->pool); + if (resource->collection) { const int gen_html = !resource->info->repos->xslt_uri; apr_hash_t *entries; - apr_pool_t *entry_pool; + apr_pool_t *iterpool; apr_array_header_t *sorted; svn_revnum_t dir_rev = SVN_INVALID_REVNUM; int i; - /* XML schema for the directory index if xslt_uri is set: - - <?xml version="1.0"?> - <?xml-stylesheet type="text/xsl" href="[info->repos->xslt_uri]"?> */ - static const char xml_index_dtd[] = - "<!DOCTYPE svn [\n" - " <!ELEMENT svn (index)>\n" - " <!ATTLIST svn version CDATA #REQUIRED\n" - " href CDATA #REQUIRED>\n" - " <!ELEMENT index (updir?, (file | dir)*)>\n" - " <!ATTLIST index name CDATA #IMPLIED\n" - " path CDATA #IMPLIED\n" - " rev CDATA #IMPLIED\n" - " base CDATA #IMPLIED>\n" - " <!ELEMENT updir EMPTY>\n" - " <!ATTLIST updir href CDATA #REQUIRED>\n" - " <!ELEMENT file EMPTY>\n" - " <!ATTLIST file name CDATA #REQUIRED\n" - " href CDATA #REQUIRED>\n" - " <!ELEMENT dir EMPTY>\n" - " <!ATTLIST dir name CDATA #REQUIRED\n" - " href CDATA #REQUIRED>\n" - "]>\n"; - /* <svn version="1.3.0 (dev-build)" href="http://subversion.apache.org"> <index name="[info->repos->repo_name]" @@ -3338,99 +3629,21 @@ deliver(const dav_resource *resource, ap_filter_t *output) resource->pool); } - bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); - - if (gen_html) - { - const char *title; - if (resource->info->repos_path == NULL) - title = "unknown location"; - else - title = resource->info->repos_path; - - if (resource->info->restype != DAV_SVN_RESTYPE_PARENTPATH_COLLECTION) - { - if (SVN_IS_VALID_REVNUM(resource->info->root.rev)) - title = apr_psprintf(resource->pool, - "Revision %ld: %s", - resource->info->root.rev, title); - if (resource->info->repos->repo_basename) - title = apr_psprintf(resource->pool, "%s - %s", - resource->info->repos->repo_basename, - title); - if (resource->info->repos->repo_name) - title = apr_psprintf(resource->pool, "%s: %s", - resource->info->repos->repo_name, - title); - } - - ap_fprintf(output, bb, "<html><head><title>%s</title></head>\n" - "<body>\n <h2>%s</h2>\n <ul>\n", title, title); - } - else - { - const char *name = resource->info->repos->repo_name; - const char *href = resource->info->repos_path; - const char *base = resource->info->repos->repo_basename; - - ap_fputs(output, bb, "<?xml version=\"1.0\"?>\n"); - ap_fprintf(output, bb, - "<?xml-stylesheet type=\"text/xsl\" href=\"%s\"?>\n", - resource->info->repos->xslt_uri); - ap_fputs(output, bb, xml_index_dtd); - ap_fputs(output, bb, - "<svn version=\"" SVN_VERSION "\"\n" - " href=\"http://subversion.apache.org/\">\n"); - ap_fputs(output, bb, " <index"); - if (name) - ap_fprintf(output, bb, " name=\"%s\"", - apr_xml_quote_string(resource->pool, name, 1)); - if (SVN_IS_VALID_REVNUM(resource->info->root.rev)) - ap_fprintf(output, bb, " rev=\"%ld\"", - resource->info->root.rev); - if (href) - ap_fprintf(output, bb, " path=\"%s\"", - apr_xml_quote_string(resource->pool, - href, - 1)); - if (base) - ap_fprintf(output, bb, " base=\"%s\"", base); - - ap_fputs(output, bb, ">\n"); - } - - if ((resource->info->restype != DAV_SVN_RESTYPE_PARENTPATH_COLLECTION) - && resource->info->repos_path - && ((resource->info->repos_path[1] != '\0') - || dav_svn__get_list_parentpath_flag(resource->info->r))) - { - const char *href; - if (resource->info->pegged) - { - href = apr_psprintf(resource->pool, "../?p=%ld", - resource->info->root.rev); - } - else - { - href = "../"; - } + bb = apr_brigade_create(resource->pool, + dav_svn__output_get_bucket_alloc(output)); - if (gen_html) - { - ap_fprintf(output, bb, - " <li><a href=\"%s\">..</a></li>\n", href); - } - else - { - ap_fprintf(output, bb, " <updir href=\"%s\"/>\n", href); - } - } + serr = emit_collection_head(resource, bb, output, gen_html, + resource->pool); + if (serr != NULL) + return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, + "could not output collection", + resource->pool); /* get a sorted list of the entries */ sorted = svn_sort__hash(entries, svn_sort_compare_items_as_paths, resource->pool); - entry_pool = svn_pool_create(resource->pool); + iterpool = svn_pool_create(resource->pool); for (i = 0; i < sorted->nelts; ++i) { @@ -3438,11 +3651,9 @@ deliver(const dav_resource *resource, ap_filter_t *output) const svn_sort__item_t); const svn_fs_dirent_t *entry = item->value; const char *name = item->key; - const char *href = name; - svn_boolean_t is_dir = (entry->kind == svn_node_dir); const char *repos_relpath = NULL; - svn_pool_clear(entry_pool); + svn_pool_clear(iterpool); /* DIR_REV is set to a valid revision if we're looking at the entries of a versioned directory. Otherwise, we're @@ -3450,120 +3661,45 @@ deliver(const dav_resource *resource, ap_filter_t *output) if (SVN_IS_VALID_REVNUM(dir_rev)) { repos_relpath = svn_fspath__join(resource->info->repos_path, - name, entry_pool); + name, iterpool); if (! dav_svn__allow_read(resource->info->r, resource->info->repos, repos_relpath, dir_rev, - entry_pool)) + iterpool)) continue; } else { if (! dav_svn__allow_list_repos(resource->info->r, - entry->name, entry_pool)) + entry->name, iterpool)) continue; } - /* append a trailing slash onto the name for directories. we NEED - this for the href portion so that the relative reference will - descend properly. for the visible portion, it is just nice. */ - /* ### The xml output doesn't like to see a trailing slash on - ### the visible portion, so avoid that. */ - if (is_dir) - href = apr_pstrcat(entry_pool, href, "/", (char *)NULL); - - if (gen_html) - name = href; - - /* We quote special characters in both XML and HTML. */ - name = apr_xml_quote_string(entry_pool, name, !gen_html); - - /* According to httpd-2.0.54/include/httpd.h, ap_os_escape_path() - behaves differently on different platforms. It claims to - "convert an OS path to a URL in an OS dependant way". - Nevertheless, there appears to be only one implementation - of the function in httpd, and the code seems completely - platform independent, so we'll assume it's appropriate for - mod_dav_svn to use it to quote outbound paths. */ - href = ap_os_escape_path(entry_pool, href, 0); - href = apr_xml_quote_string(entry_pool, href, 1); - - if (gen_html) - { - /* If our directory was access using the public peg-rev - CGI query interface, we'll let its dirents carry that - peg-rev, too. */ - if (resource->info->pegged) - { - ap_fprintf(output, bb, - " <li><a href=\"%s?p=%ld\">%s</a></li>\n", - href, resource->info->root.rev, name); - } - else - { - ap_fprintf(output, bb, - " <li><a href=\"%s\">%s</a></li>\n", - href, name); - } - } - else - { - const char *const tag = (is_dir ? "dir" : "file"); - - /* This is where we could search for props */ - - /* If our directory was access using the public peg-rev - CGI query interface, we'll let its dirents carry that - peg-rev, too. */ - if (resource->info->pegged) - { - ap_fprintf(output, bb, - " <%s name=\"%s\" href=\"%s?p=%ld\" />\n", - tag, name, href, resource->info->root.rev); - } - else - { - ap_fprintf(output, bb, - " <%s name=\"%s\" href=\"%s\" />\n", - tag, name, href); - } - } + serr = emit_collection_entry(resource, bb, output, entry, gen_html, + iterpool); + if (serr != NULL) + return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, + "could not output collection entry", + resource->pool); } - svn_pool_destroy(entry_pool); + svn_pool_destroy(iterpool); - if (gen_html) - { - if (strcmp(ap_psignature("FOO", resource->info->r), "") != 0) - { - /* Apache's signature generation code didn't eat our prefix. - ServerSignature must be enabled. Print our version info. - - WARNING: This is a kludge!! ap_psignature() doesn't promise - to return the empty string when ServerSignature is off. We - know it does by code inspection, but this behavior is subject - to change. (Perhaps we should try to get the Apache folks to - make this promise, though. Seems harmless/useful enough...) - */ - ap_fputs(output, bb, - " </ul>\n <hr noshade><em>Powered by " - "<a href=\"http://subversion.apache.org/\">" - "Apache Subversion" - "</a> version " SVN_VERSION "." - "</em>\n</body></html>"); - } - else - ap_fputs(output, bb, " </ul>\n</body></html>"); - } - else - ap_fputs(output, bb, " </index>\n</svn>\n"); + serr = emit_collection_tail(resource, bb, output, gen_html, + resource->pool); + if (serr != NULL) + return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, + "could not output collection", + resource->pool); - bkt = apr_bucket_eos_create(output->c->bucket_alloc); + bkt = apr_bucket_eos_create(dav_svn__output_get_bucket_alloc(output)); APR_BRIGADE_INSERT_TAIL(bb, bkt); - if ((status = ap_pass_brigade(output, bb)) != APR_SUCCESS) - return dav_svn__new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not write EOS to filter."); + serr = dav_svn__output_pass_brigade(output, bb); + if (serr != NULL) + return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, + "Could not write EOS to filter.", + resource->pool); return NULL; } @@ -3626,10 +3762,13 @@ deliver(const dav_resource *resource, ap_filter_t *output) "could not prepare to read a delta", resource->pool); + bb = apr_brigade_create(resource->pool, + dav_svn__output_get_bucket_alloc(output)); + /* create a stream that svndiff data will be written to, which will copy it to the network */ dc.output = output; - dc.pool = resource->pool; + dc.bb = bb; o_stream = svn_stream_create(&dc, resource->pool); svn_stream_set_write(o_stream, write_to_filter); svn_stream_set_close(o_stream, close_filter); @@ -3645,6 +3784,8 @@ deliver(const dav_resource *resource, ap_filter_t *output) to the network. */ serr = svn_txdelta_send_txstream(txd_stream, handler, h_baton, resource->pool); + apr_brigade_destroy(bb); + if (serr != NULL) return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "could not deliver the txdelta stream", @@ -3723,11 +3864,11 @@ deliver(const dav_resource *resource, ap_filter_t *output) resource->info->repos->base_url, ap_escape_uri(resource->pool, resource->info->r->uri), - NULL); + SVN_VA_NULL); str_root = apr_pstrcat(resource->pool, resource->info->repos->base_url, resource->info->repos->root_path, - NULL); + SVN_VA_NULL); serr = svn_subst_build_keywords3(&kw, keywords->data, str_cmt_rev, str_uri, str_root, @@ -3750,13 +3891,17 @@ deliver(const dav_resource *resource, ap_filter_t *output) ### which will read from the FS stream on demand */ block = apr_palloc(resource->pool, SVN__STREAM_CHUNK_SIZE); + bb = apr_brigade_create(resource->pool, + dav_svn__output_get_bucket_alloc(output)); + while (1) { apr_size_t bufsize = SVN__STREAM_CHUNK_SIZE; /* read from the FS ... */ - serr = svn_stream_read(stream, block, &bufsize); + serr = svn_stream_read_full(stream, block, &bufsize); if (serr != NULL) { + apr_brigade_destroy(bb); return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "could not read the file contents", resource->pool); @@ -3764,30 +3909,35 @@ deliver(const dav_resource *resource, ap_filter_t *output) if (bufsize == 0) break; - /* build a brigade and write to the filter ... */ - bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); - bkt = apr_bucket_transient_create(block, bufsize, - output->c->bucket_alloc); + /* write to the filter ... */ + bkt = apr_bucket_transient_create( + block, bufsize, dav_svn__output_get_bucket_alloc(output)); APR_BRIGADE_INSERT_TAIL(bb, bkt); - if ((status = ap_pass_brigade(output, bb)) != APR_SUCCESS) { - /* ### what to do with status; and that HTTP code... */ - return dav_svn__new_error(resource->pool, - HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not write data to filter."); - } + serr = dav_svn__output_pass_brigade(output, bb); + if (serr != NULL) + { + apr_brigade_destroy(bb); + /* ### that HTTP code... */ + return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, + "Could not write data to filter.", + resource->pool); + } } /* done with the file. write an EOS bucket now. */ - bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); - bkt = apr_bucket_eos_create(output->c->bucket_alloc); + bkt = apr_bucket_eos_create(dav_svn__output_get_bucket_alloc(output)); APR_BRIGADE_INSERT_TAIL(bb, bkt); - if ((status = ap_pass_brigade(output, bb)) != APR_SUCCESS) { - /* ### what to do with status; and that HTTP code... */ - return dav_svn__new_error(resource->pool, - HTTP_INTERNAL_SERVER_ERROR, 0, - "Could not write EOS to filter."); - } + serr = dav_svn__output_pass_brigade(output, bb); + if (serr != NULL) + { + apr_brigade_destroy(bb); + /* ### that HTTP code... */ + return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, + "Could not write EOS to filter.", + resource->pool); + } + apr_brigade_destroy(bb); return NULL; } } @@ -3904,23 +4054,28 @@ copy_resource(const dav_resource *src, return err; } - serr = svn_dirent_get_absolute(&src_repos_path, - svn_repos_path(src->info->repos->repos, - src->pool), - src->pool); - if (!serr) - serr = svn_dirent_get_absolute(&dst_repos_path, - svn_repos_path(dst->info->repos->repos, - dst->pool), - dst->pool); + src_repos_path = svn_repos_path(src->info->repos->repos, src->pool); + dst_repos_path = svn_repos_path(dst->info->repos->repos, dst->pool); + + if (strcmp(src_repos_path, dst_repos_path) != 0) + { + /* Perhaps the source and dst repos use different path formats? */ + serr = svn_error_compose_create( + svn_dirent_get_absolute(&src_repos_path, src_repos_path, + src->pool), + svn_dirent_get_absolute(&dst_repos_path, dst_repos_path, + dst->pool)); + + if (!serr && (strcmp(src_repos_path, dst_repos_path) != 0)) + return dav_svn__new_error_svn( + dst->pool, HTTP_INTERNAL_SERVER_ERROR, 0, + "Copy source and destination are in different repositories"); + } + else + serr = SVN_NO_ERROR; if (!serr) { - if (strcmp(src_repos_path, dst_repos_path) != 0) - return dav_svn__new_error_tag - (dst->pool, HTTP_INTERNAL_SERVER_ERROR, 0, - "Copy source and destination are in different repositories.", - SVN_DAV_ERROR_NAMESPACE, SVN_DAV_ERROR_TAG); serr = svn_fs_copy(src->info->root.root, /* root object of src rev*/ src->info->repos_path, /* relative path of src */ dst->info->root.root, /* root object of dst txn*/ @@ -4030,7 +4185,11 @@ remove_resource(dav_resource *resource, dav_response **response) if (resource->info->version_name < created_rev) { serr = svn_error_createf(SVN_ERR_RA_OUT_OF_DATE, NULL, - "Item '%s' is out of date", + resource->collection + ? "Directory '%s' is out of date" + : (resource->exists + ? "File '%s' is out of date" + : "'%s' is out of date"), resource->info->repos_path); return dav_svn__convert_err(serr, HTTP_CONFLICT, "Can't DELETE out-of-date resource", @@ -4042,8 +4201,9 @@ remove_resource(dav_resource *resource, dav_response **response) incoming lock-tokens into the filesystem's access_t. Normally they come in via 'If:' header, and get_resource() automatically notices them and does this work for us. In the - case of a directory deletion, however, svn clients are sending - 'child' lock-tokens in the DELETE request body. */ + case of a directory deletion, however, older subversion clients + are sending 'child' lock-tokens in the non-standard DELETE + request body. */ err = dav_svn__build_lock_hash(&locks, resource->info->r, resource->info->repos_path, resource->pool); @@ -4157,10 +4317,15 @@ typedef struct walker_ctx_t { } walker_ctx_t; - +/* Recursively walk a resource for walk(). When DEPTH != 0, recurse with + DEPTH-1 on child nodes. WALK_ROOT should be TRUE for the root and will be + FALSE for any descendants, to avoid unneeded work for every descendant + node. + */ static dav_error * do_walk(walker_ctx_t *ctx, int depth, + svn_boolean_t walk_root, apr_pool_t *scratch_pool) { const dav_walk_params *params = ctx->params; @@ -4225,16 +4390,19 @@ do_walk(walker_ctx_t *ctx, uri_len = ctx->uri->len; repos_len = ctx->repos_path->len; - /* Tell our logging subsystem that we're listing a directory. - - Note: if we cared, we could look at the 'User-Agent:' request - header and distinguish an svn client ('svn ls') from a generic - DAV client. */ - dav_svn__operational_log(&ctx->info, - svn_log__get_dir(ctx->info.repos_path, - ctx->info.root.rev, - TRUE, FALSE, SVN_DIRENT_ALL, - scratch_pool)); + if (walk_root) + { + /* Tell our logging subsystem that we're listing a directory. + + Note: if we cared, we could look at the 'User-Agent:' request + header and distinguish an svn client ('svn ls') from a generic + DAV client. */ + dav_svn__operational_log(&ctx->info, + svn_log__get_dir(ctx->info.repos_path, + ctx->info.root.rev, + TRUE, FALSE, SVN_DIRENT_ALL, + scratch_pool)); + } /* fetch this collection's children */ serr = svn_fs_dir_entries(&children, ctx->info.root.root, @@ -4267,7 +4435,7 @@ do_walk(walker_ctx_t *ctx, apr_pstrmemdup(iterpool, ctx->repos_path->data, ctx->repos_path->len), - key, (char *)NULL); + key, SVN_VA_NULL); if (! dav_svn__allow_read(ctx->info.r, ctx->info.repos, repos_relpath, ctx->info.root.rev, iterpool)) @@ -4287,7 +4455,10 @@ do_walk(walker_ctx_t *ctx, { err = (*params->func)(&ctx->wres, DAV_CALLTYPE_MEMBER); if (err != NULL) - return err; + { + svn_pool_destroy(iterpool); + return err; + } } else { @@ -4299,9 +4470,12 @@ do_walk(walker_ctx_t *ctx, ctx->res.uri = ctx->uri->data; /* recurse on this collection */ - err = do_walk(ctx, depth - 1, iterpool); + err = do_walk(ctx, depth - 1, FALSE, iterpool); if (err != NULL) - return err; + { + svn_pool_destroy(iterpool); + return err; + } /* restore the data */ ctx->res.collection = FALSE; @@ -4381,7 +4555,7 @@ walk(const dav_walk_params *params, int depth, dav_response **response) /* ### is the root already/always open? need to verify */ /* always return the error, and any/all multistatus responses */ - err = do_walk(&ctx, depth, params->pool); + err = do_walk(&ctx, depth, TRUE, params->pool); *response = ctx.wres.response; return err; @@ -4428,7 +4602,7 @@ dav_svn__create_working_resource(dav_resource *base, if (base->info->repos->root_path[1]) res->uri = apr_pstrcat(base->pool, base->info->repos->root_path, - path, (char *)NULL); + path, SVN_VA_NULL); else res->uri = path; res->hooks = &dav_svn__hooks_repository; @@ -4481,7 +4655,7 @@ dav_svn__working_to_regular_resource(dav_resource *resource) /* if rev was specific, create baseline-collection URL */ path = dav_svn__build_uri(repos, DAV_SVN__BUILD_URI_BC, priv->root.rev, priv->repos_path, - 0, resource->pool); + FALSE /* add_href */, resource->pool); } path = svn_path_uri_encode(path, resource->pool); priv->uri_path = svn_stringbuf_create(path, resource->pool); @@ -4526,7 +4700,7 @@ dav_svn__create_version_resource(dav_resource **version_res, static dav_error * handle_post_request(request_rec *r, dav_resource *resource, - ap_filter_t *output) + dav_svn__output *output) { svn_skel_t *request_skel, *post_skel; int status; @@ -4609,7 +4783,9 @@ int dav_svn__method_post(request_rec *r) content_type = apr_table_get(r->headers_in, "content-type"); if (content_type && (strcmp(content_type, SVN_SKEL_MIME_TYPE) == 0)) { - derr = handle_post_request(r, resource, r->output_filters); + dav_svn__output *output = dav_svn__output_create(resource->info->r, + resource->pool); + derr = handle_post_request(r, resource, output); } else { diff --git a/subversion/mod_dav_svn/status.c b/subversion/mod_dav_svn/status.c new file mode 100644 index 0000000..6dc1c9e --- /dev/null +++ b/subversion/mod_dav_svn/status.c @@ -0,0 +1,115 @@ +/* + * ==================================================================== + * 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. + * ==================================================================== + */ + +#include <httpd.h> +#include <http_core.h> +#include <http_config.h> +#include <http_request.h> +#include <http_protocol.h> + +#include "dav_svn.h" +#include "private/svn_cache.h" +#include "private/svn_fs_private.h" + +#ifdef HAVE_UNISTD_H +#include <unistd.h> /* For getpid() */ +#endif + +/* The apache headers define these and they conflict with our definitions. */ +#ifdef PACKAGE_BUGREPORT +#undef PACKAGE_BUGREPORT +#endif +#ifdef PACKAGE_NAME +#undef PACKAGE_NAME +#endif +#ifdef PACKAGE_STRING +#undef PACKAGE_STRING +#endif +#ifdef PACKAGE_TARNAME +#undef PACKAGE_TARNAME +#endif +#ifdef PACKAGE_VERSION +#undef PACKAGE_VERSION +#endif +#include "svn_private_config.h" + +#ifndef DEFAULT_TIME_FORMAT +#define DEFAULT_TIME_FORMAT "%Y-%m-%d %H:%M:%S %Z" +#endif + +/* A bit like mod_status: add a location: + + <Location /svn-status> + SetHandler svn-status + </Location> + + and then point a browser at http://server/svn-status. +*/ +int dav_svn__status(request_rec *r) +{ + svn_cache__info_t *info; + svn_string_t *text_stats; + apr_array_header_t *lines; + int i; + + if (r->method_number != M_GET || strcmp(r->handler, "svn-status")) + return DECLINED; + + info = svn_cache__membuffer_get_global_info(r->pool); + text_stats = svn_cache__format_info(info, FALSE, r->pool); + lines = svn_cstring_split(text_stats->data, "\n", FALSE, r->pool); + + ap_set_content_type(r, "text/html; charset=ISO-8859-1"); + + ap_rvputs(r, + DOCTYPE_HTML_3_2 + "<html><head>\n" + "<title>Apache SVN Status</title>\n" + "</head><body>\n" + "<h1>Apache SVN Cache Status for ", + ap_escape_html(r->pool, ap_get_server_name(r)), + " (via ", + r->connection->local_ip, + ")</h1>\n<dl>\n<dt>Server Version: ", + ap_get_server_description(), + "</dt>\n<dt>Current Time: ", + ap_ht_time(r->pool, apr_time_now(), DEFAULT_TIME_FORMAT, 0), + "</dt>\n", SVN_VA_NULL); + +#if !defined(WIN32) && defined(HAVE_UNISTD_H) && defined(HAVE_GETPID) + /* On Unix the server is generally multiple processes and this + request only shows the status of the single process that handles + the request. Ideally we would iterate over all processes but that + would need some MPM support, so we settle for simply showing the + process ID. */ + ap_rprintf(r, "<dt>Server process id: %d</dt>\n", (int)getpid()); +#endif + + for (i = 0; i < lines->nelts; ++i) + { + const char *line = APR_ARRAY_IDX(lines, i, const char *); + ap_rvputs(r, "<dt>", line, "</dt>\n", SVN_VA_NULL); + } + + ap_rvputs(r, "</dl></body></html>\n", SVN_VA_NULL); + + return 0; +} diff --git a/subversion/mod_dav_svn/util.c b/subversion/mod_dav_svn/util.c index 2890502..ce824cd 100644 --- a/subversion/mod_dav_svn/util.c +++ b/subversion/mod_dav_svn/util.c @@ -1,5 +1,8 @@ /* - * util.c: some handy utility functions + * util.c: + * # **************************************************************************** + * # TRASHY LITTLE SUBROUTINES + * # **************************************************************************** * * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one @@ -37,7 +40,6 @@ #include "dav_svn.h" #include "private/svn_fspath.h" -#include "private/svn_string_private.h" dav_error * dav_svn__new_error(apr_pool_t *pool, @@ -59,26 +61,24 @@ dav_svn__new_error(apr_pool_t *pool, return dav_new_error(pool, status, error_id, 0, desc); #else - errno = 0; /* For the same reason as in dav_svn__new_error_tag */ + errno = 0; /* For the same reason as in dav_svn__new_error_svn */ return dav_new_error(pool, status, error_id, desc); #endif } dav_error * -dav_svn__new_error_tag(apr_pool_t *pool, +dav_svn__new_error_svn(apr_pool_t *pool, int status, int error_id, - const char *desc, - const char *namespace, - const char *tagname) + const char *desc) { if (error_id == 0) error_id = SVN_ERR_RA_DAV_REQUEST_FAILED; #if AP_MODULE_MAGIC_AT_LEAST(20091119,0) return dav_new_error_tag(pool, status, error_id, 0, - desc, namespace, tagname); + desc, SVN_DAV_ERROR_NAMESPACE, SVN_DAV_ERROR_TAG); #else /* dav_new_error_tag will record errno but Subversion makes no attempt to ensure that it is valid. We reset it to avoid putting incorrect @@ -86,7 +86,8 @@ dav_svn__new_error_tag(apr_pool_t *pool, valid information. */ errno = 0; - return dav_new_error_tag(pool, status, error_id, desc, namespace, tagname); + return dav_new_error_tag(pool, status, error_id, desc, + SVN_DAV_ERROR_NAMESPACE, SVN_DAV_ERROR_TAG); #endif } @@ -96,11 +97,11 @@ dav_svn__new_error_tag(apr_pool_t *pool, static dav_error * build_error_chain(apr_pool_t *pool, svn_error_t *err, int status) { - char *msg = err->message ? apr_pstrdup(pool, err->message) : NULL; + char buffer[128]; + const char *msg = svn_err_best_message(err, buffer, sizeof(buffer)); - dav_error *derr = dav_svn__new_error_tag(pool, status, err->apr_err, msg, - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + dav_error *derr = dav_svn__new_error_svn(pool, status, err->apr_err, + apr_pstrdup(pool, msg)); if (err->child) derr->prev = build_error_chain(pool, err->child, status); @@ -115,51 +116,52 @@ dav_svn__convert_err(svn_error_t *serr, const char *message, apr_pool_t *pool) { - dav_error *derr; - - /* Remove the trace-only error chain links. We need predictable - protocol behavior regardless of whether or not we're in a - debugging build. */ - svn_error_t *purged_serr = svn_error_purge_tracing(serr); - - /* ### someday mod_dav_svn will send back 'rich' error tags, much - finer grained than plain old svn_error_t's. But for now, all - svn_error_t's are marshalled to the client via the single - generic <svn:error/> tag nestled within a <D:error> block. */ - - /* Examine the Subverion error code, and select the most - appropriate HTTP status code. If no more appropriate HTTP - status code maps to the Subversion error code, use the one - suggested status provided by the caller. */ - switch (purged_serr->apr_err) - { - case SVN_ERR_FS_NOT_FOUND: - status = HTTP_NOT_FOUND; - break; - case SVN_ERR_UNSUPPORTED_FEATURE: - status = HTTP_NOT_IMPLEMENTED; - break; - case SVN_ERR_FS_LOCK_OWNER_MISMATCH: - case SVN_ERR_FS_PATH_ALREADY_LOCKED: - status = HTTP_LOCKED; - break; - case SVN_ERR_FS_PROP_BASEVALUE_MISMATCH: - status = HTTP_PRECONDITION_FAILED; - break; - /* add other mappings here */ - } - - derr = build_error_chain(pool, purged_serr, status); - if (message != NULL - && purged_serr->apr_err != SVN_ERR_REPOS_HOOK_FAILURE) - /* Don't hide hook failures; we might hide the error text */ - derr = dav_push_error(pool, status, purged_serr->apr_err, - message, derr); - - /* Now, destroy the Subversion error. */ - svn_error_clear(serr); - - return derr; + dav_error *derr; + + /* Remove the trace-only error chain links. We need predictable + protocol behavior regardless of whether or not we're in a + debugging build. */ + svn_error_t *purged_serr = svn_error_purge_tracing(serr); + + /* ### someday mod_dav_svn will send back 'rich' error tags, much + finer grained than plain old svn_error_t's. But for now, all + svn_error_t's are marshalled to the client via the single + generic <svn:error/> tag nestled within a <D:error> block. */ + + /* Examine the Subverion error code, and select the most + appropriate HTTP status code. If no more appropriate HTTP + status code maps to the Subversion error code, use the one + suggested status provided by the caller. */ + switch (purged_serr->apr_err) + { + case SVN_ERR_FS_NOT_FOUND: + case SVN_ERR_FS_NO_SUCH_REVISION: + status = HTTP_NOT_FOUND; + break; + case SVN_ERR_UNSUPPORTED_FEATURE: + status = HTTP_NOT_IMPLEMENTED; + break; + case SVN_ERR_FS_LOCK_OWNER_MISMATCH: + case SVN_ERR_FS_PATH_ALREADY_LOCKED: + status = HTTP_LOCKED; + break; + case SVN_ERR_FS_PROP_BASEVALUE_MISMATCH: + status = HTTP_PRECONDITION_FAILED; + break; + /* add other mappings here */ + } + + derr = build_error_chain(pool, purged_serr, status); + if (message != NULL + && !svn_error_find_cause(purged_serr, SVN_ERR_REPOS_HOOK_FAILURE)) + /* Don't hide hook failures; we might hide the error text */ + derr = dav_push_error(pool, status, purged_serr->apr_err, + message, derr); + + /* Now, destroy the Subversion error. */ + svn_error_clear(serr); + + return derr; } @@ -176,10 +178,10 @@ get_last_history_rev(svn_revnum_t *revision, const char *ignored; /* Get an initial HISTORY baton. */ - SVN_ERR(svn_fs_node_history(&history, root, path, pool)); + SVN_ERR(svn_fs_node_history2(&history, root, path, pool, pool)); /* Now get the first *real* point of interesting history. */ - SVN_ERR(svn_fs_history_prev(&history, history, FALSE, pool)); + SVN_ERR(svn_fs_history_prev2(&history, history, FALSE, pool, pool)); /* Fetch the location information for this history step. */ return svn_fs_history_location(&ignored, revision, history, pool); @@ -193,15 +195,9 @@ dav_svn__get_safe_cr(svn_fs_root_t *root, const char *path, apr_pool_t *pool) svn_revnum_t history_rev; svn_fs_root_t *other_root; svn_fs_t *fs = svn_fs_root_fs(root); - const svn_fs_id_t *id, *other_id; + svn_fs_node_relation_t node_relation; svn_error_t *err; - if ((err = svn_fs_node_id(&id, root, path, pool))) - { - svn_error_clear(err); - return revision; /* couldn't get id of root/path */ - } - if ((err = get_last_history_rev(&history_rev, root, path, pool))) { svn_error_clear(err); @@ -214,13 +210,14 @@ dav_svn__get_safe_cr(svn_fs_root_t *root, const char *path, apr_pool_t *pool) return revision; /* couldn't open the history rev */ } - if ((err = svn_fs_node_id(&other_id, other_root, path, pool))) + if ((err = svn_fs_node_relation(&node_relation, root, path, + other_root, path, pool))) { svn_error_clear(err); - return revision; /* couldn't get id of other_root/path */ + return revision; } - if (svn_fs_compare_ids(id, other_id) == 0) + if (node_relation == svn_fs_node_unchanged) return history_rev; /* the history rev is safe! the same node exists at the same path in both revisions. */ @@ -234,7 +231,7 @@ dav_svn__build_uri(const dav_svn_repos *repos, enum dav_svn__build_what what, svn_revnum_t revision, const char *path, - int add_href, + svn_boolean_t add_href, apr_pool_t *pool) { const char *root_path = repos->root_path; @@ -464,6 +461,48 @@ dav_svn__find_ns(const apr_array_header_t *namespaces, const char *uri) return -1; } + +/*** Output helpers ***/ + + +struct dav_svn__output +{ + request_rec *r; +}; + +dav_svn__output * +dav_svn__output_create(request_rec *r, + apr_pool_t *pool) +{ + dav_svn__output *output = apr_pcalloc(pool, sizeof(*output)); + output->r = r; + return output; +} + +apr_bucket_alloc_t * +dav_svn__output_get_bucket_alloc(dav_svn__output *output) +{ + return output->r->connection->bucket_alloc; +} + +svn_error_t * +dav_svn__output_pass_brigade(dav_svn__output *output, + apr_bucket_brigade *bb) +{ + apr_status_t status; + + status = ap_pass_brigade(output->r->output_filters, bb); + /* Empty the brigade here, as required by ap_pass_brigade(). */ + apr_brigade_cleanup(bb); + if (status) + return svn_error_create(status, NULL, "Could not write data to filter"); + + /* Check for an aborted connection, since the brigade functions don't + appear to return useful errors when the connection is dropped. */ + if (output->r->connection->aborted) + return svn_error_create(SVN_ERR_APMOD_CONNECTION_ABORTED, NULL, NULL); + return SVN_NO_ERROR; +} /*** Brigade I/O wrappers ***/ @@ -471,17 +510,18 @@ dav_svn__find_ns(const apr_array_header_t *namespaces, const char *uri) svn_error_t * dav_svn__brigade_write(apr_bucket_brigade *bb, - ap_filter_t *output, + dav_svn__output *output, const char *data, apr_size_t len) { apr_status_t apr_err; - apr_err = apr_brigade_write(bb, ap_filter_flush, output, data, len); + apr_err = apr_brigade_write(bb, ap_filter_flush, + output->r->output_filters, data, len); if (apr_err) return svn_error_create(apr_err, 0, NULL); /* Check for an aborted connection, since the brigade functions don't appear to be return useful errors when the connection is dropped. */ - if (output->c->aborted) + if (output->r->connection->aborted) return svn_error_create(SVN_ERR_APMOD_CONNECTION_ABORTED, 0, NULL); return SVN_NO_ERROR; } @@ -489,16 +529,17 @@ dav_svn__brigade_write(apr_bucket_brigade *bb, svn_error_t * dav_svn__brigade_puts(apr_bucket_brigade *bb, - ap_filter_t *output, + dav_svn__output *output, const char *str) { apr_status_t apr_err; - apr_err = apr_brigade_puts(bb, ap_filter_flush, output, str); + apr_err = apr_brigade_puts(bb, ap_filter_flush, + output->r->output_filters, str); if (apr_err) return svn_error_create(apr_err, 0, NULL); /* Check for an aborted connection, since the brigade functions don't appear to be return useful errors when the connection is dropped. */ - if (output->c->aborted) + if (output->r->connection->aborted) return svn_error_create(SVN_ERR_APMOD_CONNECTION_ABORTED, 0, NULL); return SVN_NO_ERROR; } @@ -506,7 +547,7 @@ dav_svn__brigade_puts(apr_bucket_brigade *bb, svn_error_t * dav_svn__brigade_printf(apr_bucket_brigade *bb, - ap_filter_t *output, + dav_svn__output *output, const char *fmt, ...) { @@ -514,18 +555,41 @@ dav_svn__brigade_printf(apr_bucket_brigade *bb, va_list ap; va_start(ap, fmt); - apr_err = apr_brigade_vprintf(bb, ap_filter_flush, output, fmt, ap); + apr_err = apr_brigade_vprintf(bb, ap_filter_flush, + output->r->output_filters, fmt, ap); va_end(ap); if (apr_err) return svn_error_create(apr_err, 0, NULL); /* Check for an aborted connection, since the brigade functions don't appear to be return useful errors when the connection is dropped. */ - if (output->c->aborted) + if (output->r->connection->aborted) return svn_error_create(SVN_ERR_APMOD_CONNECTION_ABORTED, 0, NULL); return SVN_NO_ERROR; } +svn_error_t * +dav_svn__brigade_putstrs(apr_bucket_brigade *bb, + dav_svn__output *output, + ...) +{ + apr_status_t apr_err; + va_list ap; + + va_start(ap, output); + apr_err = apr_brigade_vputstrs(bb, ap_filter_flush, + output->r->output_filters, ap); + va_end(ap); + if (apr_err) + return svn_error_create(apr_err, NULL, NULL); + /* Check for an aborted connection, since the brigade functions don't + appear to return useful errors when the connection is dropped. */ + if (output->r->connection->aborted) + return svn_error_create(SVN_ERR_APMOD_CONNECTION_ABORTED, NULL, NULL); + return SVN_NO_ERROR; +} + + dav_error * @@ -541,12 +605,11 @@ dav_svn__test_canonical(const char *path, apr_pool_t *pool) return NULL; /* Otherwise, generate a generic HTTP_BAD_REQUEST error. */ - return dav_svn__new_error_tag - (pool, HTTP_BAD_REQUEST, 0, + return dav_svn__new_error_svn( + pool, HTTP_BAD_REQUEST, 0, apr_psprintf(pool, "Path '%s' is not canonicalized; " - "there is a problem with the client.", path), - SVN_DAV_ERROR_NAMESPACE, SVN_DAV_ERROR_TAG); + "there is a problem with the client.", path)); } @@ -576,18 +639,19 @@ dav_svn__sanitize_error(svn_error_t *serr, "%s", purged_serr->message); } - svn_error_clear(serr); - } - return dav_svn__convert_err(safe_err, http_status, - apr_psprintf(r->pool, "%s", safe_err->message), - r->pool); + svn_error_clear(serr); + } + + return dav_svn__convert_err(safe_err, http_status, + apr_psprintf(r->pool, "%s", safe_err->message), + r->pool); } struct brigade_write_baton { apr_bucket_brigade *bb; - ap_filter_t *output; + dav_svn__output *output; }; @@ -598,7 +662,8 @@ brigade_write_fn(void *baton, const char *data, apr_size_t *len) struct brigade_write_baton *wb = baton; apr_status_t apr_err; - apr_err = apr_brigade_write(wb->bb, ap_filter_flush, wb->output, data, *len); + apr_err = apr_brigade_write(wb->bb, ap_filter_flush, + wb->output->r->output_filters, data, *len); if (apr_err != APR_SUCCESS) return svn_error_wrap_apr(apr_err, "Error writing base64 data"); @@ -609,7 +674,7 @@ brigade_write_fn(void *baton, const char *data, apr_size_t *len) svn_stream_t * dav_svn__make_base64_output_stream(apr_bucket_brigade *bb, - ap_filter_t *output, + dav_svn__output *output, apr_pool_t *pool) { struct brigade_write_baton *wb = apr_palloc(pool, sizeof(*wb)); @@ -636,7 +701,7 @@ dav_svn__operational_log(struct dav_resource_private *info, const char *line) dav_error * dav_svn__final_flush_or_error(request_rec *r, apr_bucket_brigade *bb, - ap_filter_t *output, + dav_svn__output *output, dav_error *preferred_err, apr_pool_t *pool) { @@ -658,7 +723,7 @@ dav_svn__final_flush_or_error(request_rec *r, provided a more-important DERR, though. */ if (do_flush) { - apr_status_t apr_err = ap_fflush(output, bb); + apr_status_t apr_err = ap_fflush(output->r->output_filters, bb); if (apr_err && (! derr)) derr = dav_svn__new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0, "Error flushing brigade."); @@ -745,7 +810,7 @@ request_body_to_string(svn_string_t **request_str, int seen_eos; apr_status_t status; apr_off_t total_read = 0; - apr_off_t limit_req_body = ap_get_limit_req_body(r); + apr_off_t limit_req_body = ap_get_limit_xml_body(r); int result = HTTP_BAD_REQUEST; const char *content_length_str; char *endp; @@ -757,7 +822,7 @@ request_body_to_string(svn_string_t **request_str, content_length_str = apr_table_get(r->headers_in, "Content-Length"); if (content_length_str) { - if (svn__strtoff(&content_length, content_length_str, &endp, 10) + if (apr_strtoff(&content_length, content_length_str, &endp, 10) || endp == content_length_str || *endp || content_length < 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid Content-Length"); @@ -778,7 +843,12 @@ request_body_to_string(svn_string_t **request_str, if (content_length) { - buf = svn_stringbuf_create_ensure(content_length, pool); + /* Do not allocate more than 1 MB until we receive request body. */ + apr_size_t alloc_len = 1 * 1024 *1024; + if (content_length < alloc_len) + alloc_len = (apr_size_t) content_length; + + buf = svn_stringbuf_create_ensure(alloc_len, pool); } else { @@ -861,7 +931,7 @@ dav_svn__parse_request_skel(svn_skel_t **skel, *skel = NULL; status = request_body_to_string(&skel_str, r, pool); if (status != OK) - return OK; + return status; *skel = svn_skel__parse(skel_str->data, skel_str->len, pool); return OK; diff --git a/subversion/mod_dav_svn/version.c b/subversion/mod_dav_svn/version.c index 98208a9..638cae9 100644 --- a/subversion/mod_dav_svn/version.c +++ b/subversion/mod_dav_svn/version.c @@ -178,26 +178,33 @@ get_option(const dav_resource *resource, request_rec *r = resource->info->r; const char *repos_root_uri = dav_svn__build_uri(resource->info->repos, DAV_SVN__BUILD_URI_PUBLIC, - SVN_IGNORED_REVNUM, "", 0, resource->pool); + SVN_IGNORED_REVNUM, "", FALSE /* add_href */, + resource->pool); /* ### DAV:version-history-collection-set */ - if (elem->ns == APR_XML_NS_DAV_ID) + if (elem->ns != APR_XML_NS_DAV_ID + || strcmp(elem->name, "activity-collection-set") != 0) { - if (strcmp(elem->name, "activity-collection-set") == 0) - { - apr_text_append(resource->pool, option, - "<D:activity-collection-set>"); - apr_text_append(resource->pool, option, - dav_svn__build_uri(resource->info->repos, - DAV_SVN__BUILD_URI_ACT_COLLECTION, - SVN_INVALID_REVNUM, NULL, - 1 /* add_href */, - resource->pool)); - apr_text_append(resource->pool, option, - "</D:activity-collection-set>"); - } + /* We don't know about other options (yet). + + If we ever add multiple option request keys we should + just write the requested option value and make sure + we set the headers *once*. */ + return NULL; } + apr_text_append(resource->pool, option, + "<D:activity-collection-set>"); + + apr_text_append(resource->pool, option, + dav_svn__build_uri(resource->info->repos, + DAV_SVN__BUILD_URI_ACT_COLLECTION, + SVN_INVALID_REVNUM, NULL, + TRUE /* add_href */, + resource->pool)); + apr_text_append(resource->pool, option, + "</D:activity-collection-set>"); + /* If we're allowed (by configuration) to do so, advertise support for ephemeral transaction properties. */ if (dav_svn__check_ephemeral_txnprops_support(r)) @@ -297,25 +304,25 @@ get_option(const dav_resource *resource, apr_table_set(r->headers_out, SVN_DAV_ROOT_URI_HEADER, repos_root_uri); apr_table_set(r->headers_out, SVN_DAV_ME_RESOURCE_HEADER, apr_pstrcat(resource->pool, repos_root_uri, "/", - dav_svn__get_me_resource_uri(r), (char *)NULL)); + dav_svn__get_me_resource_uri(r), SVN_VA_NULL)); apr_table_set(r->headers_out, SVN_DAV_REV_ROOT_STUB_HEADER, apr_pstrcat(resource->pool, repos_root_uri, "/", - dav_svn__get_rev_root_stub(r), (char *)NULL)); + dav_svn__get_rev_root_stub(r), SVN_VA_NULL)); apr_table_set(r->headers_out, SVN_DAV_REV_STUB_HEADER, apr_pstrcat(resource->pool, repos_root_uri, "/", - dav_svn__get_rev_stub(r), (char *)NULL)); + dav_svn__get_rev_stub(r), SVN_VA_NULL)); apr_table_set(r->headers_out, SVN_DAV_TXN_ROOT_STUB_HEADER, apr_pstrcat(resource->pool, repos_root_uri, "/", - dav_svn__get_txn_root_stub(r), (char *)NULL)); + dav_svn__get_txn_root_stub(r), SVN_VA_NULL)); apr_table_set(r->headers_out, SVN_DAV_TXN_STUB_HEADER, apr_pstrcat(resource->pool, repos_root_uri, "/", - dav_svn__get_txn_stub(r), (char *)NULL)); + dav_svn__get_txn_stub(r), SVN_VA_NULL)); apr_table_set(r->headers_out, SVN_DAV_VTXN_ROOT_STUB_HEADER, apr_pstrcat(resource->pool, repos_root_uri, "/", - dav_svn__get_vtxn_root_stub(r), (char *)NULL)); + dav_svn__get_vtxn_root_stub(r), SVN_VA_NULL)); apr_table_set(r->headers_out, SVN_DAV_VTXN_STUB_HEADER, apr_pstrcat(resource->pool, repos_root_uri, "/", - dav_svn__get_vtxn_stub(r), (char *)NULL)); + dav_svn__get_vtxn_stub(r), SVN_VA_NULL)); apr_table_set(r->headers_out, SVN_DAV_ALLOW_BULK_UPDATES, bulk_upd_conf == CONF_BULKUPD_ON ? "On" : bulk_upd_conf == CONF_BULKUPD_OFF ? "Off" : "Prefer"); @@ -395,11 +402,9 @@ vsn_control(dav_resource *resource, const char *target) /* Only allow a NULL target, which means an create an 'empty' VCR. */ if (target != NULL) - return dav_svn__new_error_tag(resource->pool, HTTP_NOT_IMPLEMENTED, + return dav_svn__new_error_svn(resource->pool, HTTP_NOT_IMPLEMENTED, SVN_ERR_UNSUPPORTED_FEATURE, - "vsn_control called with non-null target.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "vsn_control called with non-null target"); /* This is kind of silly. The docstring for this callback says it's supposed to "put a resource under version control". But in @@ -445,20 +450,16 @@ dav_svn__checkout(dav_resource *resource, return NULL; if (resource->type != DAV_RESOURCE_TYPE_REGULAR) - return dav_svn__new_error_tag(resource->pool, HTTP_METHOD_NOT_ALLOWED, + return dav_svn__new_error_svn(resource->pool, HTTP_METHOD_NOT_ALLOWED, SVN_ERR_UNSUPPORTED_FEATURE, "auto-checkout attempted on non-regular " - "version-controlled resource.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "version-controlled resource"); if (resource->baselined) - return dav_svn__new_error_tag(resource->pool, HTTP_METHOD_NOT_ALLOWED, + return dav_svn__new_error_svn(resource->pool, HTTP_METHOD_NOT_ALLOWED, SVN_ERR_UNSUPPORTED_FEATURE, "auto-checkout attempted on baseline " - "collection, which is not supported.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "collection, which is not supported"); /* See if the shared activity already exists. */ apr_err = apr_pool_userdata_get(&data, @@ -540,51 +541,41 @@ dav_svn__checkout(dav_resource *resource, if (resource->type != DAV_RESOURCE_TYPE_VERSION) { - return dav_svn__new_error_tag(resource->pool, HTTP_METHOD_NOT_ALLOWED, + return dav_svn__new_error_svn(resource->pool, HTTP_METHOD_NOT_ALLOWED, SVN_ERR_UNSUPPORTED_FEATURE, "CHECKOUT can only be performed on a " - "version resource [at this time].", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "version resource"); } if (create_activity) { - return dav_svn__new_error_tag(resource->pool, HTTP_NOT_IMPLEMENTED, + return dav_svn__new_error_svn(resource->pool, HTTP_NOT_IMPLEMENTED, SVN_ERR_UNSUPPORTED_FEATURE, "CHECKOUT cannot create an activity at " - "this time. Use MKACTIVITY first.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "this time. Use MKACTIVITY first"); } if (is_unreserved) { - return dav_svn__new_error_tag(resource->pool, HTTP_NOT_IMPLEMENTED, + return dav_svn__new_error_svn(resource->pool, HTTP_NOT_IMPLEMENTED, SVN_ERR_UNSUPPORTED_FEATURE, "Unreserved checkouts are not yet " "available. A version history may not be " "checked out more than once, into a " - "specific activity.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "specific activity"); } if (activities == NULL) { - return dav_svn__new_error_tag(resource->pool, HTTP_CONFLICT, + return dav_svn__new_error_svn(resource->pool, HTTP_CONFLICT, SVN_ERR_INCOMPLETE_DATA, "An activity must be provided for " - "checkout.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "checkout"); } /* assert: nelts > 0. the below check effectively means > 1. */ if (activities->nelts != 1) { - return dav_svn__new_error_tag(resource->pool, HTTP_CONFLICT, + return dav_svn__new_error_svn(resource->pool, HTTP_CONFLICT, SVN_ERR_INCORRECT_PARAMS, "Only one activity may be specified within " - "the CHECKOUT.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "the CHECKOUT"); } serr = dav_svn__simple_parse_uri(&parse, resource, @@ -600,21 +591,17 @@ dav_svn__checkout(dav_resource *resource, } if (parse.activity_id == NULL) { - return dav_svn__new_error_tag(resource->pool, HTTP_CONFLICT, + return dav_svn__new_error_svn(resource->pool, HTTP_CONFLICT, SVN_ERR_INCORRECT_PARAMS, - "The provided href is not an activity URI.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "The provided href is not an activity URI"); } if ((txn_name = dav_svn__get_txn(resource->info->repos, parse.activity_id)) == NULL) { - return dav_svn__new_error_tag(resource->pool, HTTP_CONFLICT, + return dav_svn__new_error_svn(resource->pool, HTTP_CONFLICT, SVN_ERR_APMOD_ACTIVITY_NOT_FOUND, - "The specified activity does not exist.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "The specified activity does not exist"); } /* verify the specified version resource is the "latest", thus allowing @@ -642,13 +629,11 @@ dav_svn__checkout(dav_resource *resource, if (resource->info->root.rev != youngest) { - return dav_svn__new_error_tag(resource->pool, HTTP_CONFLICT, + return dav_svn__new_error_svn(resource->pool, HTTP_CONFLICT, SVN_ERR_APMOD_BAD_BASELINE, "The specified baseline is not the " "latest baseline, so it may not be " - "checked out.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "checked out"); } /* ### hmm. what if the transaction root's revision is different @@ -742,11 +727,9 @@ dav_svn__checkout(dav_resource *resource, { /* The item being modified is older than the one in the transaction. The client is out of date. */ - return dav_svn__new_error_tag + return dav_svn__new_error_svn (resource->pool, HTTP_CONFLICT, SVN_ERR_FS_CONFLICT, - "resource out of date; try updating", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "resource out of date; try updating"); } else if (resource->info->root.rev > txn_created_rev) { @@ -754,42 +737,25 @@ dav_svn__checkout(dav_resource *resource, revision than the one in the transaction. We'll check to see if they are still the same node, and if not, return an error. */ - const svn_fs_id_t *url_noderev_id, *txn_noderev_id; - - if ((serr = svn_fs_node_id(&txn_noderev_id, txn_root, - resource->info->repos_path, - resource->pool))) + svn_fs_node_relation_t node_relation; + if ((serr = svn_fs_node_relation(&node_relation, txn_root, + resource->info->repos_path, + resource->info->root.root, + resource->info->repos_path, + resource->pool))) { - err = dav_svn__new_error_tag + err = dav_svn__new_error_svn (resource->pool, HTTP_CONFLICT, serr->apr_err, "Unable to fetch the node revision id of the version " - "resource within the transaction.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "resource within the revision"); svn_error_clear(serr); return err; } - if ((serr = svn_fs_node_id(&url_noderev_id, - resource->info->root.root, - resource->info->repos_path, - resource->pool))) + if (node_relation != svn_fs_node_unchanged) { - err = dav_svn__new_error_tag - (resource->pool, HTTP_CONFLICT, serr->apr_err, - "Unable to fetch the node revision id of the version " - "resource within the revision.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); - svn_error_clear(serr); - return err; - } - if (svn_fs_compare_ids(url_noderev_id, txn_noderev_id) != 0) - { - return dav_svn__new_error_tag + return dav_svn__new_error_svn (resource->pool, HTTP_CONFLICT, SVN_ERR_FS_CONFLICT, - "version resource newer than txn (restart the commit)", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "version resource newer than txn (restart the commit)"); } } } @@ -806,11 +772,9 @@ static dav_error * uncheckout(dav_resource *resource) { if (resource->type != DAV_RESOURCE_TYPE_WORKING) - return dav_svn__new_error_tag(resource->pool, HTTP_INTERNAL_SERVER_ERROR, + return dav_svn__new_error_svn(resource->pool, HTTP_INTERNAL_SERVER_ERROR, SVN_ERR_UNSUPPORTED_FEATURE, - "UNCHECKOUT called on non-working resource.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "UNCHECKOUT called on non-working resource"); /* Try to abort the txn if it exists; but don't try too hard. :-) */ if (resource->info->root.txn) @@ -877,7 +841,7 @@ cleanup_deltify(void *data) subpool, then destroy it before exiting. */ apr_pool_t *subpool = svn_pool_create(cdb->pool); - err = svn_repos_open2(&repos, cdb->repos_path, NULL, subpool); + err = svn_repos_open3(&repos, cdb->repos_path, NULL, subpool, subpool); if (err) { ap_log_perror(APLOG_MARK, APLOG_ERR, err->apr_err, cdb->pool, @@ -949,11 +913,9 @@ dav_svn__checkin(dav_resource *resource, txn? Many txns? Etc.) */ if (resource->type != DAV_RESOURCE_TYPE_WORKING) - return dav_svn__new_error_tag(resource->pool, HTTP_INTERNAL_SERVER_ERROR, + return dav_svn__new_error_svn(resource->pool, HTTP_INTERNAL_SERVER_ERROR, SVN_ERR_UNSUPPORTED_FEATURE, - "CHECKIN called on non-working resource.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "CHECKIN called on non-working resource"); /* If the global autoversioning activity still exists, that means nobody's committed it yet. */ @@ -1010,7 +972,7 @@ dav_svn__checkin(dav_resource *resource, { const char *post_commit_err = svn_repos__post_commit_error_str (serr, resource->pool); - ap_log_perror(APLOG_MARK, APLOG_ERR, serr->apr_err, + ap_log_perror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, resource->pool, "commit of r%ld succeeded, but an error occurred " "after the commit: '%s'", @@ -1079,7 +1041,7 @@ dav_svn__checkin(dav_resource *resource, uri = dav_svn__build_uri(resource->info->repos, DAV_SVN__BUILD_URI_VERSION, new_rev, resource->info->repos_path, - 0, resource->pool); + FALSE /* add_href */, resource->pool); err = dav_svn__create_version_resource(version_resource, uri, resource->pool); @@ -1130,12 +1092,16 @@ static dav_error * deliver_report(request_rec *r, const dav_resource *resource, const apr_xml_doc *doc, - ap_filter_t *output) + ap_filter_t *unused) { int ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE); if (doc->root->ns == ns) { + dav_svn__output *output; + + output = dav_svn__output_create(resource->info->r, resource->pool); + /* ### note that these report names should have symbols... */ if (strcmp(doc->root->name, "update-report") == 0) @@ -1188,11 +1154,9 @@ deliver_report(request_rec *r, } /* ### what is a good error for an unknown report? */ - return dav_svn__new_error_tag(resource->pool, HTTP_NOT_IMPLEMENTED, + return dav_svn__new_error_svn(resource->pool, HTTP_NOT_IMPLEMENTED, SVN_ERR_UNSUPPORTED_FEATURE, - "The requested report is unknown.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "The requested report is unknown"); } @@ -1219,13 +1183,11 @@ make_activity(dav_resource *resource) /* sanity check: make sure the resource is a valid activity, in case an older mod_dav doesn't do the check for us. */ if (! can_be_activity(resource)) - return dav_svn__new_error_tag(resource->pool, HTTP_FORBIDDEN, + return dav_svn__new_error_svn(resource->pool, HTTP_FORBIDDEN, SVN_ERR_APMOD_MALFORMED_URI, "Activities cannot be created at that " "location; query the " - "DAV:activity-collection-set property.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "DAV:activity-collection-set property"); err = dav_svn__create_txn(resource->info->repos, &txn_name, NULL, resource->pool); @@ -1396,6 +1358,23 @@ dav_svn__push_locks(dav_resource *resource, return NULL; } +/* Implements svn_fs_lock_callback_t. */ +static svn_error_t * +unlock_many_cb(void *lock_baton, + const char *path, + const svn_lock_t *lock, + svn_error_t *fs_err, + apr_pool_t *pool) +{ + request_rec *r = lock_baton; + + if (fs_err) + ap_log_rerror(APLOG_MARK, APLOG_ERR, fs_err->apr_err, r, + "%s", fs_err->message); + + return SVN_NO_ERROR; +} + /* Helper for merge(). Free every lock in LOCKS. The locks live in REPOS. Log any errors for REQUEST. Use POOL for temporary @@ -1406,28 +1385,16 @@ release_locks(apr_hash_t *locks, request_rec *r, apr_pool_t *pool) { - apr_hash_index_t *hi; - const void *key; - void *val; apr_pool_t *subpool = svn_pool_create(pool); svn_error_t *err; - for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi)) - { - svn_pool_clear(subpool); - apr_hash_this(hi, &key, NULL, &val); - - /* The lock may be stolen or broken sometime between - svn_fs_commit_txn() and this post-commit cleanup. So ignore - any errors from this command; just free as many locks as we can. */ - err = svn_repos_fs_unlock(repos, key, val, FALSE, subpool); + err = svn_repos_fs_unlock_many(repos, locks, FALSE, unlock_many_cb, r, + subpool, subpool); - if (err) /* If we got an error, just log it and move along. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, err->apr_err, r, - "%s", err->message); - - svn_error_clear(err); - } + if (err) /* If we got an error, just log it and move along. */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, err->apr_err, r, + "%s", err->message); + svn_error_clear(err); svn_pool_destroy(subpool); @@ -1441,7 +1408,7 @@ merge(dav_resource *target, int no_auto_merge, int no_checkout, apr_xml_elem *prop_elem, - ap_filter_t *output) + ap_filter_t *unused) { apr_pool_t *pool; dav_error *err; @@ -1452,6 +1419,7 @@ merge(dav_resource *target, svn_revnum_t new_rev; apr_hash_t *locks; svn_boolean_t disable_merge_response = FALSE; + dav_svn__output *output; /* We'll use the target's pool for our operation. We happen to know that it matches the request pool, which (should) have the proper lifetime. */ @@ -1464,22 +1432,18 @@ merge(dav_resource *target, || (source->type == DAV_RESOURCE_TYPE_PRIVATE && source->info->restype == DAV_SVN_RESTYPE_TXN_COLLECTION))) { - return dav_svn__new_error_tag(pool, HTTP_METHOD_NOT_ALLOWED, + return dav_svn__new_error_svn(pool, HTTP_METHOD_NOT_ALLOWED, SVN_ERR_INCORRECT_PARAMS, "MERGE can only be performed using an " "activity or transaction resource as the " - "source.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "source"); } if (! source->exists) { - return dav_svn__new_error_tag(pool, HTTP_METHOD_NOT_ALLOWED, + return dav_svn__new_error_svn(pool, HTTP_METHOD_NOT_ALLOWED, SVN_ERR_INCORRECT_PARAMS, "MERGE activity or transaction resource " - "does not exist.", - SVN_DAV_ERROR_NAMESPACE, - SVN_DAV_ERROR_TAG); + "does not exist"); } /* Before attempting the final commit, we need to push any incoming @@ -1528,6 +1492,11 @@ merge(dav_resource *target, ### client some other way than hijacking the post-commit ### error message.*/ post_commit_err = svn_repos__post_commit_error_str(serr, pool); + ap_log_perror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, pool, + "commit of r%ld succeeded, but an error occurred " + "after the commit: '%s'", + new_rev, + post_commit_err); svn_error_clear(serr); serr = SVN_NO_ERROR; } @@ -1620,6 +1589,7 @@ merge(dav_resource *target, } /* process the response for the new revision. */ + output = dav_svn__output_create(target->info->r, pool); return dav_svn__merge_response(output, source->info->repos, new_rev, post_commit_err, prop_elem, disable_merge_response, pool); |