copy.c revision 299742
1/* 2 * copy.c: wc 'copy' functionality. 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24/* ==================================================================== */ 25 26 27 28/*** Includes. ***/ 29 30#include <string.h> 31#include "svn_pools.h" 32#include "svn_error.h" 33#include "svn_dirent_uri.h" 34#include "svn_path.h" 35#include "svn_hash.h" 36 37#include "wc.h" 38#include "workqueue.h" 39#include "props.h" 40#include "conflicts.h" 41 42#include "svn_private_config.h" 43#include "private/svn_wc_private.h" 44 45/* #define RECORD_MIXED_MOVE */ 46 47/*** Code. ***/ 48 49/* Make a copy of the filesystem node (or tree if RECURSIVE) at 50 SRC_ABSPATH under a temporary name in the directory 51 TMPDIR_ABSPATH and return the absolute path of the copy in 52 *DST_ABSPATH. Return the node kind of SRC_ABSPATH in *KIND. If 53 SRC_ABSPATH doesn't exist then set *DST_ABSPATH to NULL to indicate 54 that no copy was made. 55 56 If DIRENT is not NULL, it contains the on-disk information of SRC_ABSPATH. 57 RECORDED_SIZE (if not SVN_INVALID_FILESIZE) contains the recorded size of 58 SRC_ABSPATH, and RECORDED_TIME the recorded size or 0. 59 60 These values will be used to avoid unneeded work. 61 */ 62static svn_error_t * 63copy_to_tmpdir(svn_skel_t **work_item, 64 svn_node_kind_t *kind, 65 svn_wc__db_t *db, 66 const char *src_abspath, 67 const char *dst_abspath, 68 const char *tmpdir_abspath, 69 svn_boolean_t file_copy, 70 svn_boolean_t unversioned, 71 const svn_io_dirent2_t *dirent, 72 svn_filesize_t recorded_size, 73 apr_time_t recorded_time, 74 svn_cancel_func_t cancel_func, 75 void *cancel_baton, 76 apr_pool_t *result_pool, 77 apr_pool_t *scratch_pool) 78{ 79 svn_boolean_t is_special; 80 svn_io_file_del_t delete_when; 81 const char *dst_tmp_abspath; 82 svn_node_kind_t dsk_kind; 83 if (!kind) 84 kind = &dsk_kind; 85 86 *work_item = NULL; 87 88 if (dirent) 89 { 90 *kind = dirent->kind; 91 is_special = dirent->special; 92 } 93 else 94 SVN_ERR(svn_io_check_special_path(src_abspath, kind, &is_special, 95 scratch_pool)); 96 if (*kind == svn_node_none) 97 { 98 return SVN_NO_ERROR; 99 } 100 else if (*kind == svn_node_unknown) 101 { 102 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, 103 _("Source '%s' is unexpected kind"), 104 svn_dirent_local_style(src_abspath, 105 scratch_pool)); 106 } 107 else if (*kind == svn_node_dir || is_special) 108 delete_when = svn_io_file_del_on_close; 109 else /* the default case: (*kind == svn_node_file) */ 110 delete_when = svn_io_file_del_none; 111 112 /* ### Do we need a pool cleanup to remove the copy? We can't use 113 ### svn_io_file_del_on_pool_cleanup above because a) it won't 114 ### handle the directory case and b) we need to be able to remove 115 ### the cleanup before queueing the move work item. */ 116 117 if (file_copy && !unversioned) 118 { 119 svn_boolean_t modified; 120 /* It's faster to look for mods on the source now, as 121 the timestamp might match, than to examine the 122 destination later as the destination timestamp will 123 never match. */ 124 125 if (dirent 126 && dirent->kind == svn_node_file 127 && recorded_size != SVN_INVALID_FILESIZE 128 && recorded_size == dirent->filesize 129 && recorded_time == dirent->mtime) 130 { 131 modified = FALSE; /* Recorded matches on-disk. Easy out */ 132 } 133 else 134 { 135 SVN_ERR(svn_wc__internal_file_modified_p(&modified, db, src_abspath, 136 FALSE, scratch_pool)); 137 } 138 139 if (!modified) 140 { 141 /* Why create a temp copy if we can just reinstall from pristine? */ 142 SVN_ERR(svn_wc__wq_build_file_install(work_item, 143 db, dst_abspath, NULL, FALSE, 144 TRUE, 145 result_pool, scratch_pool)); 146 return SVN_NO_ERROR; 147 } 148 } 149 else if (*kind == svn_node_dir && !file_copy) 150 { 151 /* Just build a new direcory from the workqueue */ 152 SVN_ERR(svn_wc__wq_build_dir_install(work_item, 153 db, dst_abspath, 154 result_pool, scratch_pool)); 155 156 return SVN_NO_ERROR; 157 } 158 159 /* Set DST_TMP_ABSPATH to a temporary unique path. If *KIND is file, leave 160 a file there and then overwrite it; otherwise leave no node on disk at 161 that path. In the latter case, something else might use that path 162 before we get around to using it a moment later, but never mind. */ 163 SVN_ERR(svn_io_open_unique_file3(NULL, &dst_tmp_abspath, tmpdir_abspath, 164 delete_when, scratch_pool, scratch_pool)); 165 166 if (*kind == svn_node_dir) 167 { 168 if (file_copy) 169 SVN_ERR(svn_io_copy_dir_recursively( 170 src_abspath, 171 tmpdir_abspath, 172 svn_dirent_basename(dst_tmp_abspath, scratch_pool), 173 TRUE, /* copy_perms */ 174 cancel_func, cancel_baton, 175 scratch_pool)); 176 else 177 SVN_ERR(svn_io_dir_make(dst_tmp_abspath, APR_OS_DEFAULT, scratch_pool)); 178 } 179 else if (!is_special) 180 SVN_ERR(svn_io_copy_file(src_abspath, dst_tmp_abspath, 181 TRUE /* copy_perms */, 182 scratch_pool)); 183 else 184 SVN_ERR(svn_io_copy_link(src_abspath, dst_tmp_abspath, scratch_pool)); 185 186 if (file_copy) 187 { 188 /* Remove 'read-only' from the destination file; it's a local add now. */ 189 SVN_ERR(svn_io_set_file_read_write(dst_tmp_abspath, 190 FALSE, scratch_pool)); 191 } 192 193 SVN_ERR(svn_wc__wq_build_file_move(work_item, db, dst_abspath, 194 dst_tmp_abspath, dst_abspath, 195 result_pool, scratch_pool)); 196 197 return SVN_NO_ERROR; 198} 199 200/* Copy the versioned file SRC_ABSPATH in DB to the path DST_ABSPATH in DB. 201 If METADATA_ONLY is true, copy only the versioned metadata, 202 otherwise copy both the versioned metadata and the filesystem node (even 203 if it is the wrong kind, and recursively if it is a dir). 204 205 If IS_MOVE is true, record move information in working copy meta 206 data in addition to copying the file. 207 208 If the versioned file has a text conflict, and the .mine file exists in 209 the filesystem, copy the .mine file to DST_ABSPATH. Otherwise, copy the 210 versioned file itself. 211 212 This also works for versioned symlinks that are stored in the db as 213 svn_node_file with svn:special set. 214 215 If DIRENT is not NULL, it contains the on-disk information of SRC_ABSPATH. 216 RECORDED_SIZE (if not SVN_INVALID_FILESIZE) contains the recorded size of 217 SRC_ABSPATH, and RECORDED_TIME the recorded size or 0. 218 219 These values will be used to avoid unneeded work. 220*/ 221static svn_error_t * 222copy_versioned_file(svn_wc__db_t *db, 223 const char *src_abspath, 224 const char *dst_abspath, 225 const char *dst_op_root_abspath, 226 const char *tmpdir_abspath, 227 svn_boolean_t metadata_only, 228 svn_boolean_t conflicted, 229 svn_boolean_t is_move, 230 const svn_io_dirent2_t *dirent, 231 svn_filesize_t recorded_size, 232 apr_time_t recorded_time, 233 svn_cancel_func_t cancel_func, 234 void *cancel_baton, 235 svn_wc_notify_func2_t notify_func, 236 void *notify_baton, 237 apr_pool_t *scratch_pool) 238{ 239 svn_skel_t *work_items = NULL; 240 241 /* In case we are copying from one WC to another (e.g. an external dir), 242 ensure the destination WC has a copy of the pristine text. */ 243 244 /* Prepare a temp copy of the filesystem node. It is usually a file, but 245 copy recursively if it's a dir. */ 246 if (!metadata_only) 247 { 248 const char *my_src_abspath = NULL; 249 svn_boolean_t handle_as_unversioned = FALSE; 250 251 /* By default, take the copy source as given. */ 252 my_src_abspath = src_abspath; 253 254 if (conflicted) 255 { 256 svn_skel_t *conflict; 257 const char *conflict_working; 258 svn_error_t *err; 259 260 /* Is there a text conflict at the source path? */ 261 SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL, 262 db, src_abspath, 263 scratch_pool, scratch_pool)); 264 265 err = svn_wc__conflict_read_text_conflict(&conflict_working, NULL, NULL, 266 db, src_abspath, conflict, 267 scratch_pool, 268 scratch_pool); 269 270 if (err && err->apr_err == SVN_ERR_WC_MISSING) 271 { 272 /* not text conflicted */ 273 svn_error_clear(err); 274 conflict_working = NULL; 275 } 276 else 277 SVN_ERR(err); 278 279 if (conflict_working) 280 { 281 svn_node_kind_t working_kind; 282 283 /* Does the ".mine" file exist? */ 284 SVN_ERR(svn_io_check_path(conflict_working, &working_kind, 285 scratch_pool)); 286 287 if (working_kind == svn_node_file) 288 { 289 /* Don't perform unmodified/pristine optimization */ 290 handle_as_unversioned = TRUE; 291 my_src_abspath = conflict_working; 292 } 293 } 294 } 295 296 SVN_ERR(copy_to_tmpdir(&work_items, NULL, db, my_src_abspath, 297 dst_abspath, tmpdir_abspath, 298 TRUE /* file_copy */, 299 handle_as_unversioned /* unversioned */, 300 dirent, recorded_size, recorded_time, 301 cancel_func, cancel_baton, 302 scratch_pool, scratch_pool)); 303 } 304 305 /* Copy the (single) node's metadata, and move the new filesystem node 306 into place. */ 307 SVN_ERR(svn_wc__db_op_copy(db, src_abspath, dst_abspath, 308 dst_op_root_abspath, is_move, work_items, 309 scratch_pool)); 310 311 if (notify_func) 312 { 313 svn_wc_notify_t *notify 314 = svn_wc_create_notify(dst_abspath, svn_wc_notify_add, 315 scratch_pool); 316 notify->kind = svn_node_file; 317 318 (*notify_func)(notify_baton, notify, scratch_pool); 319 } 320 return SVN_NO_ERROR; 321} 322 323/* Copy the versioned dir SRC_ABSPATH in DB to the path DST_ABSPATH in DB, 324 recursively. If METADATA_ONLY is true, copy only the versioned metadata, 325 otherwise copy both the versioned metadata and the filesystem nodes (even 326 if they are the wrong kind, and including unversioned children). 327 If IS_MOVE is true, record move information in working copy meta 328 data in addition to copying the directory. 329 330 WITHIN_ONE_WC is TRUE if the copy/move is within a single working copy (root) 331 332 If DIRENT is not NULL, it contains the on-disk information of SRC_ABSPATH. 333 */ 334static svn_error_t * 335copy_versioned_dir(svn_wc__db_t *db, 336 const char *src_abspath, 337 const char *dst_abspath, 338 const char *dst_op_root_abspath, 339 const char *tmpdir_abspath, 340 svn_boolean_t metadata_only, 341 svn_boolean_t is_move, 342 const svn_io_dirent2_t *dirent, 343 svn_cancel_func_t cancel_func, 344 void *cancel_baton, 345 svn_wc_notify_func2_t notify_func, 346 void *notify_baton, 347 apr_pool_t *scratch_pool) 348{ 349 svn_skel_t *work_items = NULL; 350 const char *dir_abspath = svn_dirent_dirname(dst_abspath, scratch_pool); 351 apr_hash_t *versioned_children; 352 apr_hash_t *conflicted_children; 353 apr_hash_t *disk_children; 354 apr_hash_index_t *hi; 355 svn_node_kind_t disk_kind; 356 apr_pool_t *iterpool; 357 358 /* Prepare a temp copy of the single filesystem node (usually a dir). */ 359 if (!metadata_only) 360 { 361 SVN_ERR(copy_to_tmpdir(&work_items, &disk_kind, 362 db, src_abspath, dst_abspath, 363 tmpdir_abspath, 364 FALSE /* file_copy */, 365 FALSE /* unversioned */, 366 dirent, SVN_INVALID_FILESIZE, 0, 367 cancel_func, cancel_baton, 368 scratch_pool, scratch_pool)); 369 } 370 371 /* Copy the (single) node's metadata, and move the new filesystem node 372 into place. */ 373 SVN_ERR(svn_wc__db_op_copy(db, src_abspath, dst_abspath, 374 dst_op_root_abspath, is_move, work_items, 375 scratch_pool)); 376 377 if (notify_func) 378 { 379 svn_wc_notify_t *notify 380 = svn_wc_create_notify(dst_abspath, svn_wc_notify_add, 381 scratch_pool); 382 notify->kind = svn_node_dir; 383 384 /* When we notify that we performed a copy, make sure we already did */ 385 if (work_items != NULL) 386 SVN_ERR(svn_wc__wq_run(db, dir_abspath, 387 cancel_func, cancel_baton, scratch_pool)); 388 389 (*notify_func)(notify_baton, notify, scratch_pool); 390 } 391 392 if (!metadata_only && disk_kind == svn_node_dir) 393 /* All filesystem children, versioned and unversioned. We're only 394 interested in their names, so we can pass TRUE as the only_check_type 395 param. */ 396 SVN_ERR(svn_io_get_dirents3(&disk_children, src_abspath, TRUE, 397 scratch_pool, scratch_pool)); 398 else 399 disk_children = NULL; 400 401 /* Copy all the versioned children */ 402 iterpool = svn_pool_create(scratch_pool); 403 SVN_ERR(svn_wc__db_read_children_info(&versioned_children, 404 &conflicted_children, 405 db, src_abspath, 406 FALSE /* base_tree_only */, 407 scratch_pool, iterpool)); 408 for (hi = apr_hash_first(scratch_pool, versioned_children); 409 hi; 410 hi = apr_hash_next(hi)) 411 { 412 const char *child_name, *child_src_abspath, *child_dst_abspath; 413 struct svn_wc__db_info_t *info; 414 415 svn_pool_clear(iterpool); 416 417 if (cancel_func) 418 SVN_ERR(cancel_func(cancel_baton)); 419 420 child_name = apr_hash_this_key(hi); 421 info = apr_hash_this_val(hi); 422 child_src_abspath = svn_dirent_join(src_abspath, child_name, iterpool); 423 child_dst_abspath = svn_dirent_join(dst_abspath, child_name, iterpool); 424 425 if (info->op_root) 426 SVN_ERR(svn_wc__db_op_copy_shadowed_layer(db, 427 child_src_abspath, 428 child_dst_abspath, 429 is_move, 430 scratch_pool)); 431 432 if (info->status == svn_wc__db_status_normal 433 || info->status == svn_wc__db_status_added) 434 { 435 /* We have more work to do than just changing the DB */ 436 if (info->kind == svn_node_file) 437 { 438 /* We should skip this node if this child is a file external 439 (issues #3589, #4000) */ 440 if (!info->file_external) 441 SVN_ERR(copy_versioned_file(db, 442 child_src_abspath, 443 child_dst_abspath, 444 dst_op_root_abspath, 445 tmpdir_abspath, 446 metadata_only, info->conflicted, 447 is_move, 448 disk_children 449 ? svn_hash_gets(disk_children, 450 child_name) 451 : NULL, 452 info->recorded_size, 453 info->recorded_time, 454 cancel_func, cancel_baton, 455 NULL, NULL, 456 iterpool)); 457 } 458 else if (info->kind == svn_node_dir) 459 SVN_ERR(copy_versioned_dir(db, 460 child_src_abspath, child_dst_abspath, 461 dst_op_root_abspath, tmpdir_abspath, 462 metadata_only, is_move, 463 disk_children 464 ? svn_hash_gets(disk_children, 465 child_name) 466 : NULL, 467 cancel_func, cancel_baton, NULL, NULL, 468 iterpool)); 469 else 470 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, 471 _("cannot handle node kind for '%s'"), 472 svn_dirent_local_style(child_src_abspath, 473 scratch_pool)); 474 } 475 else if (info->status == svn_wc__db_status_deleted 476 || info->status == svn_wc__db_status_not_present 477 || info->status == svn_wc__db_status_excluded) 478 { 479 /* This will be copied as some kind of deletion. Don't touch 480 any actual files */ 481 SVN_ERR(svn_wc__db_op_copy(db, child_src_abspath, 482 child_dst_abspath, dst_op_root_abspath, 483 is_move, NULL, iterpool)); 484 485 /* Don't recurse on children when all we do is creating not-present 486 children */ 487 } 488 else if (info->status == svn_wc__db_status_incomplete) 489 { 490 /* Should go ahead and copy incomplete to incomplete? Try to 491 copy as much as possible, or give up early? */ 492 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 493 _("Cannot handle status of '%s'"), 494 svn_dirent_local_style(child_src_abspath, 495 iterpool)); 496 } 497 else 498 { 499 SVN_ERR_ASSERT(info->status == svn_wc__db_status_server_excluded); 500 501 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 502 _("Cannot copy '%s' excluded by server"), 503 svn_dirent_local_style(child_src_abspath, 504 iterpool)); 505 } 506 507 if (disk_children 508 && (info->status == svn_wc__db_status_normal 509 || info->status == svn_wc__db_status_added)) 510 { 511 /* Remove versioned child as it has been handled */ 512 svn_hash_sets(disk_children, child_name, NULL); 513 } 514 } 515 516 /* Copy the remaining filesystem children, which are unversioned, skipping 517 any conflict-marker files. */ 518 if (disk_children && apr_hash_count(disk_children)) 519 { 520 apr_hash_t *marker_files; 521 522 SVN_ERR(svn_wc__db_get_conflict_marker_files(&marker_files, db, 523 src_abspath, scratch_pool, 524 scratch_pool)); 525 526 work_items = NULL; 527 528 for (hi = apr_hash_first(scratch_pool, disk_children); hi; 529 hi = apr_hash_next(hi)) 530 { 531 const char *name = apr_hash_this_key(hi); 532 const char *unver_src_abspath, *unver_dst_abspath; 533 svn_skel_t *work_item; 534 535 if (svn_wc_is_adm_dir(name, iterpool)) 536 continue; 537 538 if (cancel_func) 539 SVN_ERR(cancel_func(cancel_baton)); 540 541 svn_pool_clear(iterpool); 542 unver_src_abspath = svn_dirent_join(src_abspath, name, iterpool); 543 unver_dst_abspath = svn_dirent_join(dst_abspath, name, iterpool); 544 545 if (marker_files && 546 svn_hash_gets(marker_files, unver_src_abspath)) 547 continue; 548 549 SVN_ERR(copy_to_tmpdir(&work_item, NULL, db, unver_src_abspath, 550 unver_dst_abspath, tmpdir_abspath, 551 TRUE /* recursive */, TRUE /* unversioned */, 552 NULL, SVN_INVALID_FILESIZE, 0, 553 cancel_func, cancel_baton, 554 scratch_pool, iterpool)); 555 556 if (work_item) 557 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); 558 } 559 SVN_ERR(svn_wc__db_wq_add(db, dst_abspath, work_items, iterpool)); 560 } 561 562 svn_pool_destroy(iterpool); 563 564 return SVN_NO_ERROR; 565} 566 567 568/* The guts of svn_wc_copy3() and svn_wc_move(). 569 * The additional parameter IS_MOVE indicates whether this is a copy or 570 * a move operation. 571 * 572 * If RECORD_MOVE_ON_DELETE is not NULL and a move had to be degraded 573 * to a copy, then set *RECORD_MOVE_ON_DELETE to FALSE. */ 574static svn_error_t * 575copy_or_move(svn_boolean_t *record_move_on_delete, 576 svn_wc_context_t *wc_ctx, 577 const char *src_abspath, 578 const char *dst_abspath, 579 svn_boolean_t metadata_only, 580 svn_boolean_t is_move, 581 svn_boolean_t allow_mixed_revisions, 582 svn_cancel_func_t cancel_func, 583 void *cancel_baton, 584 svn_wc_notify_func2_t notify_func, 585 void *notify_baton, 586 apr_pool_t *scratch_pool) 587{ 588 svn_wc__db_t *db = wc_ctx->db; 589 svn_node_kind_t src_db_kind; 590 const char *dstdir_abspath; 591 svn_boolean_t conflicted; 592 const char *tmpdir_abspath; 593 const char *src_wcroot_abspath; 594 const char *dst_wcroot_abspath; 595 svn_boolean_t within_one_wc; 596 svn_wc__db_status_t src_status; 597 svn_error_t *err; 598 svn_filesize_t recorded_size; 599 apr_time_t recorded_time; 600 601 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath)); 602 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath)); 603 604 dstdir_abspath = svn_dirent_dirname(dst_abspath, scratch_pool); 605 606 /* Ensure DSTDIR_ABSPATH belongs to the same repository as SRC_ABSPATH; 607 throw an error if not. */ 608 { 609 svn_wc__db_status_t dstdir_status; 610 const char *src_repos_root_url, *dst_repos_root_url; 611 const char *src_repos_uuid, *dst_repos_uuid; 612 const char *src_repos_relpath; 613 614 err = svn_wc__db_read_info(&src_status, &src_db_kind, NULL, 615 &src_repos_relpath, &src_repos_root_url, 616 &src_repos_uuid, NULL, NULL, NULL, NULL, NULL, 617 NULL, NULL, NULL, NULL, NULL, NULL, 618 &recorded_size, &recorded_time, 619 NULL, &conflicted, NULL, NULL, NULL, NULL, 620 NULL, NULL, 621 db, src_abspath, scratch_pool, scratch_pool); 622 623 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) 624 { 625 /* Replicate old error code and text */ 626 svn_error_clear(err); 627 return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL, 628 _("'%s' is not under version control"), 629 svn_dirent_local_style(src_abspath, 630 scratch_pool)); 631 } 632 else 633 SVN_ERR(err); 634 635 /* Do this now, as we know the right data is cached */ 636 SVN_ERR(svn_wc__db_get_wcroot(&src_wcroot_abspath, db, src_abspath, 637 scratch_pool, scratch_pool)); 638 639 switch (src_status) 640 { 641 case svn_wc__db_status_deleted: 642 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 643 _("Deleted node '%s' can't be copied."), 644 svn_dirent_local_style(src_abspath, 645 scratch_pool)); 646 647 case svn_wc__db_status_excluded: 648 case svn_wc__db_status_server_excluded: 649 case svn_wc__db_status_not_present: 650 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, 651 _("The node '%s' was not found."), 652 svn_dirent_local_style(src_abspath, 653 scratch_pool)); 654 default: 655 break; 656 } 657 658 if (is_move && ! strcmp(src_abspath, src_wcroot_abspath)) 659 { 660 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 661 _("'%s' is the root of a working copy and " 662 "cannot be moved"), 663 svn_dirent_local_style(src_abspath, 664 scratch_pool)); 665 } 666 if (is_move && src_repos_relpath && !src_repos_relpath[0]) 667 { 668 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 669 _("'%s' represents the repository root " 670 "and cannot be moved"), 671 svn_dirent_local_style(src_abspath, 672 scratch_pool)); 673 } 674 675 err = svn_wc__db_read_info(&dstdir_status, NULL, NULL, NULL, 676 &dst_repos_root_url, &dst_repos_uuid, NULL, 677 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 678 NULL, NULL, NULL, NULL, NULL, NULL, 679 NULL, NULL, NULL, NULL, 680 NULL, NULL, NULL, 681 db, dstdir_abspath, 682 scratch_pool, scratch_pool); 683 684 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) 685 { 686 /* An unversioned destination directory exists on disk. */ 687 svn_error_clear(err); 688 return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL, 689 _("'%s' is not under version control"), 690 svn_dirent_local_style(dstdir_abspath, 691 scratch_pool)); 692 } 693 else 694 SVN_ERR(err); 695 696 /* Do this now, as we know the right data is cached */ 697 SVN_ERR(svn_wc__db_get_wcroot(&dst_wcroot_abspath, db, dstdir_abspath, 698 scratch_pool, scratch_pool)); 699 700 if (!src_repos_root_url) 701 { 702 if (src_status == svn_wc__db_status_added) 703 SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL, 704 &src_repos_root_url, 705 &src_repos_uuid, NULL, NULL, NULL, 706 NULL, 707 db, src_abspath, 708 scratch_pool, scratch_pool)); 709 else 710 /* If not added, the node must have a base or we can't copy */ 711 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, 712 &src_repos_root_url, 713 &src_repos_uuid, NULL, NULL, NULL, 714 NULL, NULL, NULL, NULL, NULL, NULL, 715 NULL, 716 db, src_abspath, 717 scratch_pool, scratch_pool)); 718 } 719 720 if (!dst_repos_root_url) 721 { 722 if (dstdir_status == svn_wc__db_status_added) 723 SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL, 724 &dst_repos_root_url, 725 &dst_repos_uuid, NULL, NULL, NULL, 726 NULL, 727 db, dstdir_abspath, 728 scratch_pool, scratch_pool)); 729 else 730 /* If not added, the node must have a base or we can't copy */ 731 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, 732 &dst_repos_root_url, 733 &dst_repos_uuid, NULL, NULL, NULL, 734 NULL, NULL, NULL, NULL, NULL, NULL, 735 NULL, 736 db, dstdir_abspath, 737 scratch_pool, scratch_pool)); 738 } 739 740 if (strcmp(src_repos_root_url, dst_repos_root_url) != 0 741 || strcmp(src_repos_uuid, dst_repos_uuid) != 0) 742 return svn_error_createf( 743 SVN_ERR_WC_INVALID_SCHEDULE, NULL, 744 _("Cannot copy to '%s', as it is not from repository '%s'; " 745 "it is from '%s'"), 746 svn_dirent_local_style(dst_abspath, scratch_pool), 747 src_repos_root_url, dst_repos_root_url); 748 749 if (dstdir_status == svn_wc__db_status_deleted) 750 return svn_error_createf( 751 SVN_ERR_WC_INVALID_SCHEDULE, NULL, 752 _("Cannot copy to '%s' as it is scheduled for deletion"), 753 svn_dirent_local_style(dst_abspath, scratch_pool)); 754 /* ### should report dstdir_abspath instead of dst_abspath? */ 755 } 756 757 /* TODO(#2843): Rework the error report. */ 758 /* Check if the copy target is missing or hidden and thus not exist on the 759 disk, before actually doing the file copy. */ 760 { 761 svn_wc__db_status_t dst_status; 762 763 err = svn_wc__db_read_info(&dst_status, NULL, NULL, NULL, NULL, NULL, 764 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 765 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 766 NULL, NULL, NULL, NULL, NULL, 767 db, dst_abspath, scratch_pool, scratch_pool); 768 769 if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 770 return svn_error_trace(err); 771 772 svn_error_clear(err); 773 774 if (!err) 775 switch (dst_status) 776 { 777 case svn_wc__db_status_excluded: 778 return svn_error_createf( 779 SVN_ERR_ENTRY_EXISTS, NULL, 780 _("'%s' is already under version control " 781 "but is excluded."), 782 svn_dirent_local_style(dst_abspath, scratch_pool)); 783 case svn_wc__db_status_server_excluded: 784 return svn_error_createf( 785 SVN_ERR_ENTRY_EXISTS, NULL, 786 _("'%s' is already under version control"), 787 svn_dirent_local_style(dst_abspath, scratch_pool)); 788 789 case svn_wc__db_status_deleted: 790 case svn_wc__db_status_not_present: 791 break; /* OK to add */ 792 793 default: 794 return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL, 795 _("There is already a versioned item '%s'"), 796 svn_dirent_local_style(dst_abspath, 797 scratch_pool)); 798 } 799 } 800 801 /* Check that the target path is not obstructed, if required. */ 802 if (!metadata_only) 803 { 804 svn_node_kind_t dst_kind; 805 806 /* (We need only to check the root of the copy, not every path inside 807 copy_versioned_file/_dir.) */ 808 SVN_ERR(svn_io_check_path(dst_abspath, &dst_kind, scratch_pool)); 809 if (dst_kind != svn_node_none) 810 return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL, 811 _("'%s' already exists and is in the way"), 812 svn_dirent_local_style(dst_abspath, 813 scratch_pool)); 814 } 815 816 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmpdir_abspath, db, 817 dstdir_abspath, 818 scratch_pool, scratch_pool)); 819 820 within_one_wc = (strcmp(src_wcroot_abspath, dst_wcroot_abspath) == 0); 821 822 if (is_move 823 && !within_one_wc) 824 { 825 if (record_move_on_delete) 826 *record_move_on_delete = FALSE; 827 828 is_move = FALSE; 829 } 830 831 if (!within_one_wc) 832 SVN_ERR(svn_wc__db_pristine_transfer(db, src_abspath, dst_wcroot_abspath, 833 cancel_func, cancel_baton, 834 scratch_pool)); 835 836 if (src_db_kind == svn_node_file 837 || src_db_kind == svn_node_symlink) 838 { 839 err = copy_versioned_file(db, src_abspath, dst_abspath, dst_abspath, 840 tmpdir_abspath, 841 metadata_only, conflicted, is_move, 842 NULL, recorded_size, recorded_time, 843 cancel_func, cancel_baton, 844 notify_func, notify_baton, 845 scratch_pool); 846 } 847 else 848 { 849 if (is_move 850 && src_status == svn_wc__db_status_normal) 851 { 852 svn_revnum_t min_rev; 853 svn_revnum_t max_rev; 854 855 /* Verify that the move source is a single-revision subtree. */ 856 SVN_ERR(svn_wc__db_min_max_revisions(&min_rev, &max_rev, db, 857 src_abspath, FALSE, scratch_pool)); 858 if (SVN_IS_VALID_REVNUM(min_rev) && SVN_IS_VALID_REVNUM(max_rev) && 859 min_rev != max_rev) 860 { 861 if (!allow_mixed_revisions) 862 return svn_error_createf(SVN_ERR_WC_MIXED_REVISIONS, NULL, 863 _("Cannot move mixed-revision " 864 "subtree '%s' [%ld:%ld]; " 865 "try updating it first"), 866 svn_dirent_local_style(src_abspath, 867 scratch_pool), 868 min_rev, max_rev); 869 870#ifndef RECORD_MIXED_MOVE 871 is_move = FALSE; 872 if (record_move_on_delete) 873 *record_move_on_delete = FALSE; 874#endif 875 } 876 } 877 878 err = copy_versioned_dir(db, src_abspath, dst_abspath, dst_abspath, 879 tmpdir_abspath, metadata_only, is_move, 880 NULL /* dirent */, 881 cancel_func, cancel_baton, 882 notify_func, notify_baton, 883 scratch_pool); 884 } 885 886 if (err && svn_error_find_cause(err, SVN_ERR_CANCELLED)) 887 return svn_error_trace(err); 888 889 if (is_move) 890 err = svn_error_compose_create(err, 891 svn_wc__db_op_handle_move_back(NULL, 892 db, dst_abspath, src_abspath, 893 NULL /* work_items */, 894 scratch_pool)); 895 896 /* Run the work queue with the remaining work */ 897 SVN_ERR(svn_error_compose_create( 898 err, 899 svn_wc__wq_run(db, dst_abspath, 900 cancel_func, cancel_baton, 901 scratch_pool))); 902 903 return SVN_NO_ERROR; 904} 905 906 907/* Public Interface */ 908 909svn_error_t * 910svn_wc_copy3(svn_wc_context_t *wc_ctx, 911 const char *src_abspath, 912 const char *dst_abspath, 913 svn_boolean_t metadata_only, 914 svn_cancel_func_t cancel_func, 915 void *cancel_baton, 916 svn_wc_notify_func2_t notify_func, 917 void *notify_baton, 918 apr_pool_t *scratch_pool) 919{ 920 /* Verify that we have the required write lock. */ 921 SVN_ERR(svn_wc__write_check(wc_ctx->db, 922 svn_dirent_dirname(dst_abspath, scratch_pool), 923 scratch_pool)); 924 925 return svn_error_trace(copy_or_move(NULL, wc_ctx, src_abspath, dst_abspath, 926 metadata_only, FALSE /* is_move */, 927 TRUE /* allow_mixed_revisions */, 928 cancel_func, cancel_baton, 929 notify_func, notify_baton, 930 scratch_pool)); 931} 932 933 934/* Remove the conflict markers of NODE_ABSPATH, that were left over after 935 copying NODE_ABSPATH from SRC_ABSPATH. 936 937 Only use this function when you know what you're doing. This function 938 explicitly ignores some case insensitivity issues! 939 940 */ 941static svn_error_t * 942remove_node_conflict_markers(svn_wc__db_t *db, 943 const char *src_abspath, 944 const char *node_abspath, 945 apr_pool_t *scratch_pool) 946{ 947 svn_skel_t *conflict; 948 949 SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL, 950 db, src_abspath, 951 scratch_pool, scratch_pool)); 952 953 /* Do we have conflict markers that should be removed? */ 954 if (conflict != NULL) 955 { 956 const apr_array_header_t *markers; 957 int i; 958 const char *src_dir = svn_dirent_dirname(src_abspath, scratch_pool); 959 const char *dst_dir = svn_dirent_dirname(node_abspath, scratch_pool); 960 961 SVN_ERR(svn_wc__conflict_read_markers(&markers, db, src_abspath, 962 conflict, 963 scratch_pool, scratch_pool)); 964 965 /* No iterpool: Maximum number of possible conflict markers is 4 */ 966 for (i = 0; markers && (i < markers->nelts); i++) 967 { 968 const char *marker_abspath; 969 const char *child_relpath; 970 const char *child_abspath; 971 972 marker_abspath = APR_ARRAY_IDX(markers, i, const char *); 973 974 child_relpath = svn_dirent_skip_ancestor(src_dir, marker_abspath); 975 976 if (child_relpath) 977 { 978 child_abspath = svn_dirent_join(dst_dir, child_relpath, 979 scratch_pool); 980 981 SVN_ERR(svn_io_remove_file2(child_abspath, TRUE, scratch_pool)); 982 } 983 } 984 } 985 986 return SVN_NO_ERROR; 987} 988 989/* Remove all the conflict markers below SRC_DIR_ABSPATH, that were left over 990 after copying WC_DIR_ABSPATH from SRC_DIR_ABSPATH. 991 992 This function doesn't remove the conflict markers on WC_DIR_ABSPATH 993 itself! 994 995 Only use this function when you know what you're doing. This function 996 explicitly ignores some case insensitivity issues! 997 */ 998static svn_error_t * 999remove_all_conflict_markers(svn_wc__db_t *db, 1000 const char *src_dir_abspath, 1001 const char *dst_dir_abspath, 1002 svn_cancel_func_t cancel_func, 1003 void *cancel_baton, 1004 apr_pool_t *scratch_pool) 1005{ 1006 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1007 apr_hash_t *nodes; 1008 apr_hash_t *conflicts; /* Unused */ 1009 apr_hash_index_t *hi; 1010 1011 /* Reuse a status helper to obtain all subdirs and conflicts in a single 1012 db transaction. */ 1013 /* ### This uses a rifle to kill a fly. But at least it doesn't use heavy 1014 artillery. */ 1015 SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, db, 1016 src_dir_abspath, 1017 FALSE /* base_tree_only */, 1018 scratch_pool, iterpool)); 1019 1020 for (hi = apr_hash_first(scratch_pool, nodes); 1021 hi; 1022 hi = apr_hash_next(hi)) 1023 { 1024 const char *name = apr_hash_this_key(hi); 1025 struct svn_wc__db_info_t *info = apr_hash_this_val(hi); 1026 1027 if (cancel_func) 1028 SVN_ERR(cancel_func(cancel_baton)); 1029 1030 if (info->conflicted) 1031 { 1032 svn_pool_clear(iterpool); 1033 SVN_ERR(remove_node_conflict_markers( 1034 db, 1035 svn_dirent_join(src_dir_abspath, name, iterpool), 1036 svn_dirent_join(dst_dir_abspath, name, iterpool), 1037 iterpool)); 1038 } 1039 if (info->kind == svn_node_dir) 1040 { 1041 svn_pool_clear(iterpool); 1042 SVN_ERR(remove_all_conflict_markers( 1043 db, 1044 svn_dirent_join(src_dir_abspath, name, iterpool), 1045 svn_dirent_join(dst_dir_abspath, name, iterpool), 1046 cancel_func, cancel_baton, 1047 iterpool)); 1048 } 1049 } 1050 1051 svn_pool_destroy(iterpool); 1052 return SVN_NO_ERROR; 1053} 1054 1055svn_error_t * 1056svn_wc__move2(svn_wc_context_t *wc_ctx, 1057 const char *src_abspath, 1058 const char *dst_abspath, 1059 svn_boolean_t metadata_only, 1060 svn_boolean_t allow_mixed_revisions, 1061 svn_cancel_func_t cancel_func, 1062 void *cancel_baton, 1063 svn_wc_notify_func2_t notify_func, 1064 void *notify_baton, 1065 apr_pool_t *scratch_pool) 1066{ 1067 svn_wc__db_t *db = wc_ctx->db; 1068 svn_boolean_t record_on_delete = TRUE; 1069 svn_node_kind_t kind; 1070 svn_boolean_t conflicted; 1071 1072 /* Verify that we have the required write locks. */ 1073 SVN_ERR(svn_wc__write_check(wc_ctx->db, 1074 svn_dirent_dirname(src_abspath, scratch_pool), 1075 scratch_pool)); 1076 SVN_ERR(svn_wc__write_check(wc_ctx->db, 1077 svn_dirent_dirname(dst_abspath, scratch_pool), 1078 scratch_pool)); 1079 1080 SVN_ERR(copy_or_move(&record_on_delete, 1081 wc_ctx, src_abspath, dst_abspath, 1082 TRUE /* metadata_only */, 1083 TRUE /* is_move */, 1084 allow_mixed_revisions, 1085 cancel_func, cancel_baton, 1086 notify_func, notify_baton, 1087 scratch_pool)); 1088 1089 /* An interrupt at this point will leave the new copy marked as 1090 moved-here but the source has not yet been deleted or marked as 1091 moved-to. */ 1092 1093 /* Should we be using a workqueue for this move? It's not clear. 1094 What should happen if the copy above is interrupted? The user 1095 may want to abort the move and a workqueue might interfere with 1096 that. 1097 1098 BH: On Windows it is not unlikely to encounter an access denied on 1099 this line. Installing the move in the workqueue via the copy_or_move 1100 might make it hard to recover from that situation, while the DB 1101 is still in a valid state. So be careful when switching this over 1102 to the workqueue. */ 1103 if (!metadata_only) 1104 { 1105 svn_error_t *err; 1106 1107 err = svn_error_trace(svn_io_file_rename(src_abspath, dst_abspath, 1108 scratch_pool)); 1109 1110 /* Let's try if we can keep wc.db consistent even when the move 1111 fails. Deleting the target is a wc.db only operation, while 1112 going forward (delaying the error) would try to change 1113 conflict markers, which might also fail. */ 1114 if (err) 1115 return svn_error_trace( 1116 svn_error_compose_create( 1117 err, 1118 svn_wc__db_op_delete(wc_ctx->db, dst_abspath, NULL, TRUE, 1119 NULL, NULL, cancel_func, cancel_baton, 1120 NULL, NULL, 1121 scratch_pool))); 1122 } 1123 1124 SVN_ERR(svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL, NULL, 1125 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1126 NULL, NULL, NULL, NULL, NULL, NULL, 1127 &conflicted, NULL, NULL, NULL, 1128 NULL, NULL, NULL, 1129 db, src_abspath, 1130 scratch_pool, scratch_pool)); 1131 1132 if (kind == svn_node_dir) 1133 SVN_ERR(remove_all_conflict_markers(db, src_abspath, dst_abspath, 1134 cancel_func, cancel_baton, 1135 scratch_pool)); 1136 1137 if (conflicted) 1138 { 1139 /* When we moved a directory, we moved the conflict markers 1140 with the target... if we moved a file we only moved the 1141 file itself and the markers are still in the old location */ 1142 SVN_ERR(remove_node_conflict_markers(db, src_abspath, 1143 (kind == svn_node_dir) 1144 ? dst_abspath 1145 : src_abspath, 1146 scratch_pool)); 1147 } 1148 1149 SVN_ERR(svn_wc__db_op_delete(db, src_abspath, 1150 record_on_delete ? dst_abspath : NULL, 1151 TRUE /* delete_dir_externals */, 1152 NULL /* conflict */, NULL /* work_items */, 1153 cancel_func, cancel_baton, 1154 notify_func, notify_baton, 1155 scratch_pool)); 1156 1157 return SVN_NO_ERROR; 1158} 1159