/* This is a wrapper around svn_uri_condense_targets() and * svn_dirent_condense_targets() (the choice of which is made based on * the value of TARGETS_ARE_URIS) which takes care of the * single-target special case. * * Callers are expected to check for an empty *COMMON_PARENT (which * means, "there was nothing common") for themselves. */ static svn_error_t * condense_targets(const char **common_parent, apr_array_header_t **target_relpaths, const apr_array_header_t *targets, svn_boolean_t targets_are_uris, svn_boolean_t remove_redundancies, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { if (targets_are_uris) { SVN_ERR(svn_uri_condense_targets(common_parent, target_relpaths, targets, remove_redundancies, result_pool, scratch_pool)); } else { SVN_ERR(svn_dirent_condense_targets(common_parent, target_relpaths, targets, remove_redundancies, result_pool, scratch_pool)); } /* svn_*_condense_targets leaves *TARGET_RELPATHS empty if TARGETS only had 1 member, so we special case that. */ if (apr_is_empty_array(*target_relpaths)) { const char *base_name; if (targets_are_uris) { svn_uri_split(common_parent, &base_name, *common_parent, result_pool); } else { svn_dirent_split(common_parent, &base_name, *common_parent, result_pool); } APR_ARRAY_PUSH(*target_relpaths, const char *) = base_name; } return SVN_NO_ERROR; }
svn_error_t * svn_client_info3(const char *abspath_or_url, const svn_opt_revision_t *peg_revision, const svn_opt_revision_t *revision, svn_depth_t depth, svn_boolean_t fetch_excluded, svn_boolean_t fetch_actual_only, const apr_array_header_t *changelists, svn_client_info_receiver2_t receiver, void *receiver_baton, svn_client_ctx_t *ctx, apr_pool_t *pool) { svn_ra_session_t *ra_session; svn_client__pathrev_t *pathrev; svn_lock_t *lock; svn_boolean_t related; const char *base_name; svn_dirent_t *the_ent; svn_client_info2_t *info; svn_error_t *err; if (depth == svn_depth_unknown) depth = svn_depth_empty; if ((revision == NULL || revision->kind == svn_opt_revision_unspecified) && (peg_revision == NULL || peg_revision->kind == svn_opt_revision_unspecified)) { /* Do all digging in the working copy. */ wc_info_receiver_baton_t b; b.client_receiver_func = receiver; b.client_receiver_baton = receiver_baton; return svn_error_trace( svn_wc__get_info(ctx->wc_ctx, abspath_or_url, depth, fetch_excluded, fetch_actual_only, changelists, wc_info_receiver, &b, ctx->cancel_func, ctx->cancel_baton, pool)); } /* Go repository digging instead. */ /* Trace rename history (starting at path_or_url@peg_revision) and return RA session to the possibly-renamed URL as it exists in REVISION. The ra_session returned will be anchored on this "final" URL. */ SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &pathrev, abspath_or_url, NULL, peg_revision, revision, ctx, pool)); svn_uri_split(NULL, &base_name, pathrev->url, pool); /* Get the dirent for the URL itself. */ err = ra_stat_compatible(ra_session, pathrev->rev, &the_ent, DIRENT_FIELDS, ctx, pool); if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED) { svn_error_clear(err); /* If we're recursing, degrade gracefully: rather than throw an error, return no information about the repos root. */ if (depth > svn_depth_empty) goto pre_1_2_recurse; /* Otherwise, we really are stuck. Better tell the user what's going on. */ return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, _("Server does not support retrieving " "information about the repository root")); } else if (err) { return svn_error_trace(err); } if (! the_ent) return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, _("URL '%s' non-existent in revision %ld"), pathrev->url, pathrev->rev); /* Check if the URL exists in HEAD and refers to the same resource. In this case, we check the repository for a lock on this URL. ### There is a possible race here, since HEAD might have changed since ### we checked it. A solution to this problem could be to do the below ### check in a loop which only terminates if the HEAD revision is the same ### before and after this check. That could, however, lead to a ### starvation situation instead. */ SVN_ERR(same_resource_in_head(&related, pathrev->url, pathrev->rev, ra_session, ctx, pool)); if (related) { err = svn_ra_get_lock(ra_session, &lock, "", pool); /* An old mod_dav_svn will always work; there's nothing wrong with doing a PROPFIND for a property named "DAV:supportedlock". But an old svnserve will error. */ if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED) { svn_error_clear(err); lock = NULL; } else if (err) return svn_error_trace(err); } else lock = NULL; /* Push the URL's dirent (and lock) at the callback.*/ SVN_ERR(build_info_from_dirent(&info, the_ent, lock, pathrev, pool)); SVN_ERR(receiver(receiver_baton, base_name, info, pool)); /* Possibly recurse, using the original RA session. */ if (depth > svn_depth_empty && (the_ent->kind == svn_node_dir)) { apr_hash_t *locks; pre_1_2_recurse: if (peg_revision->kind == svn_opt_revision_head) { err = svn_ra_get_locks2(ra_session, &locks, "", depth, pool); /* Catch specific errors thrown by old mod_dav_svn or svnserve. */ if (err && (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED || err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)) { svn_error_clear(err); locks = apr_hash_make(pool); /* use an empty hash */ } else if (err) return svn_error_trace(err); } else locks = apr_hash_make(pool); /* use an empty hash */ SVN_ERR(push_dir_info(ra_session, pathrev, "", receiver, receiver_baton, depth, ctx, locks, pool)); } return SVN_NO_ERROR; }
static svn_error_t * mkdir_urls(const apr_array_header_t *urls, svn_boolean_t make_parents, const apr_hash_t *revprop_table, svn_commit_callback2_t commit_callback, void *commit_baton, svn_client_ctx_t *ctx, apr_pool_t *pool) { svn_ra_session_t *ra_session = NULL; const svn_delta_editor_t *editor; void *edit_baton; const char *log_msg; apr_array_header_t *targets; apr_hash_t *targets_hash; apr_hash_t *commit_revprops; svn_error_t *err; const char *common; int i; /* Find any non-existent parent directories */ if (make_parents) { apr_array_header_t *all_urls = apr_array_make(pool, urls->nelts, sizeof(const char *)); const char *first_url = APR_ARRAY_IDX(urls, 0, const char *); apr_pool_t *iterpool = svn_pool_create(pool); SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, first_url, NULL, NULL, FALSE, TRUE, ctx, pool)); for (i = 0; i < urls->nelts; i++) { const char *url = APR_ARRAY_IDX(urls, i, const char *); svn_pool_clear(iterpool); SVN_ERR(add_url_parents(ra_session, url, all_urls, iterpool, pool)); } svn_pool_destroy(iterpool); urls = all_urls; } /* Condense our list of mkdir targets. */ SVN_ERR(svn_uri_condense_targets(&common, &targets, urls, FALSE, pool, pool)); /*Remove duplicate targets introduced by make_parents with more targets. */ SVN_ERR(svn_hash_from_cstring_keys(&targets_hash, targets, pool)); SVN_ERR(svn_hash_keys(&targets, targets_hash, pool)); if (! targets->nelts) { const char *bname; svn_uri_split(&common, &bname, common, pool); APR_ARRAY_PUSH(targets, const char *) = bname; if (*bname == '\0') return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, _("There is no valid uri above '%s'"), common); } else { svn_boolean_t resplit = FALSE; /* We can't "mkdir" the root of an editor drive, so if one of our targets is the empty string, we need to back everything up by a path component. */ for (i = 0; i < targets->nelts; i++) { const char *path = APR_ARRAY_IDX(targets, i, const char *); if (! *path) { resplit = TRUE; break; } } if (resplit) { const char *bname; svn_uri_split(&common, &bname, common, pool); if (*bname == '\0') return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, _("There is no valid uri above '%s'"), common); for (i = 0; i < targets->nelts; i++) { const char *path = APR_ARRAY_IDX(targets, i, const char *); path = svn_relpath_join(bname, path, pool); APR_ARRAY_IDX(targets, i, const char *) = path; } } } qsort(targets->elts, targets->nelts, targets->elt_size, svn_sort_compare_paths); /* ### This reparent may be problematic in limited-authz-to-common-parent ### scenarios (compare issue #3242). See also issue #3649. */ if (ra_session) SVN_ERR(svn_ra_reparent(ra_session, common, pool)); /* Create new commit items and add them to the array. */ if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx)) { svn_client_commit_item3_t *item; const char *tmp_file; apr_array_header_t *commit_items = apr_array_make(pool, targets->nelts, sizeof(item)); for (i = 0; i < targets->nelts; i++) { const char *path = APR_ARRAY_IDX(targets, i, const char *); item = svn_client_commit_item3_create(pool); item->url = svn_path_url_add_component2(common, path, pool); item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD; APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item; } SVN_ERR(svn_client__get_log_msg(&log_msg, &tmp_file, commit_items, ctx, pool)); if (! log_msg) return SVN_NO_ERROR; } else
/* Like svn_ra_stat() but with a compatibility hack for pre-1.2 svnserve. */ static svn_error_t * ra_stat_compatible(svn_ra_session_t *ra_session, svn_revnum_t rev, svn_dirent_t **dirent_p, apr_uint32_t dirent_fields, svn_client_ctx_t *ctx, apr_pool_t *pool) { const char *repos_root_URL, *url; svn_error_t *err; svn_dirent_t *the_ent; SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_URL, pool)); SVN_ERR(svn_ra_get_session_url(ra_session, &url, pool)); err = svn_ra_stat(ra_session, "", rev, &the_ent, pool); /* svn_ra_stat() will work against old versions of mod_dav_svn, but not old versions of svnserve. In the case of a pre-1.2 svnserve, catch the specific error it throws:*/ if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED) { /* Fall back to pre-1.2 strategy for fetching dirent's URL. */ svn_node_kind_t url_kind; svn_ra_session_t *parent_ra_session; apr_hash_t *parent_ents; const char *parent_url, *base_name; if (strcmp(url, repos_root_URL) == 0) { /* In this universe, there's simply no way to fetch information about the repository's root directory! */ return err; } svn_error_clear(err); SVN_ERR(svn_ra_check_path(ra_session, "", rev, &url_kind, pool)); if (url_kind == svn_node_none) return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, _("URL '%s' non-existent in revision %ld"), url, rev); /* Open a new RA session to the item's parent. */ svn_uri_split(&parent_url, &base_name, url, pool); SVN_ERR(svn_client__open_ra_session_internal(&parent_ra_session, NULL, parent_url, NULL, NULL, FALSE, TRUE, ctx, pool)); /* Get all parent's entries, and find the item's dirent in the hash. */ SVN_ERR(svn_ra_get_dir2(parent_ra_session, &parent_ents, NULL, NULL, "", rev, dirent_fields, pool)); the_ent = apr_hash_get(parent_ents, base_name, APR_HASH_KEY_STRING); if (the_ent == NULL) return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, _("URL '%s' non-existent in revision %ld"), url, rev); } else if (err) { return svn_error_trace(err); } *dirent_p = the_ent; return SVN_NO_ERROR; }