/* ### FIXME: Consider somehow sharing code with ### libsvn_repos/load-fs-vtable.c:prefix_mergeinfo_paths() */ static svn_error_t * prefix_mergeinfo_paths(svn_string_t **mergeinfo_val, const svn_string_t *mergeinfo_orig, const char *parent_dir, apr_pool_t *pool) { apr_hash_t *prefixed_mergeinfo, *mergeinfo; apr_hash_index_t *hi; void *rangelist; SVN_ERR(svn_mergeinfo_parse(&mergeinfo, mergeinfo_orig->data, pool)); prefixed_mergeinfo = apr_hash_make(pool); for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi)) { const void *key; const char *path, *merge_source; apr_hash_this(hi, &key, NULL, &rangelist); merge_source = svn_relpath_canonicalize(key, pool); /* The svn:mergeinfo property syntax demands a repos abspath */ path = svn_fspath__canonicalize(svn_relpath_join(parent_dir, merge_source, pool), pool); svn_hash_sets(prefixed_mergeinfo, path, rangelist); } return svn_mergeinfo_to_string(mergeinfo_val, prefixed_mergeinfo, pool); }
dav_error * dav_svn__get_mergeinfo_report(const dav_resource *resource, const apr_xml_doc *doc, ap_filter_t *output) { svn_error_t *serr; dav_error *derr = NULL; apr_xml_elem *child; svn_mergeinfo_catalog_t catalog; svn_boolean_t include_descendants = FALSE; dav_svn__authz_read_baton arb; const dav_svn_repos *repos = resource->info->repos; int ns; apr_bucket_brigade *bb; apr_hash_index_t *hi; /* These get determined from the request document. */ svn_revnum_t rev = SVN_INVALID_REVNUM; /* By default look for explicit mergeinfo only. */ svn_mergeinfo_inheritance_t inherit = svn_mergeinfo_explicit; apr_array_header_t *paths = apr_array_make(resource->pool, 0, sizeof(const char *)); /* Sanity check. */ if (!resource->info->repos_path) return dav_svn__new_error(resource->pool, HTTP_BAD_REQUEST, 0, "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, "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); } for (child = doc->root->first_child; child != NULL; child = child->next) { /* if this element isn't one of ours, then skip it */ if (child->ns != ns) continue; if (strcmp(child->name, SVN_DAV__REVISION) == 0) rev = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1)); else if (strcmp(child->name, SVN_DAV__INHERIT) == 0) { inherit = svn_inheritance_from_word( dav_xml_get_cdata(child, resource->pool, 1)); } else if (strcmp(child->name, SVN_DAV__PATH) == 0) { const char *target; const char *rel_path = dav_xml_get_cdata(child, resource->pool, 0); if ((derr = dav_svn__test_canonical(rel_path, resource->pool))) return derr; /* Force REL_PATH to be a relative path, not an fspath. */ rel_path = svn_relpath_canonicalize(rel_path, resource->pool); /* Append the REL_PATH to the base FS path to get an absolute repository path. */ target = svn_fspath__join(resource->info->repos_path, rel_path, resource->pool); (*((const char **)(apr_array_push(paths)))) = target; } else if (strcmp(child->name, SVN_DAV__INCLUDE_DESCENDANTS) == 0) { const char *word = dav_xml_get_cdata(child, resource->pool, 1); if (strcmp(word, "yes") == 0) include_descendants = TRUE; /* Else the client isn't supposed to send anyway, so just leave it false. */ } /* else unknown element; skip it */ } /* Build authz read baton */ arb.r = resource->info->r; arb.repos = resource->info->repos; /* Build mergeinfo brigade */ bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); serr = svn_repos_fs_get_mergeinfo(&catalog, repos->repos, paths, rev, inherit, include_descendants, dav_svn__authz_read_func(&arb), &arb, resource->pool); if (serr) { derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, serr->message, resource->pool); goto cleanup; } serr = svn_mergeinfo__remove_prefix_from_catalog(&catalog, catalog, resource->info->repos_path, resource->pool); if (serr) { derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, serr->message, resource->pool); goto cleanup; } /* Ideally, dav_svn__brigade_printf() would set a flag in bb (or rather, in r->sent_bodyct, see dav_method_report()), and ap_fflush() would not set that flag unless it actually sent something. But we are condemned to live in another universe, so we must keep track ourselves of whether we've sent anything or not. See the long comment after the 'cleanup' label for more details. */ serr = dav_svn__brigade_puts(bb, output, DAV_XML_HEADER DEBUG_CR "<S:" SVN_DAV__MERGEINFO_REPORT " " "xmlns:S=\"" SVN_XML_NAMESPACE "\" " "xmlns:D=\"DAV:\">" DEBUG_CR); if (serr) { derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, serr->message, resource->pool); goto cleanup; } for (hi = apr_hash_first(resource->pool, catalog); hi; hi = apr_hash_next(hi)) { const void *key; void *value; const char *path; svn_mergeinfo_t mergeinfo; svn_string_t *mergeinfo_string; apr_hash_this(hi, &key, NULL, &value); path = key; mergeinfo = value; serr = svn_mergeinfo_to_string(&mergeinfo_string, mergeinfo, resource->pool); if (serr) { derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Error ending REPORT response.", resource->pool); goto cleanup; } serr = dav_svn__brigade_printf (bb, output, "<S:" SVN_DAV__MERGEINFO_ITEM ">" DEBUG_CR "<S:" SVN_DAV__MERGEINFO_PATH ">%s</S:" SVN_DAV__MERGEINFO_PATH ">" DEBUG_CR "<S:" SVN_DAV__MERGEINFO_INFO ">%s</S:" SVN_DAV__MERGEINFO_INFO ">" DEBUG_CR "</S:" SVN_DAV__MERGEINFO_ITEM ">", apr_xml_quote_string(resource->pool, path, 0), apr_xml_quote_string(resource->pool, mergeinfo_string->data, 0)); if (serr) { derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Error ending REPORT response.", resource->pool); goto cleanup; } } if ((serr = dav_svn__brigade_puts(bb, output, "</S:" SVN_DAV__MERGEINFO_REPORT ">" DEBUG_CR))) { derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Error ending REPORT response.", resource->pool); goto cleanup; } cleanup: /* We've detected a 'high level' svn action to log. */ dav_svn__operational_log(resource->info, svn_log__get_mergeinfo(paths, inherit, include_descendants, resource->pool)); return dav_svn__final_flush_or_error(resource->info->r, bb, output, derr, resource->pool); }
/* ### FIXME: Consider somehow sharing code with ### libsvn_repos/load-fs-vtable.c:renumber_mergeinfo_revs() */ static svn_error_t * renumber_mergeinfo_revs(svn_string_t **final_val, const svn_string_t *initial_val, struct revision_baton *rb, apr_pool_t *pool) { apr_pool_t *subpool = svn_pool_create(pool); svn_mergeinfo_t mergeinfo, predates_stream_mergeinfo; svn_mergeinfo_t final_mergeinfo = apr_hash_make(subpool); apr_hash_index_t *hi; SVN_ERR(svn_mergeinfo_parse(&mergeinfo, initial_val->data, subpool)); /* Issue #3020 http://subversion.tigris.org/issues/show_bug.cgi?id=3020#desc16 Remove mergeinfo older than the oldest revision in the dump stream and adjust its revisions by the difference between the head rev of the target repository and the current dump stream rev. */ if (rb->pb->oldest_dumpstream_rev > 1) { SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges( &predates_stream_mergeinfo, mergeinfo, rb->pb->oldest_dumpstream_rev - 1, 0, TRUE, subpool, subpool)); SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges( &mergeinfo, mergeinfo, rb->pb->oldest_dumpstream_rev - 1, 0, FALSE, subpool, subpool)); SVN_ERR(svn_mergeinfo__adjust_mergeinfo_rangelists( &predates_stream_mergeinfo, predates_stream_mergeinfo, -rb->rev_offset, subpool, subpool)); } else { predates_stream_mergeinfo = NULL; } for (hi = apr_hash_first(subpool, mergeinfo); hi; hi = apr_hash_next(hi)) { svn_rangelist_t *rangelist; struct parse_baton *pb = rb->pb; int i; const void *path; apr_ssize_t pathlen; void *val; apr_hash_this(hi, &path, &pathlen, &val); rangelist = val; /* Possibly renumber revisions in merge source's rangelist. */ for (i = 0; i < rangelist->nelts; i++) { svn_revnum_t rev_from_map; svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); rev_from_map = get_revision_mapping(pb->rev_map, range->start); if (SVN_IS_VALID_REVNUM(rev_from_map)) { range->start = rev_from_map; } else if (range->start == pb->oldest_dumpstream_rev - 1) { /* Since the start revision of svn_merge_range_t are not inclusive there is one possible valid start revision that won't be found in the PB->REV_MAP mapping of load stream revsions to loaded revisions: The revision immediately preceeding the oldest revision from the load stream. This is a valid revision for mergeinfo, but not a valid copy from revision (which PB->REV_MAP also maps for) so it will never be in the mapping. If that is what we have here, then find the mapping for the oldest rev from the load stream and subtract 1 to get the renumbered, non-inclusive, start revision. */ rev_from_map = get_revision_mapping(pb->rev_map, pb->oldest_dumpstream_rev); if (SVN_IS_VALID_REVNUM(rev_from_map)) range->start = rev_from_map - 1; } else { /* If we can't remap the start revision then don't even bother trying to remap the end revision. It's possible we might actually succeed at the latter, which can result in invalid mergeinfo with a start rev > end rev. If that gets into the repository then a world of bustage breaks loose anytime that bogus mergeinfo is parsed. See http://subversion.tigris.org/issues/show_bug.cgi?id=3020#desc16. */ continue; } rev_from_map = get_revision_mapping(pb->rev_map, range->end); if (SVN_IS_VALID_REVNUM(rev_from_map)) range->end = rev_from_map; } apr_hash_set(final_mergeinfo, path, pathlen, rangelist); } if (predates_stream_mergeinfo) { SVN_ERR(svn_mergeinfo_merge2(final_mergeinfo, predates_stream_mergeinfo, subpool, subpool)); } SVN_ERR(svn_mergeinfo__canonicalize_ranges(final_mergeinfo, subpool)); SVN_ERR(svn_mergeinfo_to_string(final_val, final_mergeinfo, pool)); svn_pool_destroy(subpool); return SVN_NO_ERROR; }
static svn_error_t * change_dir_prop(void *dir_baton, const char *name, const svn_string_t *value, apr_pool_t *pool) { node_baton_t *db = dir_baton; edit_baton_t *eb = db->edit_baton; /* Only regular properties can pass over libsvn_ra */ if (svn_property_kind2(name) != svn_prop_regular_kind) return SVN_NO_ERROR; /* Maybe drop svn:mergeinfo. */ if (eb->strip_mergeinfo && (strcmp(name, SVN_PROP_MERGEINFO) == 0)) { eb->mergeinfo_stripped = TRUE; return SVN_NO_ERROR; } /* Maybe convert svnmerge-integrated data into svn:mergeinfo. (We ignore svnmerge-blocked for now.) */ /* ### FIXME: Consult the mirror repository's HEAD prop values and ### merge svn:mergeinfo, svnmerge-integrated, and svnmerge-blocked. */ if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-integrated") == 0)) { if (value) { /* svnmerge-integrated differs from svn:mergeinfo in a pair of ways. First, it can use tabs, newlines, or spaces to delimit source information. Secondly, the source paths are relative URLs, whereas svn:mergeinfo uses relative paths (not URI-encoded). */ svn_error_t *err; svn_stringbuf_t *mergeinfo_buf = svn_stringbuf_create_empty(pool); svn_mergeinfo_t mergeinfo; int i; apr_array_header_t *sources = svn_cstring_split(value->data, " \t\n", TRUE, pool); svn_string_t *new_value; for (i = 0; i < sources->nelts; i++) { const char *rel_path; apr_array_header_t *path_revs = svn_cstring_split(APR_ARRAY_IDX(sources, i, const char *), ":", TRUE, pool); /* ### TODO: Warn? */ if (path_revs->nelts != 2) continue; /* Append this source's mergeinfo data. */ rel_path = APR_ARRAY_IDX(path_revs, 0, const char *); rel_path = svn_path_uri_decode(rel_path, pool); svn_stringbuf_appendcstr(mergeinfo_buf, rel_path); svn_stringbuf_appendcstr(mergeinfo_buf, ":"); svn_stringbuf_appendcstr(mergeinfo_buf, APR_ARRAY_IDX(path_revs, 1, const char *)); svn_stringbuf_appendcstr(mergeinfo_buf, "\n"); } /* Try to parse the mergeinfo string we've created, just to check for bogosity. If all goes well, we'll unparse it again and use that as our property value. */ err = svn_mergeinfo_parse(&mergeinfo, mergeinfo_buf->data, pool); if (err) { svn_error_clear(err); return SVN_NO_ERROR; } SVN_ERR(svn_mergeinfo_to_string(&new_value, mergeinfo, pool)); value = new_value; } name = SVN_PROP_MERGEINFO; eb->svnmerge_migrated = TRUE; } /* Remember if we see any svnmerge-blocked properties. */ if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-blocked") == 0)) { eb->svnmerge_blocked = TRUE; } /* Normalize svn:* properties as necessary. */ if (svn_prop_needs_translation(name)) { svn_boolean_t was_normalized; svn_boolean_t mergeinfo_tweaked = FALSE; /* Normalize encoding to UTF-8, and EOL style to LF. */ SVN_ERR(normalize_string(&value, &was_normalized, eb->source_prop_encoding, pool, pool)); /* Maybe adjust svn:mergeinfo. */ if (value && strcmp(name, SVN_PROP_MERGEINFO) == 0) { SVN_ERR(remove_r0_mergeinfo(&value, &mergeinfo_tweaked, pool, pool)); if (mergeinfo_tweaked) eb->mergeinfo_tweaked = TRUE; } if (was_normalized) (*(eb->normalized_node_props_counter))++; } return eb->wrapped_editor->change_dir_prop(db->wrapped_node_baton, name, value, pool); }