static svn_error_t * open_directory(const char *path, void *parent_baton, svn_revnum_t base_revision, apr_pool_t *pool, void **child_baton) { struct dir_baton *pb = parent_baton; struct edit_baton *eb = pb->edit_baton; svn_node_kind_t kind; const char *full_path; full_path = svn_fspath__join(eb->base_path, svn_relpath_canonicalize(path, pool), pool); /* Check PATH in our transaction. If it does not exist, return a 'Path not present' error. */ SVN_ERR(svn_fs_check_path(&kind, eb->txn_root, full_path, pool)); if (kind == svn_node_none) return svn_error_createf(SVN_ERR_FS_NOT_DIRECTORY, NULL, _("Path '%s' not present"), path); /* Build a new dir baton for this directory. */ *child_baton = make_dir_baton(eb, pb, full_path, pb->was_copied, base_revision, pool); return SVN_NO_ERROR; }
/* Make a new entry named NAME in PARENT. If IS_DIR is true, then the node revision the new entry points to will be a directory, else it will be a file. The new node will be allocated in POOL. PARENT must be mutable, and must not have an entry named NAME. Use POOL for all allocations, except caching the node_revision in PARENT. */ static svn_error_t * make_entry(dag_node_t **child_p, dag_node_t *parent, const char *parent_path, const char *name, svn_boolean_t is_dir, const svn_fs_x__id_part_t *txn_id, apr_pool_t *pool) { const svn_fs_id_t *new_node_id; node_revision_t new_noderev, *parent_noderev; /* Make sure that NAME is a single path component. */ if (! svn_path_is_single_path_component(name)) return svn_error_createf (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL, _("Attempted to create a node with an illegal name '%s'"), name); /* Make sure that parent is a directory */ if (parent->kind != svn_node_dir) return svn_error_create (SVN_ERR_FS_NOT_DIRECTORY, NULL, _("Attempted to create entry in non-directory parent")); /* Check that the parent is mutable. */ if (! svn_fs_x__dag_check_mutable(parent)) return svn_error_createf (SVN_ERR_FS_NOT_MUTABLE, NULL, _("Attempted to clone child of non-mutable node")); /* Create the new node's NODE-REVISION */ memset(&new_noderev, 0, sizeof(new_noderev)); new_noderev.kind = is_dir ? svn_node_dir : svn_node_file; new_noderev.created_path = svn_fspath__join(parent_path, name, pool); SVN_ERR(get_node_revision(&parent_noderev, parent)); new_noderev.copyroot_path = apr_pstrdup(pool, parent_noderev->copyroot_path); new_noderev.copyroot_rev = parent_noderev->copyroot_rev; new_noderev.copyfrom_rev = SVN_INVALID_REVNUM; new_noderev.copyfrom_path = NULL; SVN_ERR(svn_fs_x__create_node (&new_node_id, svn_fs_x__dag_get_fs(parent), &new_noderev, svn_fs_x__id_copy_id(svn_fs_x__dag_get_id(parent)), txn_id, pool)); /* Create a new dag_node_t for our new node */ SVN_ERR(svn_fs_x__dag_get_node(child_p, svn_fs_x__dag_get_fs(parent), new_node_id, pool)); /* We can safely call set_entry because we already know that PARENT is mutable, and we just created CHILD, so we know it has no ancestors (therefore, PARENT cannot be an ancestor of CHILD) */ return set_entry(parent, name, svn_fs_x__dag_get_id(*child_p), new_noderev.kind, txn_id, pool); }
svn_error_t * svn_fs_x__dag_copy(dag_node_t *to_node, const char *entry, dag_node_t *from_node, svn_boolean_t preserve_history, svn_revnum_t from_rev, const char *from_path, const svn_fs_x__id_part_t *txn_id, apr_pool_t *pool) { const svn_fs_id_t *id; if (preserve_history) { node_revision_t *from_noderev, *to_noderev; svn_fs_x__id_part_t copy_id; const svn_fs_id_t *src_id = svn_fs_x__dag_get_id(from_node); svn_fs_t *fs = svn_fs_x__dag_get_fs(from_node); /* Make a copy of the original node revision. */ SVN_ERR(get_node_revision(&from_noderev, from_node)); to_noderev = copy_node_revision(from_noderev, pool); /* Reserve a copy ID for this new copy. */ SVN_ERR(svn_fs_x__reserve_copy_id(©_id, fs, txn_id, pool)); /* Create a successor with its predecessor pointing at the copy source. */ to_noderev->predecessor_id = svn_fs_x__id_copy(src_id, pool); if (to_noderev->predecessor_count != -1) to_noderev->predecessor_count++; to_noderev->created_path = svn_fspath__join(svn_fs_x__dag_get_created_path(to_node), entry, pool); to_noderev->copyfrom_path = apr_pstrdup(pool, from_path); to_noderev->copyfrom_rev = from_rev; /* Set the copyroot equal to our own id. */ to_noderev->copyroot_path = NULL; SVN_ERR(svn_fs_x__create_successor(&id, fs, src_id, to_noderev, ©_id, txn_id, pool)); } else /* don't preserve history */ { id = svn_fs_x__dag_get_id(from_node); } /* Set the entry in to_node to the new id. */ return svn_fs_x__dag_set_entry(to_node, entry, id, from_node->kind, txn_id, pool); }
static svn_error_t * delete_entry(const char *path, svn_revnum_t revision, void *parent_baton, apr_pool_t *pool) { struct dir_baton *parent = parent_baton; struct edit_baton *eb = parent->edit_baton; svn_node_kind_t kind; svn_revnum_t cr_rev; svn_repos_authz_access_t required = svn_authz_write; const char *full_path; full_path = svn_fspath__join(eb->base_path, svn_relpath_canonicalize(path, pool), pool); /* Check PATH in our transaction. */ SVN_ERR(svn_fs_check_path(&kind, eb->txn_root, full_path, pool)); /* Deletion requires a recursive write access, as well as write access to the parent directory. */ if (kind == svn_node_dir) required |= svn_authz_recursive; SVN_ERR(check_authz(eb, full_path, eb->txn_root, required, pool)); SVN_ERR(check_authz(eb, parent->path, eb->txn_root, svn_authz_write, pool)); /* If PATH doesn't exist in the txn, the working copy is out of date. */ if (kind == svn_node_none) return out_of_date(full_path, kind); /* Now, make sure we're deleting the node we *think* we're deleting, else return an out-of-dateness error. */ SVN_ERR(svn_fs_node_created_rev(&cr_rev, eb->txn_root, full_path, pool)); if (SVN_IS_VALID_REVNUM(revision) && (revision < cr_rev)) return out_of_date(full_path, kind); /* This routine is a mindless wrapper. We call svn_fs_delete() because that will delete files and recursively delete directories. */ return svn_fs_delete(eb->txn_root, full_path, pool); }
static svn_error_t * open_file(const char *path, void *parent_baton, svn_revnum_t base_revision, apr_pool_t *pool, void **file_baton) { struct file_baton *new_fb; struct dir_baton *pb = parent_baton; struct edit_baton *eb = pb->edit_baton; svn_revnum_t cr_rev; apr_pool_t *subpool = svn_pool_create(pool); const char *full_path; full_path = svn_fspath__join(eb->base_path, svn_relpath_canonicalize(path, pool), pool); /* Check for read authorization. */ SVN_ERR(check_authz(eb, full_path, eb->txn_root, svn_authz_read, subpool)); /* Get this node's creation revision (doubles as an existence check). */ SVN_ERR(svn_fs_node_created_rev(&cr_rev, eb->txn_root, full_path, subpool)); /* If the node our caller has is an older revision number than the one in our transaction, return an out-of-dateness error. */ if (SVN_IS_VALID_REVNUM(base_revision) && (base_revision < cr_rev)) return out_of_date(full_path, svn_node_file); /* Build a new file baton */ new_fb = apr_pcalloc(pool, sizeof(*new_fb)); new_fb->edit_baton = eb; new_fb->path = full_path; *file_baton = new_fb; /* Destory the work subpool. */ svn_pool_destroy(subpool); return SVN_NO_ERROR; }
svn_error_t * svn_fs__append_to_merged_froms(svn_mergeinfo_t *output, svn_mergeinfo_t input, const char *rel_path, apr_pool_t *pool) { apr_hash_index_t *hi; *output = apr_hash_make(pool); for (hi = apr_hash_first(pool, input); hi; hi = apr_hash_next(hi)) { const char *path = apr_hash_this_key(hi); svn_rangelist_t *rangelist = apr_hash_this_val(hi); svn_hash_sets(*output, svn_fspath__join(path, rel_path, pool), svn_rangelist_dup(rangelist, pool)); } return SVN_NO_ERROR; }
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); }
dav_error * dav_svn__get_inherited_props_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; apr_array_header_t *inherited_props; dav_svn__authz_read_baton arb; int ns; apr_bucket_brigade *bb; const char *path = "/"; svn_fs_root_t *root; int i; svn_revnum_t rev = SVN_INVALID_REVNUM; apr_pool_t *iterpool; /* 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_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"); } iterpool = svn_pool_create(resource->pool); 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, iterpool, 1)); } else if (strcmp(child->name, SVN_DAV__PATH) == 0) { path = dav_xml_get_cdata(child, resource->pool, 0); if ((derr = dav_svn__test_canonical(path, iterpool))) return derr; path = svn_fspath__join(resource->info->repos_path, path, resource->pool); } /* else unknown element; skip it */ } /* Build authz read baton */ arb.r = resource->info->r; arb.repos = resource->info->repos; /* Build inherited property brigade */ bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); serr = svn_fs_revision_root(&root, resource->info->repos->fs, rev, resource->pool); if (serr != NULL) return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "couldn't retrieve revision root", resource->pool); serr = svn_repos_fs_get_inherited_props(&inherited_props, root, path, NULL, dav_svn__authz_read_func(&arb), &arb, resource->pool, iterpool); if (serr) { derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, NULL, resource->pool); goto cleanup; } serr = dav_svn__brigade_puts(bb, output, DAV_XML_HEADER DEBUG_CR "<S:" SVN_DAV__INHERITED_PROPS_REPORT " " "xmlns:S=\"" SVN_XML_NAMESPACE "\" " "xmlns:D=\"DAV:\">" DEBUG_CR); if (serr) { derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, NULL, resource->pool); goto cleanup; } for (i = 0; i < inherited_props->nelts; i++) { svn_prop_inherited_item_t *elt = APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *); svn_pool_clear(iterpool); serr = dav_svn__brigade_printf( bb, output, "<S:" SVN_DAV__IPROP_ITEM ">" DEBUG_CR "<S:" SVN_DAV__IPROP_PATH ">%s</S:" SVN_DAV__IPROP_PATH ">" DEBUG_CR, apr_xml_quote_string(resource->pool, elt->path_or_url, 0)); if (!serr) { apr_hash_index_t *hi; for (hi = apr_hash_first(resource->pool, elt->prop_hash); hi; hi = apr_hash_next(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( bb, output, "<S:" SVN_DAV__IPROP_PROPNAME ">%s</S:" SVN_DAV__IPROP_PROPNAME ">" DEBUG_CR, apr_xml_quote_string(iterpool, propname, 0)); if (!serr) { if (svn_xml_is_xml_safe(propval->data, propval->len)) { svn_stringbuf_t *tmp = NULL; svn_xml_escape_cdata_string(&tmp, propval, iterpool); xml_safe = tmp->data; serr = dav_svn__brigade_printf( bb, output, "<S:" SVN_DAV__IPROP_PROPVAL ">%s</S:" SVN_DAV__IPROP_PROPVAL ">" DEBUG_CR, xml_safe); } else { xml_safe = svn_base64_encode_string2( propval, TRUE, iterpool)->data; serr = dav_svn__brigade_printf( bb, output, "<S:" SVN_DAV__IPROP_PROPVAL " encoding=\"base64\"" ">%s</S:" SVN_DAV__IPROP_PROPVAL ">" DEBUG_CR, xml_safe); } } if (serr) break; } if (!serr) serr = dav_svn__brigade_printf(bb, output, "</S:" SVN_DAV__IPROP_ITEM ">" DEBUG_CR); } 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__INHERITED_PROPS_REPORT ">" DEBUG_CR))) { derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Error ending REPORT response.", resource->pool); goto cleanup; } cleanup: /* Log this 'high level' svn action. */ dav_svn__operational_log(resource->info, svn_log__get_inherited_props(path, rev, resource->pool)); svn_pool_destroy(iterpool); return dav_svn__final_flush_or_error(resource->info->r, bb, output, derr, resource->pool); }
/* Respond to a client request for a REPORT of type file-revs-report for the RESOURCE. Get request body from DOC and send result to OUTPUT. */ dav_error * dav_svn__file_revs_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; int ns; struct file_rev_baton frb; dav_svn__authz_read_baton arb; const char *abs_path = NULL; /* These get determined from the request document. */ svn_revnum_t start = SVN_INVALID_REVNUM; svn_revnum_t end = SVN_INVALID_REVNUM; svn_boolean_t include_merged_revisions = FALSE; /* off by default */ /* Construct the authz read check baton. */ arb.r = resource->info->r; arb.repos = resource->info->repos; /* 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); /* ### This is done on other places, but the document element is in this namespace, so is this necessary at all? */ 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); } /* Get request information. */ 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, "start-revision") == 0) start = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1)); else if (strcmp(child->name, "end-revision") == 0) end = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1)); else if (strcmp(child->name, "include-merged-revisions") == 0) include_merged_revisions = TRUE; /* presence indicates positivity */ else if (strcmp(child->name, "path") == 0) { 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. */ abs_path = svn_fspath__join(resource->info->repos_path, rel_path, resource->pool); } /* else unknown element; skip it */ } /* 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); frb.bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); frb.output = output; frb.needs_header = TRUE; frb.svndiff_version = resource->info->svndiff_version; frb.compression_level = dav_svn__get_compression_level(resource->info->r); /* file_rev_handler will send header first time it is called. */ /* Get the revisions and send them. */ serr = svn_repos_get_file_revs2(resource->info->repos->repos, abs_path, start, end, include_merged_revisions, dav_svn__authz_read_func(&arb), &arb, file_rev_handler, &frb, resource->pool); if (serr) { /* We don't 'goto cleanup' because ap_fflush() tells httpd to write the HTTP headers out, and that includes whatever r->status is at that particular time. When we call dav_svn__convert_err(), we don't immediately set r->status 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)); } if ((serr = maybe_send_header(&frb))) { derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Error beginning REPORT reponse", resource->pool); goto cleanup; } if ((serr = dav_svn__brigade_puts(frb.bb, frb.output, "</S:file-revs-report>" DEBUG_CR))) { derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Error ending REPORT reponse", resource->pool); goto cleanup; } cleanup: /* We've detected a 'high level' svn action to log. */ dav_svn__operational_log(resource->info, svn_log__get_file_revs(abs_path, start, end, include_merged_revisions, resource->pool)); return dav_svn__final_flush_or_error(resource->info->r, frb.bb, output, derr, resource->pool); }
svn_error_t * svn_fs_x__dag_clone_child(dag_node_t **child_p, dag_node_t *parent, const char *parent_path, const char *name, const svn_fs_x__id_part_t *copy_id, const svn_fs_x__id_part_t *txn_id, svn_boolean_t is_parent_copyroot, apr_pool_t *pool) { dag_node_t *cur_entry; /* parent's current entry named NAME */ const svn_fs_id_t *new_node_id; /* node id we'll put into NEW_NODE */ svn_fs_t *fs = svn_fs_x__dag_get_fs(parent); apr_pool_t *subpool = svn_pool_create(pool); /* First check that the parent is mutable. */ if (! svn_fs_x__dag_check_mutable(parent)) return svn_error_createf (SVN_ERR_FS_NOT_MUTABLE, NULL, "Attempted to clone child of non-mutable node"); /* Make sure that NAME is a single path component. */ if (! svn_path_is_single_path_component(name)) return svn_error_createf (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL, "Attempted to make a child clone with an illegal name '%s'", name); /* Find the node named NAME in PARENT's entries list if it exists. */ SVN_ERR(svn_fs_x__dag_open(&cur_entry, parent, name, pool, subpool)); /* Check for mutability in the node we found. If it's mutable, we don't need to clone it. */ if (svn_fs_x__dag_check_mutable(cur_entry)) { /* This has already been cloned */ new_node_id = cur_entry->id; } else { node_revision_t *noderev, *parent_noderev; /* Go get a fresh NODE-REVISION for current child node. */ SVN_ERR(get_node_revision(&noderev, cur_entry)); if (is_parent_copyroot) { SVN_ERR(get_node_revision(&parent_noderev, parent)); noderev->copyroot_rev = parent_noderev->copyroot_rev; noderev->copyroot_path = apr_pstrdup(pool, parent_noderev->copyroot_path); } noderev->copyfrom_path = NULL; noderev->copyfrom_rev = SVN_INVALID_REVNUM; noderev->predecessor_id = svn_fs_x__id_copy(cur_entry->id, pool); if (noderev->predecessor_count != -1) noderev->predecessor_count++; noderev->created_path = svn_fspath__join(parent_path, name, pool); SVN_ERR(svn_fs_x__create_successor(&new_node_id, fs, cur_entry->id, noderev, copy_id, txn_id, pool)); /* Replace the ID in the parent's ENTRY list with the ID which refers to the mutable clone of this child. */ SVN_ERR(set_entry(parent, name, new_node_id, noderev->kind, txn_id, pool)); } /* Initialize the youngster. */ svn_pool_destroy(subpool); return svn_fs_x__dag_get_node(child_p, fs, new_node_id, pool); }
/* Respond to a S:deleted-rev-report request. */ dav_error * dav_svn__get_deleted_rev_report(const dav_resource *resource, const apr_xml_doc *doc, ap_filter_t *output) { apr_xml_elem *child; int ns; const char *rel_path = NULL; const char *abs_path = NULL; svn_revnum_t peg_rev = SVN_INVALID_REVNUM; svn_revnum_t end_rev = SVN_INVALID_REVNUM; svn_revnum_t deleted_rev; apr_bucket_brigade *bb; svn_error_t *err; apr_status_t apr_err; dav_error *derr = NULL; /* Sanity check. */ 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, "peg-revision") == 0) { peg_rev = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1)); } else if (strcmp(child->name, "end-revision") == 0) { end_rev = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1)); } else if (strcmp(child->name, "path") == 0) { 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 REL_PATH to the base FS path to get an absolute repository path. */ abs_path = svn_fspath__join(resource->info->repos_path, rel_path, resource->pool); } } /* Check that all parameters are present and valid. */ if (! (abs_path && 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); } /* Do what we actually came here for: Find the rev abs_path was deleted. */ err = svn_repos_deleted_rev(resource->info->repos->fs, abs_path, peg_rev, end_rev, &deleted_rev, resource->pool); if (err) { svn_error_clear(err); return dav_svn__new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0, "Could not find revision path was deleted."); } bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); apr_err = ap_fprintf(output, bb, 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), HTTP_INTERNAL_SERVER_ERROR, "Error writing REPORT response.", resource->pool); return dav_svn__final_flush_or_error(resource->info->r, bb, output, derr, resource->pool); }
dav_error * dav_svn__log_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; struct log_receiver_baton lrb; dav_svn__authz_read_baton arb; const dav_svn_repos *repos = resource->info->repos; const char *target = NULL; int limit = 0; int ns; svn_boolean_t seen_revprop_element; /* These get determined from the request document. */ svn_revnum_t start = SVN_INVALID_REVNUM; /* defaults to HEAD */ svn_revnum_t end = SVN_INVALID_REVNUM; /* defaults to HEAD */ 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 = apr_array_make(resource->pool, 1, 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); } /* If this is still FALSE after the loop, we haven't seen either of the revprop elements, meaning a pre-1.5 client; we'll return the standard author/date/log revprops. */ seen_revprop_element = FALSE; lrb.requested_custom_revprops = FALSE; lrb.encode_binary_props = FALSE; 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, "start-revision") == 0) start = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1)); else if (strcmp(child->name, "end-revision") == 0) end = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1)); else if (strcmp(child->name, "limit") == 0) { serr = svn_cstring_atoi(&limit, dav_xml_get_cdata(child, resource->pool, 1)); if (serr) { return dav_svn__convert_err(serr, HTTP_BAD_REQUEST, "Malformed CDATA in element " "\"limit\"", resource->pool); } } else if (strcmp(child->name, "discover-changed-paths") == 0) discover_changed_paths = TRUE; /* presence indicates positivity */ else if (strcmp(child->name, "strict-node-history") == 0) strict_node_history = TRUE; /* presence indicates positivity */ else if (strcmp(child->name, "include-merged-revisions") == 0) include_merged_revisions = TRUE; /* presence indicates positivity */ else if (strcmp(child->name, "encode-binary-props") == 0) lrb.encode_binary_props = TRUE; /* presence indicates positivity */ else if (strcmp(child->name, "all-revprops") == 0) { revprops = NULL; /* presence indicates fetch all revprops */ seen_revprop_element = lrb.requested_custom_revprops = TRUE; } else if (strcmp(child->name, "no-revprops") == 0) { /* presence indicates fetch no revprops */ seen_revprop_element = lrb.requested_custom_revprops = TRUE; } else if (strcmp(child->name, "revprop") == 0) { if (revprops) { /* We're not fetching all revprops, append to fetch list. */ const char *name = dav_xml_get_cdata(child, resource->pool, 0); APR_ARRAY_PUSH(revprops, const char *) = name; if (!lrb.requested_custom_revprops && strcmp(name, SVN_PROP_REVISION_AUTHOR) != 0 && strcmp(name, SVN_PROP_REVISION_DATE) != 0 && strcmp(name, SVN_PROP_REVISION_LOG) != 0) lrb.requested_custom_revprops = TRUE; } seen_revprop_element = TRUE; } else if (strcmp(child->name, "path") == 0) { 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); APR_ARRAY_PUSH(paths, const char *) = target; } /* else unknown element; skip it */ } if (!seen_revprop_element) { /* pre-1.5 client */ APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_AUTHOR; APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_DATE; APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_LOG; } /* Build authz read baton */ arb.r = resource->info->r; arb.repos = resource->info->repos; /* Build log receiver baton */ lrb.bb = apr_brigade_create(resource->pool, /* not the subpool! */ output->c->bucket_alloc); lrb.output = output; lrb.needs_header = TRUE; lrb.stack_depth = 0; /* lrb.requested_custom_revprops set above */ /* 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 flag in our log_receiver_baton structure). */ /* Send zero or more log items. */ serr = svn_repos_get_logs4(repos->repos, paths, start, end, limit, discover_changed_paths, strict_node_history, include_merged_revisions, revprops, dav_svn__authz_read_func(&arb), &arb, log_receiver, &lrb, resource->pool); if (serr) { derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, serr->message, resource->pool); goto cleanup; } if ((serr = maybe_send_header(&lrb))) { derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Error beginning REPORT response.", resource->pool); goto cleanup; } if ((serr = dav_svn__brigade_puts(lrb.bb, lrb.output, "</S:log-report>" DEBUG_CR))) { derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Error ending REPORT response.", resource->pool); goto cleanup; } cleanup: dav_svn__operational_log(resource->info, svn_log__log(paths, start, end, limit, discover_changed_paths, strict_node_history, include_merged_revisions, revprops, resource->pool)); return dav_svn__final_flush_or_error(resource->info->r, lrb.bb, output, derr, resource->pool); }
/* This function is the shared guts of add_file() and add_directory(), which see for the meanings of the parameters. The only extra parameter here is IS_DIR, which is TRUE when adding a directory, and FALSE when adding a file. */ static svn_error_t * add_file_or_directory(const char *path, void *parent_baton, const char *copy_path, svn_revnum_t copy_revision, svn_boolean_t is_dir, apr_pool_t *pool, void **return_baton) { struct dir_baton *pb = parent_baton; struct edit_baton *eb = pb->edit_baton; apr_pool_t *subpool = svn_pool_create(pool); svn_boolean_t was_copied = FALSE; const char *full_path; full_path = svn_fspath__join(eb->base_path, svn_relpath_canonicalize(path, pool), pool); /* Sanity check. */ if (copy_path && (! SVN_IS_VALID_REVNUM(copy_revision))) return svn_error_createf (SVN_ERR_FS_GENERAL, NULL, _("Got source path but no source revision for '%s'"), full_path); if (copy_path) { const char *fs_path; svn_fs_root_t *copy_root; svn_node_kind_t kind; size_t repos_url_len; svn_repos_authz_access_t required; /* Copy requires recursive write access to the destination path and write access to the parent path. */ required = svn_authz_write | (is_dir ? svn_authz_recursive : 0); SVN_ERR(check_authz(eb, full_path, eb->txn_root, required, subpool)); SVN_ERR(check_authz(eb, pb->path, eb->txn_root, svn_authz_write, subpool)); /* Check PATH in our transaction. Make sure it does not exist unless its parent directory was copied (in which case, the thing might have been copied in as well), else return an out-of-dateness error. */ SVN_ERR(svn_fs_check_path(&kind, eb->txn_root, full_path, subpool)); if ((kind != svn_node_none) && (! pb->was_copied)) return out_of_date(full_path, kind); /* For now, require that the url come from the same repository that this commit is operating on. */ copy_path = svn_path_uri_decode(copy_path, subpool); repos_url_len = strlen(eb->repos_url); if (strncmp(copy_path, eb->repos_url, repos_url_len) != 0) return svn_error_createf (SVN_ERR_FS_GENERAL, NULL, _("Source url '%s' is from different repository"), copy_path); fs_path = apr_pstrdup(subpool, copy_path + repos_url_len); /* Now use the "fs_path" as an absolute path within the repository to make the copy from. */ SVN_ERR(svn_fs_revision_root(©_root, eb->fs, copy_revision, subpool)); /* Copy also requires (recursive) read access to the source */ required = svn_authz_read | (is_dir ? svn_authz_recursive : 0); SVN_ERR(check_authz(eb, fs_path, copy_root, required, subpool)); SVN_ERR(svn_fs_copy(copy_root, fs_path, eb->txn_root, full_path, subpool)); was_copied = TRUE; } else { /* No ancestry given, just make a new directory or empty file. Note that we don't perform an existence check here like the copy-from case does -- that's because svn_fs_make_*() already errors out if the file already exists. Verify write access to the full path and to the parent. */ SVN_ERR(check_authz(eb, full_path, eb->txn_root, svn_authz_write, subpool)); SVN_ERR(check_authz(eb, pb->path, eb->txn_root, svn_authz_write, subpool)); if (is_dir) SVN_ERR(svn_fs_make_dir(eb->txn_root, full_path, subpool)); else SVN_ERR(svn_fs_make_file(eb->txn_root, full_path, subpool)); } /* Cleanup our temporary subpool. */ svn_pool_destroy(subpool); /* Build a new child baton. */ if (is_dir) { *return_baton = make_dir_baton(eb, pb, full_path, was_copied, SVN_INVALID_REVNUM, pool); } else { struct file_baton *new_fb = apr_pcalloc(pool, sizeof(*new_fb)); new_fb->edit_baton = eb; new_fb->path = full_path; *return_baton = new_fb; } return SVN_NO_ERROR; }
/* Recursively traverse EDIT_PATH (as it exists under SOURCE_ROOT) emitting the appropriate editor calls to add it and its children without any history. This is meant to be used when either a subset of the tree has been ignored and we need to copy something from that subset to the part of the tree we do care about, or if a subset of the tree is unavailable because of authz and we need to use it as the source of a copy. */ static svn_error_t * add_subdir(svn_fs_root_t *source_root, svn_fs_root_t *target_root, const svn_delta_editor_t *editor, void *edit_baton, const char *edit_path, void *parent_baton, const char *source_fspath, svn_repos_authz_func_t authz_read_func, void *authz_read_baton, apr_hash_t *changed_paths, apr_pool_t *pool, void **dir_baton) { apr_pool_t *subpool = svn_pool_create(pool); apr_hash_index_t *hi, *phi; apr_hash_t *dirents; apr_hash_t *props; SVN_ERR(editor->add_directory(edit_path, parent_baton, NULL, SVN_INVALID_REVNUM, pool, dir_baton)); SVN_ERR(svn_fs_node_proplist(&props, target_root, edit_path, pool)); for (phi = apr_hash_first(pool, props); phi; phi = apr_hash_next(phi)) { const void *key; void *val; svn_pool_clear(subpool); apr_hash_this(phi, &key, NULL, &val); SVN_ERR(editor->change_dir_prop(*dir_baton, key, val, subpool)); } /* We have to get the dirents from the source path, not the target, because we want nested copies from *readable* paths to be handled by path_driver_cb_func, not add_subdir (in order to preserve history). */ SVN_ERR(svn_fs_dir_entries(&dirents, source_root, source_fspath, pool)); for (hi = apr_hash_first(pool, dirents); hi; hi = apr_hash_next(hi)) { svn_fs_path_change2_t *change; svn_boolean_t readable = TRUE; svn_fs_dirent_t *dent; const char *copyfrom_path = NULL; svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM; const char *new_edit_path; void *val; svn_pool_clear(subpool); apr_hash_this(hi, NULL, NULL, &val); dent = val; new_edit_path = svn_relpath_join(edit_path, dent->name, subpool); /* If a file or subdirectory of the copied directory is listed as a changed path (because it was modified after the copy but before the commit), we remove it from the changed_paths hash so that future calls to path_driver_cb_func will ignore it. */ change = apr_hash_get(changed_paths, new_edit_path, APR_HASH_KEY_STRING); if (change) { apr_hash_set(changed_paths, new_edit_path, APR_HASH_KEY_STRING, NULL); /* If it's a delete, skip this entry. */ if (change->change_kind == svn_fs_path_change_delete) continue; /* If it's a replacement, check for copyfrom info (if we don't have it already. */ if (change->change_kind == svn_fs_path_change_replace) { if (! change->copyfrom_known) { SVN_ERR(svn_fs_copied_from(&change->copyfrom_rev, &change->copyfrom_path, target_root, new_edit_path, pool)); change->copyfrom_known = TRUE; } copyfrom_path = change->copyfrom_path; copyfrom_rev = change->copyfrom_rev; } } if (authz_read_func) SVN_ERR(authz_read_func(&readable, target_root, new_edit_path, authz_read_baton, pool)); if (! readable) continue; if (dent->kind == svn_node_dir) { svn_fs_root_t *new_source_root; const char *new_source_fspath; void *new_dir_baton; if (copyfrom_path) { svn_fs_t *fs = svn_fs_root_fs(source_root); SVN_ERR(svn_fs_revision_root(&new_source_root, fs, copyfrom_rev, pool)); new_source_fspath = copyfrom_path; } else { new_source_root = source_root; new_source_fspath = svn_fspath__join(source_fspath, dent->name, subpool); } /* ### authz considerations? * * I think not; when path_driver_cb_func() calls add_subdir(), it * passes SOURCE_ROOT and SOURCE_FSPATH that are unreadable. */ if (change && change->change_kind == svn_fs_path_change_replace && copyfrom_path == NULL) { SVN_ERR(editor->add_directory(new_edit_path, *dir_baton, NULL, SVN_INVALID_REVNUM, subpool, &new_dir_baton)); } else { SVN_ERR(add_subdir(new_source_root, target_root, editor, edit_baton, new_edit_path, *dir_baton, new_source_fspath, authz_read_func, authz_read_baton, changed_paths, subpool, &new_dir_baton)); } SVN_ERR(editor->close_directory(new_dir_baton, subpool)); } else if (dent->kind == svn_node_file) { svn_txdelta_window_handler_t delta_handler; void *delta_handler_baton, *file_baton; svn_txdelta_stream_t *delta_stream; svn_checksum_t *checksum; SVN_ERR(editor->add_file(new_edit_path, *dir_baton, NULL, SVN_INVALID_REVNUM, pool, &file_baton)); SVN_ERR(svn_fs_node_proplist(&props, target_root, new_edit_path, subpool)); for (phi = apr_hash_first(pool, props); phi; phi = apr_hash_next(phi)) { const void *key; apr_hash_this(phi, &key, NULL, &val); SVN_ERR(editor->change_file_prop(file_baton, key, val, subpool)); } SVN_ERR(editor->apply_textdelta(file_baton, NULL, pool, &delta_handler, &delta_handler_baton)); SVN_ERR(svn_fs_get_file_delta_stream(&delta_stream, NULL, NULL, target_root, new_edit_path, pool)); SVN_ERR(svn_txdelta_send_txstream(delta_stream, delta_handler, delta_handler_baton, pool)); SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, target_root, new_edit_path, TRUE, pool)); SVN_ERR(editor->close_file(file_baton, svn_checksum_to_cstring(checksum, pool), pool)); } else SVN_ERR_MALFUNCTION(); } svn_pool_destroy(subpool); return SVN_NO_ERROR; }
/* Given the CHANGED_PATHS and REVISION from an instance of a svn_log_message_receiver_t function, determine at which location PATH may be expected in the next log message, and set *PREV_PATH_P to that value. KIND is the node kind of PATH. Set *ACTION_P to a character describing the change that caused this revision (as listed in svn_log_changed_path_t) and set *COPYFROM_REV_P to the revision PATH was copied from, or SVN_INVALID_REVNUM if it was not copied. ACTION_P and COPYFROM_REV_P may be NULL, in which case they are not used. Perform all allocations in POOL. This is useful for tracking the various changes in location a particular resource has undergone when performing an RA->get_logs() operation on that resource. */ static svn_error_t * prev_log_path(const char **prev_path_p, char *action_p, svn_revnum_t *copyfrom_rev_p, apr_hash_t *changed_paths, const char *path, svn_node_kind_t kind, svn_revnum_t revision, apr_pool_t *pool) { svn_log_changed_path_t *change; const char *prev_path = NULL; /* It's impossible to find the predecessor path of a NULL path. */ SVN_ERR_ASSERT(path); /* Initialize our return values for the action and copyfrom_rev in case we have an unhandled case later on. */ if (action_p) *action_p = 'M'; if (copyfrom_rev_p) *copyfrom_rev_p = SVN_INVALID_REVNUM; if (changed_paths) { /* See if PATH was explicitly changed in this revision. */ change = svn_hash_gets(changed_paths, path); if (change) { /* If PATH was not newly added in this revision, then it may or may not have also been part of a moved subtree. In this case, set a default previous path, but still look through the parents of this path for a possible copy event. */ if (change->action != 'A' && change->action != 'R') { prev_path = path; } else { /* PATH is new in this revision. This means it cannot have been part of a copied subtree. */ if (change->copyfrom_path) prev_path = apr_pstrdup(pool, change->copyfrom_path); else prev_path = NULL; *prev_path_p = prev_path; if (action_p) *action_p = change->action; if (copyfrom_rev_p) *copyfrom_rev_p = change->copyfrom_rev; return SVN_NO_ERROR; } } if (apr_hash_count(changed_paths)) { /* The path was not explicitly changed in this revision. The fact that we're hearing about this revision implies, then, that the path was a child of some copied directory. We need to find that directory, and effectively "re-base" our path on that directory's copyfrom_path. */ int i; apr_array_header_t *paths; /* Build a sorted list of the changed paths. */ paths = svn_sort__hash(changed_paths, svn_sort_compare_items_as_paths, pool); /* Now, walk the list of paths backwards, looking a parent of our path that has copyfrom information. */ for (i = paths->nelts; i > 0; i--) { svn_sort__item_t item = APR_ARRAY_IDX(paths, i - 1, svn_sort__item_t); const char *ch_path = item.key; size_t len = strlen(ch_path); /* See if our path is the child of this change path. If not, keep looking. */ if (! ((strncmp(ch_path, path, len) == 0) && (path[len] == '/'))) continue; /* Okay, our path *is* a child of this change path. If this change was copied, we just need to apply the portion of our path that is relative to this change's path, to the change's copyfrom path. Otherwise, this change isn't really interesting to us, and our search continues. */ change = item.value; if (change->copyfrom_path) { if (action_p) *action_p = change->action; if (copyfrom_rev_p) *copyfrom_rev_p = change->copyfrom_rev; prev_path = svn_fspath__join(change->copyfrom_path, path + len + 1, pool); break; } } } } /* If we didn't find what we expected to find, return an error. (Because directories bubble-up, we get a bunch of logs we might not want. Be forgiving in that case.) */ if (! prev_path) { if (kind == svn_node_dir) prev_path = apr_pstrdup(pool, path); else return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL, _("Missing changed-path information for " "'%s' in revision %ld"), svn_dirent_local_style(path, pool), revision); } *prev_path_p = prev_path; return SVN_NO_ERROR; }
dav_error * dav_svn__get_location_segments_report(const dav_resource *resource, const apr_xml_doc *doc, ap_filter_t *output) { svn_error_t *serr; dav_error *derr = NULL; apr_bucket_brigade *bb; int ns; apr_xml_elem *child; const char *abs_path = NULL; svn_revnum_t peg_revision = SVN_INVALID_REVNUM; svn_revnum_t start_rev = SVN_INVALID_REVNUM; svn_revnum_t end_rev = SVN_INVALID_REVNUM; dav_svn__authz_read_baton arb; struct location_segment_baton location_segment_baton; /* 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_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"); } /* Gather the parameters. */ 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, "peg-revision") == 0) { peg_revision = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1)); } else if (strcmp(child->name, "start-revision") == 0) { start_rev = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1)); } else if (strcmp(child->name, "end-revision") == 0) { end_rev = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1)); } else if (strcmp(child->name, "path") == 0) { 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. */ abs_path = svn_fspath__join(resource->info->repos_path, rel_path, resource->pool); } } /* Check that all parameters are present and valid. */ if (! abs_path) 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)) { svn_revnum_t youngest; serr = svn_fs_youngest_rev(&youngest, resource->info->repos->fs, resource->pool); if (serr != NULL) return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Could not determine youngest revision", resource->pool); if (!SVN_IS_VALID_REVNUM(start_rev)) start_rev = youngest; if (!SVN_IS_VALID_REVNUM(peg_revision)) peg_revision = youngest; } /* No END_REV? We'll use 0. */ if (!SVN_IS_VALID_REVNUM(end_rev)) end_rev = 0; if (end_rev > start_rev) return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0, "End revision must not be younger than " "start revision"); if (start_rev > peg_revision) return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0, "Start revision must not be younger than " "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); /* Do what we came here for. */ location_segment_baton.sent_opener = FALSE; location_segment_baton.output = output; location_segment_baton.bb = bb; if ((serr = svn_repos_node_location_segments(resource->info->repos->repos, abs_path, peg_revision, start_rev, end_rev, location_segment_receiver, &location_segment_baton, dav_svn__authz_read_func(&arb), &arb, resource->pool))) { derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, NULL, resource->pool); goto cleanup; } if ((serr = maybe_send_opener(&location_segment_baton))) { derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Error beginning REPORT response.", resource->pool); goto cleanup; } if ((serr = dav_svn__brigade_puts(bb, output, "</S:get-location-segments-report>" DEBUG_CR))) { derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Error ending REPORT response.", resource->pool); goto cleanup; } cleanup: return dav_svn__final_flush_or_error(resource->info->r, bb, output, derr, resource->pool); }