1251881Speter/* 2251881Speter * copy_foreign.c: copy from other repository support. 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter/* ==================================================================== */ 25251881Speter 26251881Speter/*** Includes. ***/ 27251881Speter 28251881Speter#include <string.h> 29251881Speter#include "svn_hash.h" 30251881Speter#include "svn_client.h" 31251881Speter#include "svn_delta.h" 32251881Speter#include "svn_dirent_uri.h" 33251881Speter#include "svn_error.h" 34251881Speter#include "svn_error_codes.h" 35251881Speter#include "svn_path.h" 36251881Speter#include "svn_pools.h" 37251881Speter#include "svn_props.h" 38251881Speter#include "svn_ra.h" 39251881Speter#include "svn_wc.h" 40251881Speter 41251881Speter#include <apr_md5.h> 42251881Speter 43251881Speter#include "client.h" 44251881Speter#include "private/svn_subr_private.h" 45251881Speter#include "private/svn_wc_private.h" 46251881Speter#include "svn_private_config.h" 47251881Speter 48251881Speterstruct edit_baton_t 49251881Speter{ 50251881Speter apr_pool_t *pool; 51251881Speter const char *anchor_abspath; 52251881Speter 53251881Speter svn_wc_context_t *wc_ctx; 54251881Speter svn_wc_notify_func2_t notify_func; 55251881Speter void *notify_baton; 56251881Speter}; 57251881Speter 58251881Speterstruct dir_baton_t 59251881Speter{ 60251881Speter apr_pool_t *pool; 61251881Speter 62251881Speter struct dir_baton_t *pb; 63251881Speter struct edit_baton_t *eb; 64251881Speter 65251881Speter const char *local_abspath; 66251881Speter 67251881Speter svn_boolean_t created; 68251881Speter apr_hash_t *properties; 69251881Speter 70251881Speter int users; 71251881Speter}; 72251881Speter 73251881Speter/* svn_delta_editor_t function */ 74251881Speterstatic svn_error_t * 75251881Speteredit_open(void *edit_baton, 76251881Speter svn_revnum_t base_revision, 77251881Speter apr_pool_t *result_pool, 78251881Speter void **root_baton) 79251881Speter{ 80251881Speter struct edit_baton_t *eb = edit_baton; 81251881Speter apr_pool_t *dir_pool = svn_pool_create(eb->pool); 82251881Speter struct dir_baton_t *db = apr_pcalloc(dir_pool, sizeof(*db)); 83251881Speter 84251881Speter db->pool = dir_pool; 85251881Speter db->eb = eb; 86251881Speter db->users = 1; 87251881Speter db->local_abspath = eb->anchor_abspath; 88251881Speter 89251881Speter SVN_ERR(svn_io_make_dir_recursively(eb->anchor_abspath, dir_pool)); 90251881Speter 91251881Speter *root_baton = db; 92251881Speter 93251881Speter return SVN_NO_ERROR; 94251881Speter} 95251881Speter 96251881Speter/* svn_delta_editor_t function */ 97251881Speterstatic svn_error_t * 98251881Speteredit_close(void *edit_baton, 99251881Speter apr_pool_t *scratch_pool) 100251881Speter{ 101251881Speter return SVN_NO_ERROR; 102251881Speter} 103251881Speter 104251881Speterstatic svn_error_t * 105251881Speterdir_add(const char *path, 106251881Speter void *parent_baton, 107251881Speter const char *copyfrom_path, 108251881Speter svn_revnum_t copyfrom_revision, 109251881Speter apr_pool_t *result_pool, 110251881Speter void **child_baton) 111251881Speter{ 112251881Speter struct dir_baton_t *pb = parent_baton; 113251881Speter struct edit_baton_t *eb = pb->eb; 114251881Speter apr_pool_t *dir_pool = svn_pool_create(pb->pool); 115251881Speter struct dir_baton_t *db = apr_pcalloc(dir_pool, sizeof(*db)); 116251881Speter svn_boolean_t under_root; 117251881Speter 118251881Speter pb->users++; 119251881Speter 120251881Speter db->pb = pb; 121251881Speter db->eb = pb->eb; 122251881Speter db->pool = dir_pool; 123251881Speter db->users = 1; 124251881Speter 125251881Speter SVN_ERR(svn_dirent_is_under_root(&under_root, &db->local_abspath, 126251881Speter eb->anchor_abspath, path, db->pool)); 127251881Speter if (! under_root) 128251881Speter { 129251881Speter return svn_error_createf( 130251881Speter SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, 131251881Speter _("Path '%s' is not in the working copy"), 132251881Speter svn_dirent_local_style(path, db->pool)); 133251881Speter } 134251881Speter 135251881Speter SVN_ERR(svn_io_make_dir_recursively(db->local_abspath, db->pool)); 136251881Speter 137251881Speter *child_baton = db; 138251881Speter return SVN_NO_ERROR; 139251881Speter} 140251881Speter 141251881Speterstatic svn_error_t * 142251881Speterdir_change_prop(void *dir_baton, 143251881Speter const char *name, 144251881Speter const svn_string_t *value, 145251881Speter apr_pool_t *scratch_pool) 146251881Speter{ 147251881Speter struct dir_baton_t *db = dir_baton; 148251881Speter struct edit_baton_t *eb = db->eb; 149251881Speter svn_prop_kind_t prop_kind; 150251881Speter 151251881Speter prop_kind = svn_property_kind2(name); 152251881Speter 153251881Speter if (prop_kind != svn_prop_regular_kind 154251881Speter || ! strcmp(name, SVN_PROP_MERGEINFO)) 155251881Speter { 156251881Speter /* We can't handle DAV, ENTRY and merge specific props here */ 157251881Speter return SVN_NO_ERROR; 158251881Speter } 159251881Speter 160251881Speter if (! db->created) 161251881Speter { 162251881Speter /* We can still store them in the hash for immediate addition 163251881Speter with the svn_wc_add_from_disk2() call */ 164251881Speter if (! db->properties) 165251881Speter db->properties = apr_hash_make(db->pool); 166251881Speter 167251881Speter if (value != NULL) 168251881Speter svn_hash_sets(db->properties, apr_pstrdup(db->pool, name), 169251881Speter svn_string_dup(value, db->pool)); 170251881Speter } 171251881Speter else 172251881Speter { 173251881Speter /* We have already notified for this directory, so don't do that again */ 174251881Speter SVN_ERR(svn_wc_prop_set4(eb->wc_ctx, db->local_abspath, name, value, 175251881Speter svn_depth_empty, FALSE, NULL, 176251881Speter NULL, NULL, /* Cancelation */ 177251881Speter NULL, NULL, /* Notification */ 178251881Speter scratch_pool)); 179251881Speter } 180251881Speter 181251881Speter return SVN_NO_ERROR; 182251881Speter} 183251881Speter 184251881Speter/* Releases the directory baton if there are no more users */ 185251881Speterstatic svn_error_t * 186251881Spetermaybe_done(struct dir_baton_t *db) 187251881Speter{ 188251881Speter db->users--; 189251881Speter 190251881Speter if (db->users == 0) 191251881Speter { 192251881Speter struct dir_baton_t *pb = db->pb; 193251881Speter 194251881Speter svn_pool_clear(db->pool); 195251881Speter 196251881Speter if (pb) 197251881Speter SVN_ERR(maybe_done(pb)); 198251881Speter } 199251881Speter 200251881Speter return SVN_NO_ERROR; 201251881Speter} 202251881Speter 203251881Speterstatic svn_error_t * 204251881Speterensure_added(struct dir_baton_t *db, 205251881Speter apr_pool_t *scratch_pool) 206251881Speter{ 207251881Speter if (db->created) 208251881Speter return SVN_NO_ERROR; 209251881Speter 210251881Speter if (db->pb) 211251881Speter SVN_ERR(ensure_added(db->pb, scratch_pool)); 212251881Speter 213251881Speter db->created = TRUE; 214251881Speter 215251881Speter /* Add the directory with all the already collected properties */ 216251881Speter SVN_ERR(svn_wc_add_from_disk2(db->eb->wc_ctx, 217251881Speter db->local_abspath, 218251881Speter db->properties, 219251881Speter db->eb->notify_func, 220251881Speter db->eb->notify_baton, 221251881Speter scratch_pool)); 222251881Speter 223251881Speter return SVN_NO_ERROR; 224251881Speter} 225251881Speter 226251881Speterstatic svn_error_t * 227251881Speterdir_close(void *dir_baton, 228251881Speter apr_pool_t *scratch_pool) 229251881Speter{ 230251881Speter struct dir_baton_t *db = dir_baton; 231251881Speter /*struct edit_baton_t *eb = db->eb;*/ 232251881Speter 233251881Speter SVN_ERR(ensure_added(db, scratch_pool)); 234251881Speter 235251881Speter SVN_ERR(maybe_done(db)); 236251881Speter 237251881Speter return SVN_NO_ERROR; 238251881Speter} 239251881Speter 240251881Speterstruct file_baton_t 241251881Speter{ 242251881Speter apr_pool_t *pool; 243251881Speter 244251881Speter struct dir_baton_t *pb; 245251881Speter struct edit_baton_t *eb; 246251881Speter 247251881Speter const char *local_abspath; 248251881Speter apr_hash_t *properties; 249251881Speter 250251881Speter svn_boolean_t writing; 251251881Speter unsigned char digest[APR_MD5_DIGESTSIZE]; 252251881Speter 253251881Speter const char *tmp_path; 254251881Speter}; 255251881Speter 256251881Speterstatic svn_error_t * 257251881Speterfile_add(const char *path, 258251881Speter void *parent_baton, 259251881Speter const char *copyfrom_path, 260251881Speter svn_revnum_t copyfrom_revision, 261251881Speter apr_pool_t *result_pool, 262251881Speter void **file_baton) 263251881Speter{ 264251881Speter struct dir_baton_t *pb = parent_baton; 265251881Speter struct edit_baton_t *eb = pb->eb; 266251881Speter apr_pool_t *file_pool = svn_pool_create(pb->pool); 267251881Speter struct file_baton_t *fb = apr_pcalloc(file_pool, sizeof(*fb)); 268251881Speter svn_boolean_t under_root; 269251881Speter 270251881Speter pb->users++; 271251881Speter 272251881Speter fb->pool = file_pool; 273251881Speter fb->eb = eb; 274251881Speter fb->pb = pb; 275251881Speter 276251881Speter SVN_ERR(svn_dirent_is_under_root(&under_root, &fb->local_abspath, 277251881Speter eb->anchor_abspath, path, fb->pool)); 278251881Speter if (! under_root) 279251881Speter { 280251881Speter return svn_error_createf( 281251881Speter SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, 282251881Speter _("Path '%s' is not in the working copy"), 283251881Speter svn_dirent_local_style(path, fb->pool)); 284251881Speter } 285251881Speter 286251881Speter *file_baton = fb; 287251881Speter return SVN_NO_ERROR; 288251881Speter} 289251881Speter 290251881Speterstatic svn_error_t * 291251881Speterfile_change_prop(void *file_baton, 292251881Speter const char *name, 293251881Speter const svn_string_t *value, 294251881Speter apr_pool_t *scratch_pool) 295251881Speter{ 296251881Speter struct file_baton_t *fb = file_baton; 297251881Speter svn_prop_kind_t prop_kind; 298251881Speter 299251881Speter prop_kind = svn_property_kind2(name); 300251881Speter 301251881Speter if (prop_kind != svn_prop_regular_kind 302251881Speter || ! strcmp(name, SVN_PROP_MERGEINFO)) 303251881Speter { 304251881Speter /* We can't handle DAV, ENTRY and merge specific props here */ 305251881Speter return SVN_NO_ERROR; 306251881Speter } 307251881Speter 308251881Speter /* We store all properties in the hash for immediate addition 309251881Speter with the svn_wc_add_from_disk2() call */ 310251881Speter if (! fb->properties) 311251881Speter fb->properties = apr_hash_make(fb->pool); 312251881Speter 313251881Speter if (value != NULL) 314251881Speter svn_hash_sets(fb->properties, apr_pstrdup(fb->pool, name), 315251881Speter svn_string_dup(value, fb->pool)); 316251881Speter 317251881Speter return SVN_NO_ERROR; 318251881Speter} 319251881Speter 320251881Speterstatic svn_error_t * 321251881Speterfile_textdelta(void *file_baton, 322251881Speter const char *base_checksum, 323251881Speter apr_pool_t *result_pool, 324251881Speter svn_txdelta_window_handler_t *handler, 325251881Speter void **handler_baton) 326251881Speter{ 327251881Speter struct file_baton_t *fb = file_baton; 328251881Speter svn_stream_t *target; 329251881Speter 330251881Speter SVN_ERR_ASSERT(! fb->writing); 331251881Speter 332251881Speter SVN_ERR(svn_stream_open_writable(&target, fb->local_abspath, fb->pool, 333251881Speter fb->pool)); 334251881Speter 335251881Speter fb->writing = TRUE; 336251881Speter svn_txdelta_apply(svn_stream_empty(fb->pool) /* source */, 337251881Speter target, 338251881Speter fb->digest, 339251881Speter fb->local_abspath, 340251881Speter fb->pool, 341251881Speter /* Provide the handler directly */ 342251881Speter handler, handler_baton); 343251881Speter 344251881Speter return SVN_NO_ERROR; 345251881Speter} 346251881Speter 347251881Speterstatic svn_error_t * 348251881Speterfile_close(void *file_baton, 349251881Speter const char *text_checksum, 350251881Speter apr_pool_t *scratch_pool) 351251881Speter{ 352251881Speter struct file_baton_t *fb = file_baton; 353251881Speter struct edit_baton_t *eb = fb->eb; 354251881Speter struct dir_baton_t *pb = fb->pb; 355251881Speter 356251881Speter SVN_ERR(ensure_added(pb, fb->pool)); 357251881Speter 358251881Speter if (text_checksum) 359251881Speter { 360251881Speter svn_checksum_t *expected_checksum; 361251881Speter svn_checksum_t *actual_checksum; 362251881Speter 363251881Speter SVN_ERR(svn_checksum_parse_hex(&expected_checksum, svn_checksum_md5, 364251881Speter text_checksum, fb->pool)); 365251881Speter actual_checksum = svn_checksum__from_digest_md5(fb->digest, fb->pool); 366251881Speter 367251881Speter if (! svn_checksum_match(expected_checksum, actual_checksum)) 368251881Speter return svn_error_trace( 369251881Speter svn_checksum_mismatch_err(expected_checksum, 370251881Speter actual_checksum, 371251881Speter fb->pool, 372251881Speter _("Checksum mismatch for '%s'"), 373251881Speter svn_dirent_local_style( 374251881Speter fb->local_abspath, 375251881Speter fb->pool))); 376251881Speter } 377251881Speter 378251881Speter SVN_ERR(svn_wc_add_from_disk2(eb->wc_ctx, fb->local_abspath, fb->properties, 379251881Speter eb->notify_func, eb->notify_baton, 380251881Speter fb->pool)); 381251881Speter 382251881Speter svn_pool_destroy(fb->pool); 383251881Speter SVN_ERR(maybe_done(pb)); 384251881Speter 385251881Speter return SVN_NO_ERROR; 386251881Speter} 387251881Speter 388251881Speterstatic svn_error_t * 389251881Spetercopy_foreign_dir(svn_ra_session_t *ra_session, 390251881Speter svn_client__pathrev_t *location, 391251881Speter svn_wc_context_t *wc_ctx, 392251881Speter const char *dst_abspath, 393251881Speter svn_depth_t depth, 394251881Speter svn_wc_notify_func2_t notify_func, 395251881Speter void *notify_baton, 396251881Speter svn_cancel_func_t cancel_func, 397251881Speter void *cancel_baton, 398251881Speter apr_pool_t *scratch_pool) 399251881Speter{ 400251881Speter struct edit_baton_t eb; 401251881Speter svn_delta_editor_t *editor = svn_delta_default_editor(scratch_pool); 402251881Speter const svn_delta_editor_t *wrapped_editor; 403251881Speter void *wrapped_baton; 404251881Speter const svn_ra_reporter3_t *reporter; 405251881Speter void *reporter_baton; 406251881Speter 407251881Speter eb.pool = scratch_pool; 408251881Speter eb.anchor_abspath = dst_abspath; 409251881Speter 410251881Speter eb.wc_ctx = wc_ctx; 411251881Speter eb.notify_func = notify_func; 412251881Speter eb.notify_baton = notify_baton; 413251881Speter 414251881Speter editor->open_root = edit_open; 415251881Speter editor->close_edit = edit_close; 416251881Speter 417251881Speter editor->add_directory = dir_add; 418251881Speter editor->change_dir_prop = dir_change_prop; 419251881Speter editor->close_directory = dir_close; 420251881Speter 421251881Speter editor->add_file = file_add; 422251881Speter editor->change_file_prop = file_change_prop; 423251881Speter editor->apply_textdelta = file_textdelta; 424251881Speter editor->close_file = file_close; 425251881Speter 426251881Speter SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, cancel_baton, 427251881Speter editor, &eb, 428251881Speter &wrapped_editor, &wrapped_baton, 429251881Speter scratch_pool)); 430251881Speter 431251881Speter SVN_ERR(svn_ra_do_update3(ra_session, &reporter, &reporter_baton, 432251881Speter location->rev, "", svn_depth_infinity, 433251881Speter FALSE, FALSE, wrapped_editor, wrapped_baton, 434251881Speter scratch_pool, scratch_pool)); 435251881Speter 436251881Speter SVN_ERR(reporter->set_path(reporter_baton, "", location->rev, depth, 437251881Speter TRUE /* incomplete */, 438251881Speter NULL, scratch_pool)); 439251881Speter 440251881Speter SVN_ERR(reporter->finish_report(reporter_baton, scratch_pool)); 441251881Speter 442251881Speter return SVN_NO_ERROR; 443251881Speter} 444251881Speter 445251881Speter 446251881Spetersvn_error_t * 447251881Spetersvn_client__copy_foreign(const char *url, 448251881Speter const char *dst_abspath, 449251881Speter svn_opt_revision_t *peg_revision, 450251881Speter svn_opt_revision_t *revision, 451251881Speter svn_depth_t depth, 452251881Speter svn_boolean_t make_parents, 453251881Speter svn_boolean_t already_locked, 454251881Speter svn_client_ctx_t *ctx, 455251881Speter apr_pool_t *scratch_pool) 456251881Speter{ 457251881Speter svn_ra_session_t *ra_session; 458251881Speter svn_client__pathrev_t *loc; 459251881Speter svn_node_kind_t kind; 460251881Speter svn_node_kind_t wc_kind; 461251881Speter const char *dir_abspath; 462251881Speter 463251881Speter SVN_ERR_ASSERT(svn_path_is_url(url)); 464251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath)); 465251881Speter 466251881Speter /* Do we need to validate/update revisions? */ 467251881Speter 468251881Speter SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc, 469251881Speter url, NULL, 470251881Speter peg_revision, 471251881Speter revision, ctx, 472251881Speter scratch_pool)); 473251881Speter 474251881Speter SVN_ERR(svn_ra_check_path(ra_session, "", loc->rev, &kind, scratch_pool)); 475251881Speter 476251881Speter if (kind != svn_node_file && kind != svn_node_dir) 477251881Speter return svn_error_createf( 478251881Speter SVN_ERR_ILLEGAL_TARGET, NULL, 479251881Speter _("'%s' is not a valid location inside a repository"), 480251881Speter url); 481251881Speter 482251881Speter SVN_ERR(svn_wc_read_kind2(&wc_kind, ctx->wc_ctx, dst_abspath, FALSE, TRUE, 483251881Speter scratch_pool)); 484251881Speter 485251881Speter if (wc_kind != svn_node_none) 486251881Speter { 487251881Speter return svn_error_createf( 488251881Speter SVN_ERR_ENTRY_EXISTS, NULL, 489251881Speter _("'%s' is already under version control"), 490251881Speter svn_dirent_local_style(dst_abspath, scratch_pool)); 491251881Speter } 492251881Speter 493251881Speter dir_abspath = svn_dirent_dirname(dst_abspath, scratch_pool); 494251881Speter SVN_ERR(svn_wc_read_kind2(&wc_kind, ctx->wc_ctx, dir_abspath, 495251881Speter FALSE, FALSE, scratch_pool)); 496251881Speter 497251881Speter if (wc_kind == svn_node_none) 498251881Speter { 499251881Speter if (make_parents) 500251881Speter SVN_ERR(svn_client__make_local_parents(dir_abspath, make_parents, ctx, 501251881Speter scratch_pool)); 502251881Speter 503251881Speter SVN_ERR(svn_wc_read_kind2(&wc_kind, ctx->wc_ctx, dir_abspath, 504251881Speter FALSE, FALSE, scratch_pool)); 505251881Speter } 506251881Speter 507251881Speter if (wc_kind != svn_node_dir) 508251881Speter return svn_error_createf( 509251881Speter SVN_ERR_ENTRY_NOT_FOUND, NULL, 510251881Speter _("Can't add '%s', because no parent directory is found"), 511251881Speter svn_dirent_local_style(dst_abspath, scratch_pool)); 512251881Speter 513251881Speter 514251881Speter if (kind == svn_node_file) 515251881Speter { 516251881Speter svn_stream_t *target; 517251881Speter apr_hash_t *props; 518251881Speter apr_hash_index_t *hi; 519251881Speter SVN_ERR(svn_stream_open_writable(&target, dst_abspath, scratch_pool, 520251881Speter scratch_pool)); 521251881Speter 522251881Speter SVN_ERR(svn_ra_get_file(ra_session, "", loc->rev, target, NULL, &props, 523251881Speter scratch_pool)); 524251881Speter 525251881Speter if (props != NULL) 526251881Speter for (hi = apr_hash_first(scratch_pool, props); hi; 527251881Speter hi = apr_hash_next(hi)) 528251881Speter { 529251881Speter const char *name = svn__apr_hash_index_key(hi); 530251881Speter 531251881Speter if (svn_property_kind2(name) != svn_prop_regular_kind 532251881Speter || ! strcmp(name, SVN_PROP_MERGEINFO)) 533251881Speter { 534251881Speter /* We can't handle DAV, ENTRY and merge specific props here */ 535251881Speter svn_hash_sets(props, name, NULL); 536251881Speter } 537251881Speter } 538251881Speter 539251881Speter if (!already_locked) 540251881Speter SVN_WC__CALL_WITH_WRITE_LOCK( 541251881Speter svn_wc_add_from_disk2(ctx->wc_ctx, dst_abspath, props, 542251881Speter ctx->notify_func2, ctx->notify_baton2, 543251881Speter scratch_pool), 544251881Speter ctx->wc_ctx, dir_abspath, FALSE, scratch_pool); 545251881Speter else 546251881Speter SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, dst_abspath, props, 547251881Speter ctx->notify_func2, ctx->notify_baton2, 548251881Speter scratch_pool)); 549251881Speter } 550251881Speter else 551251881Speter { 552251881Speter if (!already_locked) 553251881Speter SVN_WC__CALL_WITH_WRITE_LOCK( 554251881Speter copy_foreign_dir(ra_session, loc, 555251881Speter ctx->wc_ctx, dst_abspath, 556251881Speter depth, 557251881Speter ctx->notify_func2, ctx->notify_baton2, 558251881Speter ctx->cancel_func, ctx->cancel_baton, 559251881Speter scratch_pool), 560251881Speter ctx->wc_ctx, dir_abspath, FALSE, scratch_pool); 561251881Speter else 562251881Speter SVN_ERR(copy_foreign_dir(ra_session, loc, 563251881Speter ctx->wc_ctx, dst_abspath, 564251881Speter depth, 565251881Speter ctx->notify_func2, ctx->notify_baton2, 566251881Speter ctx->cancel_func, ctx->cancel_baton, 567251881Speter scratch_pool)); 568251881Speter } 569251881Speter 570251881Speter return SVN_NO_ERROR; 571251881Speter} 572