1/* 2 * externals.c : routines dealing with (file) externals in the working copy 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#include <stdlib.h> 27#include <string.h> 28 29#include <apr_pools.h> 30#include <apr_hash.h> 31#include <apr_tables.h> 32#include <apr_general.h> 33#include <apr_uri.h> 34 35#include "svn_dirent_uri.h" 36#include "svn_path.h" 37#include "svn_error.h" 38#include "svn_hash.h" 39#include "svn_io.h" 40#include "svn_pools.h" 41#include "svn_props.h" 42#include "svn_string.h" 43#include "svn_time.h" 44#include "svn_types.h" 45#include "svn_wc.h" 46 47#include "private/svn_skel.h" 48#include "private/svn_subr_private.h" 49 50#include "wc.h" 51#include "adm_files.h" 52#include "props.h" 53#include "translate.h" 54#include "workqueue.h" 55#include "conflicts.h" 56 57#include "svn_private_config.h" 58 59/** Externals **/ 60 61/* 62 * Look for either 63 * 64 * -r N 65 * -rN 66 * 67 * in the LINE_PARTS array and update the revision field in ITEM with 68 * the revision if the revision is found. Set REV_IDX to the index in 69 * LINE_PARTS where the revision specification starts. Remove from 70 * LINE_PARTS the element(s) that specify the revision. 71 * PARENT_DIRECTORY_DISPLAY and LINE are given to return a nice error 72 * string. 73 * 74 * If this function returns successfully, then LINE_PARTS will have 75 * only two elements in it. 76 */ 77static svn_error_t * 78find_and_remove_externals_revision(int *rev_idx, 79 const char **line_parts, 80 int num_line_parts, 81 svn_wc_external_item2_t *item, 82 const char *parent_directory_display, 83 const char *line, 84 apr_pool_t *pool) 85{ 86 int i; 87 88 for (i = 0; i < 2; ++i) 89 { 90 const char *token = line_parts[i]; 91 92 if (token[0] == '-' && token[1] == 'r') 93 { 94 svn_opt_revision_t end_revision = { svn_opt_revision_unspecified }; 95 const char *digits_ptr; 96 int shift_count; 97 int j; 98 99 *rev_idx = i; 100 101 if (token[2] == '\0') 102 { 103 /* There must be a total of four elements in the line if 104 -r N is used. */ 105 if (num_line_parts != 4) 106 goto parse_error; 107 108 shift_count = 2; 109 digits_ptr = line_parts[i+1]; 110 } 111 else 112 { 113 /* There must be a total of three elements in the line 114 if -rN is used. */ 115 if (num_line_parts != 3) 116 goto parse_error; 117 118 shift_count = 1; 119 digits_ptr = token+2; 120 } 121 122 if (svn_opt_parse_revision(&item->revision, 123 &end_revision, 124 digits_ptr, pool) != 0) 125 goto parse_error; 126 /* We want a single revision, not a range. */ 127 if (end_revision.kind != svn_opt_revision_unspecified) 128 goto parse_error; 129 /* Allow only numbers and dates, not keywords. */ 130 if (item->revision.kind != svn_opt_revision_number 131 && item->revision.kind != svn_opt_revision_date) 132 goto parse_error; 133 134 /* Shift any line elements past the revision specification 135 down over the revision specification. */ 136 for (j = i; j < num_line_parts-shift_count; ++j) 137 line_parts[j] = line_parts[j+shift_count]; 138 line_parts[num_line_parts-shift_count] = NULL; 139 140 /* Found the revision, so leave the function immediately, do 141 * not continue looking for additional revisions. */ 142 return SVN_NO_ERROR; 143 } 144 } 145 146 /* No revision was found, so there must be exactly two items in the 147 line array. */ 148 if (num_line_parts == 2) 149 return SVN_NO_ERROR; 150 151 parse_error: 152 return svn_error_createf 153 (SVN_ERR_CLIENT_INVALID_EXTERNALS_DESCRIPTION, NULL, 154 _("Error parsing %s property on '%s': '%s'"), 155 SVN_PROP_EXTERNALS, 156 parent_directory_display, 157 line); 158} 159 160svn_error_t * 161svn_wc_parse_externals_description3(apr_array_header_t **externals_p, 162 const char *parent_directory, 163 const char *desc, 164 svn_boolean_t canonicalize_url, 165 apr_pool_t *pool) 166{ 167 int i; 168 apr_array_header_t *externals = NULL; 169 apr_array_header_t *lines = svn_cstring_split(desc, "\n\r", TRUE, pool); 170 const char *parent_directory_display = svn_path_is_url(parent_directory) ? 171 parent_directory : svn_dirent_local_style(parent_directory, pool); 172 173 /* If an error occurs halfway through parsing, *externals_p should stay 174 * untouched. So, store the list in a local var first. */ 175 if (externals_p) 176 externals = apr_array_make(pool, 1, sizeof(svn_wc_external_item2_t *)); 177 178 for (i = 0; i < lines->nelts; i++) 179 { 180 const char *line = APR_ARRAY_IDX(lines, i, const char *); 181 apr_status_t status; 182 char **line_parts; 183 int num_line_parts; 184 svn_wc_external_item2_t *item; 185 const char *token0; 186 const char *token1; 187 svn_boolean_t token0_is_url; 188 svn_boolean_t token1_is_url; 189 190 /* Index into line_parts where the revision specification 191 started. */ 192 int rev_idx = -1; 193 194 if ((! line) || (line[0] == '#')) 195 continue; 196 197 /* else proceed */ 198 199 status = apr_tokenize_to_argv(line, &line_parts, pool); 200 if (status) 201 return svn_error_wrap_apr(status, 202 _("Can't split line into components: '%s'"), 203 line); 204 /* Count the number of tokens. */ 205 for (num_line_parts = 0; line_parts[num_line_parts]; num_line_parts++) 206 ; 207 208 SVN_ERR(svn_wc_external_item2_create(&item, pool)); 209 item->revision.kind = svn_opt_revision_unspecified; 210 item->peg_revision.kind = svn_opt_revision_unspecified; 211 212 /* 213 * There are six different formats of externals: 214 * 215 * 1) DIR URL 216 * 2) DIR -r N URL 217 * 3) DIR -rN URL 218 * 4) URL DIR 219 * 5) -r N URL DIR 220 * 6) -rN URL DIR 221 * 222 * The last three allow peg revisions in the URL. 223 * 224 * With relative URLs and no '-rN' or '-r N', there is no way to 225 * distinguish between 'DIR URL' and 'URL DIR' when URL is a 226 * relative URL like /svn/repos/trunk, so this case is taken as 227 * case 4). 228 */ 229 if (num_line_parts < 2 || num_line_parts > 4) 230 return svn_error_createf 231 (SVN_ERR_CLIENT_INVALID_EXTERNALS_DESCRIPTION, NULL, 232 _("Error parsing %s property on '%s': '%s'"), 233 SVN_PROP_EXTERNALS, 234 parent_directory_display, 235 line); 236 237 /* To make it easy to check for the forms, find and remove -r N 238 or -rN from the line item array. If it is found, rev_idx 239 contains the index into line_parts where '-r' was found and 240 set item->revision to the parsed revision. */ 241 /* ### ugh. stupid cast. */ 242 SVN_ERR(find_and_remove_externals_revision(&rev_idx, 243 (const char **)line_parts, 244 num_line_parts, item, 245 parent_directory_display, 246 line, pool)); 247 248 token0 = line_parts[0]; 249 token1 = line_parts[1]; 250 251 token0_is_url = svn_path_is_url(token0); 252 token1_is_url = svn_path_is_url(token1); 253 254 if (token0_is_url && token1_is_url) 255 return svn_error_createf 256 (SVN_ERR_CLIENT_INVALID_EXTERNALS_DESCRIPTION, NULL, 257 _("Invalid %s property on '%s': " 258 "cannot use two absolute URLs ('%s' and '%s') in an external; " 259 "one must be a path where an absolute or relative URL is " 260 "checked out to"), 261 SVN_PROP_EXTERNALS, parent_directory_display, token0, token1); 262 263 if (0 == rev_idx && token1_is_url) 264 return svn_error_createf 265 (SVN_ERR_CLIENT_INVALID_EXTERNALS_DESCRIPTION, NULL, 266 _("Invalid %s property on '%s': " 267 "cannot use a URL '%s' as the target directory for an external " 268 "definition"), 269 SVN_PROP_EXTERNALS, parent_directory_display, token1); 270 271 if (1 == rev_idx && token0_is_url) 272 return svn_error_createf 273 (SVN_ERR_CLIENT_INVALID_EXTERNALS_DESCRIPTION, NULL, 274 _("Invalid %s property on '%s': " 275 "cannot use a URL '%s' as the target directory for an external " 276 "definition"), 277 SVN_PROP_EXTERNALS, parent_directory_display, token0); 278 279 /* The appearence of -r N or -rN forces the type of external. 280 If -r is at the beginning of the line or the first token is 281 an absolute URL or if the second token is not an absolute 282 URL, then the URL supports peg revisions. */ 283 if (0 == rev_idx || 284 (-1 == rev_idx && (token0_is_url || ! token1_is_url))) 285 { 286 /* The URL is passed to svn_opt_parse_path in 287 uncanonicalized form so that the scheme relative URL 288 //hostname/foo is not collapsed to a server root relative 289 URL /hostname/foo. */ 290 SVN_ERR(svn_opt_parse_path(&item->peg_revision, &item->url, 291 token0, pool)); 292 item->target_dir = token1; 293 } 294 else 295 { 296 item->target_dir = token0; 297 item->url = token1; 298 item->peg_revision = item->revision; 299 } 300 301 SVN_ERR(svn_opt_resolve_revisions(&item->peg_revision, 302 &item->revision, TRUE, FALSE, 303 pool)); 304 305 item->target_dir = svn_dirent_internal_style(item->target_dir, pool); 306 307 if (item->target_dir[0] == '\0' 308 || svn_dirent_is_absolute(item->target_dir) 309 || svn_path_is_backpath_present(item->target_dir) 310 || !svn_dirent_skip_ancestor("dummy", 311 svn_dirent_join("dummy", 312 item->target_dir, 313 pool))) 314 return svn_error_createf 315 (SVN_ERR_CLIENT_INVALID_EXTERNALS_DESCRIPTION, NULL, 316 _("Invalid %s property on '%s': " 317 "target '%s' is an absolute path or involves '..'"), 318 SVN_PROP_EXTERNALS, 319 parent_directory_display, 320 item->target_dir); 321 322 if (canonicalize_url) 323 { 324 /* Uh... this is stupid. But it's consistent with what our 325 code did before we split up the relpath/dirent/uri APIs. 326 Still, given this, it's no wonder that our own libraries 327 don't ask this function to canonicalize the results. */ 328 if (svn_path_is_url(item->url)) 329 item->url = svn_uri_canonicalize(item->url, pool); 330 else 331 item->url = svn_dirent_canonicalize(item->url, pool); 332 } 333 334 if (externals) 335 APR_ARRAY_PUSH(externals, svn_wc_external_item2_t *) = item; 336 } 337 338 if (externals_p) 339 *externals_p = externals; 340 341 return SVN_NO_ERROR; 342} 343 344svn_error_t * 345svn_wc__externals_find_target_dups(apr_array_header_t **duplicate_targets, 346 apr_array_header_t *externals, 347 apr_pool_t *pool, 348 apr_pool_t *scratch_pool) 349{ 350 int i; 351 unsigned int len; 352 unsigned int len2; 353 const char *target; 354 apr_hash_t *targets = apr_hash_make(scratch_pool); 355 apr_hash_t *targets2 = NULL; 356 *duplicate_targets = NULL; 357 358 for (i = 0; i < externals->nelts; i++) 359 { 360 target = APR_ARRAY_IDX(externals, i, 361 svn_wc_external_item2_t*)->target_dir; 362 len = apr_hash_count(targets); 363 svn_hash_sets(targets, target, ""); 364 if (len == apr_hash_count(targets)) 365 { 366 /* Hashtable length is unchanged. This must be a duplicate. */ 367 368 /* Collapse multiple duplicates of the same target by using a second 369 * hash layer. */ 370 if (! targets2) 371 targets2 = apr_hash_make(scratch_pool); 372 len2 = apr_hash_count(targets2); 373 svn_hash_sets(targets2, target, ""); 374 if (len2 < apr_hash_count(targets2)) 375 { 376 /* The second hash list just got bigger, i.e. this target has 377 * not been counted as duplicate before. */ 378 if (! *duplicate_targets) 379 { 380 *duplicate_targets = apr_array_make( 381 pool, 1, sizeof(svn_wc_external_item2_t*)); 382 } 383 APR_ARRAY_PUSH((*duplicate_targets), const char *) = target; 384 } 385 /* Else, this same target has already been recorded as a duplicate, 386 * don't count it again. */ 387 } 388 } 389 return SVN_NO_ERROR; 390} 391 392struct edit_baton 393{ 394 apr_pool_t *pool; 395 svn_wc__db_t *db; 396 397 /* We explicitly use wri_abspath and local_abspath here, because we 398 might want to install file externals in an obstructing working copy */ 399 const char *wri_abspath; /* The working defining the file external */ 400 const char *local_abspath; /* The file external itself */ 401 const char *name; /* The basename of the file external itself */ 402 403 /* Information from the caller */ 404 svn_boolean_t use_commit_times; 405 const apr_array_header_t *ext_patterns; 406 const char *diff3cmd; 407 408 const char *url; 409 const char *repos_root_url; 410 const char *repos_uuid; 411 412 const char *record_ancestor_abspath; 413 const char *recorded_repos_relpath; 414 svn_revnum_t recorded_peg_revision; 415 svn_revnum_t recorded_revision; 416 417 /* Introducing a new file external */ 418 svn_boolean_t added; 419 420 svn_wc_conflict_resolver_func2_t conflict_func; 421 void *conflict_baton; 422 svn_cancel_func_t cancel_func; 423 void *cancel_baton; 424 svn_wc_notify_func2_t notify_func; 425 void *notify_baton; 426 427 svn_revnum_t *target_revision; 428 429 /* What was there before the update */ 430 svn_revnum_t original_revision; 431 const svn_checksum_t *original_checksum; 432 433 /* What we are installing now */ 434 const char *new_pristine_abspath; 435 svn_checksum_t *new_sha1_checksum; 436 svn_checksum_t *new_md5_checksum; 437 438 /* List of incoming propchanges */ 439 apr_array_header_t *propchanges; 440 441 /* Array of svn_prop_inherited_item_t * structures representing the 442 properties inherited by the base node at LOCAL_ABSPATH. */ 443 apr_array_header_t *iprops; 444 445 /* The last change information */ 446 svn_revnum_t changed_rev; 447 apr_time_t changed_date; 448 const char *changed_author; 449 450 svn_boolean_t had_props; 451 452 svn_boolean_t file_closed; 453}; 454 455/* svn_delta_editor_t function for svn_wc__get_file_external_editor */ 456static svn_error_t * 457set_target_revision(void *edit_baton, 458 svn_revnum_t target_revision, 459 apr_pool_t *pool) 460{ 461 struct edit_baton *eb = edit_baton; 462 463 *eb->target_revision = target_revision; 464 465 return SVN_NO_ERROR; 466} 467 468/* svn_delta_editor_t function for svn_wc__get_file_external_editor */ 469static svn_error_t * 470open_root(void *edit_baton, 471 svn_revnum_t base_revision, 472 apr_pool_t *dir_pool, 473 void **root_baton) 474{ 475 *root_baton = edit_baton; 476 return SVN_NO_ERROR; 477} 478 479/* svn_delta_editor_t function for svn_wc__get_file_external_editor */ 480static svn_error_t * 481add_file(const char *path, 482 void *parent_baton, 483 const char *copyfrom_path, 484 svn_revnum_t copyfrom_revision, 485 apr_pool_t *file_pool, 486 void **file_baton) 487{ 488 struct edit_baton *eb = parent_baton; 489 if (strcmp(path, eb->name)) 490 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, 491 _("This editor can only update '%s'"), 492 svn_dirent_local_style(eb->local_abspath, 493 file_pool)); 494 495 *file_baton = eb; 496 eb->original_revision = SVN_INVALID_REVNUM; 497 eb->added = TRUE; 498 499 return SVN_NO_ERROR; 500} 501 502/* svn_delta_editor_t function for svn_wc__get_file_external_editor */ 503static svn_error_t * 504open_file(const char *path, 505 void *parent_baton, 506 svn_revnum_t base_revision, 507 apr_pool_t *file_pool, 508 void **file_baton) 509{ 510 struct edit_baton *eb = parent_baton; 511 svn_node_kind_t kind; 512 if (strcmp(path, eb->name)) 513 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, 514 _("This editor can only update '%s'"), 515 svn_dirent_local_style(eb->local_abspath, 516 file_pool)); 517 518 *file_baton = eb; 519 SVN_ERR(svn_wc__db_base_get_info(NULL, &kind, &eb->original_revision, 520 NULL, NULL, NULL, &eb->changed_rev, 521 &eb->changed_date, &eb->changed_author, 522 NULL, &eb->original_checksum, NULL, NULL, 523 &eb->had_props, NULL, NULL, 524 eb->db, eb->local_abspath, 525 eb->pool, file_pool)); 526 527 if (kind != svn_node_file) 528 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 529 _("Node '%s' is no existing file external"), 530 svn_dirent_local_style(eb->local_abspath, 531 file_pool)); 532 return SVN_NO_ERROR; 533} 534 535/* svn_delta_editor_t function for svn_wc__get_file_external_editor */ 536static svn_error_t * 537apply_textdelta(void *file_baton, 538 const char *base_checksum_digest, 539 apr_pool_t *pool, 540 svn_txdelta_window_handler_t *handler, 541 void **handler_baton) 542{ 543 struct edit_baton *eb = file_baton; 544 svn_stream_t *src_stream; 545 svn_stream_t *dest_stream; 546 547 if (eb->original_checksum) 548 { 549 if (base_checksum_digest) 550 { 551 svn_checksum_t *expected_checksum; 552 const svn_checksum_t *original_md5; 553 554 SVN_ERR(svn_checksum_parse_hex(&expected_checksum, svn_checksum_md5, 555 base_checksum_digest, pool)); 556 557 if (eb->original_checksum->kind != svn_checksum_md5) 558 SVN_ERR(svn_wc__db_pristine_get_md5(&original_md5, 559 eb->db, eb->wri_abspath, 560 eb->original_checksum, 561 pool, pool)); 562 else 563 original_md5 = eb->original_checksum; 564 565 if (!svn_checksum_match(expected_checksum, original_md5)) 566 return svn_error_trace(svn_checksum_mismatch_err( 567 expected_checksum, 568 original_md5, 569 pool, 570 _("Base checksum mismatch for '%s'"), 571 svn_dirent_local_style(eb->local_abspath, 572 pool))); 573 } 574 575 SVN_ERR(svn_wc__db_pristine_read(&src_stream, NULL, eb->db, 576 eb->wri_abspath, eb->original_checksum, 577 pool, pool)); 578 } 579 else 580 src_stream = svn_stream_empty(pool); 581 582 SVN_ERR(svn_wc__open_writable_base(&dest_stream, &eb->new_pristine_abspath, 583 &eb->new_md5_checksum, 584 &eb->new_sha1_checksum, 585 eb->db, eb->wri_abspath, 586 eb->pool, pool)); 587 588 svn_txdelta_apply(src_stream, dest_stream, NULL, eb->local_abspath, pool, 589 handler, handler_baton); 590 591 return SVN_NO_ERROR; 592} 593 594/* svn_delta_editor_t function for svn_wc__get_file_external_editor */ 595static svn_error_t * 596change_file_prop(void *file_baton, 597 const char *name, 598 const svn_string_t *value, 599 apr_pool_t *pool) 600{ 601 struct edit_baton *eb = file_baton; 602 svn_prop_t *propchange; 603 604 propchange = apr_array_push(eb->propchanges); 605 propchange->name = apr_pstrdup(eb->pool, name); 606 propchange->value = value ? svn_string_dup(value, eb->pool) : NULL; 607 608 return SVN_NO_ERROR; 609} 610 611/* svn_delta_editor_t function for svn_wc__get_file_external_editor */ 612static svn_error_t * 613close_file(void *file_baton, 614 const char *expected_md5_digest, 615 apr_pool_t *pool) 616{ 617 struct edit_baton *eb = file_baton; 618 svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown; 619 svn_wc_notify_state_t content_state = svn_wc_notify_state_unknown; 620 svn_boolean_t obstructed = FALSE; 621 622 eb->file_closed = TRUE; /* We bump the revision here */ 623 624 /* Check the checksum, if provided */ 625 if (expected_md5_digest) 626 { 627 svn_checksum_t *expected_md5_checksum; 628 const svn_checksum_t *actual_md5_checksum = eb->new_md5_checksum; 629 630 SVN_ERR(svn_checksum_parse_hex(&expected_md5_checksum, svn_checksum_md5, 631 expected_md5_digest, pool)); 632 633 if (actual_md5_checksum == NULL) 634 { 635 actual_md5_checksum = eb->original_checksum; 636 637 if (actual_md5_checksum != NULL 638 && actual_md5_checksum->kind != svn_checksum_md5) 639 { 640 SVN_ERR(svn_wc__db_pristine_get_md5(&actual_md5_checksum, 641 eb->db, eb->wri_abspath, 642 actual_md5_checksum, 643 pool, pool)); 644 } 645 } 646 647 if (! svn_checksum_match(expected_md5_checksum, actual_md5_checksum)) 648 return svn_checksum_mismatch_err( 649 expected_md5_checksum, 650 actual_md5_checksum, pool, 651 _("Checksum mismatch for '%s'"), 652 svn_dirent_local_style(eb->local_abspath, pool)); 653 } 654 655 /* First move the file in the pristine store; this hands over the cleanup 656 behavior to the pristine store. */ 657 if (eb->new_sha1_checksum) 658 { 659 SVN_ERR(svn_wc__db_pristine_install(eb->db, eb->new_pristine_abspath, 660 eb->new_sha1_checksum, 661 eb->new_md5_checksum, pool)); 662 663 eb->new_pristine_abspath = NULL; 664 } 665 666 /* Merge the changes */ 667 { 668 svn_skel_t *all_work_items = NULL; 669 svn_skel_t *conflict_skel = NULL; 670 svn_skel_t *work_item; 671 apr_hash_t *base_props = NULL; 672 apr_hash_t *actual_props = NULL; 673 apr_hash_t *new_pristine_props = NULL; 674 apr_hash_t *new_actual_props = NULL; 675 apr_hash_t *new_dav_props = NULL; 676 const svn_checksum_t *new_checksum = NULL; 677 const svn_checksum_t *original_checksum = NULL; 678 679 svn_boolean_t added = !SVN_IS_VALID_REVNUM(eb->original_revision); 680 const char *repos_relpath = svn_uri_skip_ancestor(eb->repos_root_url, 681 eb->url, pool); 682 683 if (! added) 684 { 685 new_checksum = eb->original_checksum; 686 687 if (eb->had_props) 688 SVN_ERR(svn_wc__db_base_get_props( 689 &base_props, eb->db, eb->local_abspath, pool, pool)); 690 691 SVN_ERR(svn_wc__db_read_props( 692 &actual_props, eb->db, eb->local_abspath, pool, pool)); 693 } 694 695 if (!base_props) 696 base_props = apr_hash_make(pool); 697 698 if (!actual_props) 699 actual_props = apr_hash_make(pool); 700 701 if (eb->new_sha1_checksum) 702 new_checksum = eb->new_sha1_checksum; 703 704 /* Merge the properties */ 705 { 706 apr_array_header_t *entry_prop_changes; 707 apr_array_header_t *dav_prop_changes; 708 apr_array_header_t *regular_prop_changes; 709 int i; 710 711 SVN_ERR(svn_categorize_props(eb->propchanges, &entry_prop_changes, 712 &dav_prop_changes, ®ular_prop_changes, 713 pool)); 714 715 /* Read the entry-prop changes to update the last-changed info. */ 716 for (i = 0; i < entry_prop_changes->nelts; i++) 717 { 718 const svn_prop_t *prop = &APR_ARRAY_IDX(entry_prop_changes, i, 719 svn_prop_t); 720 721 if (! prop->value) 722 continue; /* authz or something */ 723 724 if (! strcmp(prop->name, SVN_PROP_ENTRY_LAST_AUTHOR)) 725 eb->changed_author = apr_pstrdup(pool, prop->value->data); 726 else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_REV)) 727 { 728 apr_int64_t rev; 729 SVN_ERR(svn_cstring_atoi64(&rev, prop->value->data)); 730 eb->changed_rev = (svn_revnum_t)rev; 731 } 732 else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_DATE)) 733 SVN_ERR(svn_time_from_cstring(&eb->changed_date, prop->value->data, 734 pool)); 735 } 736 737 /* Store the DAV-prop (aka WC-prop) changes. (This treats a list 738 * of changes as a list of new props, but we only use this when 739 * adding a new file and it's equivalent in that case.) */ 740 if (dav_prop_changes->nelts > 0) 741 new_dav_props = svn_prop_array_to_hash(dav_prop_changes, pool); 742 743 /* Merge the regular prop changes. */ 744 if (regular_prop_changes->nelts > 0) 745 { 746 new_pristine_props = svn_prop__patch(base_props, regular_prop_changes, 747 pool); 748 SVN_ERR(svn_wc__merge_props(&conflict_skel, 749 &prop_state, 750 &new_actual_props, 751 eb->db, eb->local_abspath, 752 NULL /* server_baseprops*/, 753 base_props, 754 actual_props, 755 regular_prop_changes, 756 pool, pool)); 757 } 758 else 759 { 760 new_pristine_props = base_props; 761 new_actual_props = actual_props; 762 } 763 } 764 765 /* Merge the text */ 766 if (eb->new_sha1_checksum) 767 { 768 svn_node_kind_t disk_kind; 769 svn_boolean_t install_pristine = FALSE; 770 const char *install_from = NULL; 771 772 SVN_ERR(svn_io_check_path(eb->local_abspath, &disk_kind, pool)); 773 774 if (disk_kind == svn_node_none) 775 { 776 /* Just install the file */ 777 install_pristine = TRUE; 778 content_state = svn_wc_notify_state_changed; 779 } 780 else if (disk_kind != svn_node_file 781 || (eb->added && disk_kind == svn_node_file)) 782 { 783 /* The node is obstructed; we just change the DB */ 784 obstructed = TRUE; 785 content_state = svn_wc_notify_state_unchanged; 786 } 787 else 788 { 789 svn_boolean_t is_mod; 790 SVN_ERR(svn_wc__internal_file_modified_p(&is_mod, 791 eb->db, eb->local_abspath, 792 FALSE, pool)); 793 794 if (!is_mod) 795 { 796 install_pristine = TRUE; 797 content_state = svn_wc_notify_state_changed; 798 } 799 else 800 { 801 svn_boolean_t found_text_conflict; 802 803 /* Ok, we have to do some work to merge a local change */ 804 SVN_ERR(svn_wc__perform_file_merge(&work_item, 805 &conflict_skel, 806 &found_text_conflict, 807 eb->db, 808 eb->local_abspath, 809 eb->wri_abspath, 810 new_checksum, 811 original_checksum, 812 actual_props, 813 eb->ext_patterns, 814 eb->original_revision, 815 *eb->target_revision, 816 eb->propchanges, 817 eb->diff3cmd, 818 eb->cancel_func, 819 eb->cancel_baton, 820 pool, pool)); 821 822 all_work_items = svn_wc__wq_merge(all_work_items, work_item, 823 pool); 824 825 if (found_text_conflict) 826 content_state = svn_wc_notify_state_conflicted; 827 else 828 content_state = svn_wc_notify_state_merged; 829 } 830 } 831 if (install_pristine) 832 { 833 SVN_ERR(svn_wc__wq_build_file_install(&work_item, eb->db, 834 eb->local_abspath, 835 install_from, 836 eb->use_commit_times, TRUE, 837 pool, pool)); 838 839 all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool); 840 } 841 } 842 else 843 { 844 content_state = svn_wc_notify_state_unchanged; 845 /* ### Retranslate on magic property changes, etc. */ 846 } 847 848 /* Generate a conflict description, if needed */ 849 if (conflict_skel) 850 { 851 SVN_ERR(svn_wc__conflict_skel_set_op_switch( 852 conflict_skel, 853 svn_wc_conflict_version_create2( 854 eb->repos_root_url, 855 eb->repos_uuid, 856 repos_relpath, 857 eb->original_revision, 858 svn_node_file, 859 pool), 860 svn_wc_conflict_version_create2( 861 eb->repos_root_url, 862 eb->repos_uuid, 863 repos_relpath, 864 *eb->target_revision, 865 svn_node_file, 866 pool), 867 pool, pool)); 868 SVN_ERR(svn_wc__conflict_create_markers(&work_item, 869 eb->db, eb->local_abspath, 870 conflict_skel, 871 pool, pool)); 872 all_work_items = svn_wc__wq_merge(all_work_items, work_item, 873 pool); 874 } 875 876 /* Install the file in the DB */ 877 SVN_ERR(svn_wc__db_external_add_file( 878 eb->db, 879 eb->local_abspath, 880 eb->wri_abspath, 881 repos_relpath, 882 eb->repos_root_url, 883 eb->repos_uuid, 884 *eb->target_revision, 885 new_pristine_props, 886 eb->iprops, 887 eb->changed_rev, 888 eb->changed_date, 889 eb->changed_author, 890 new_checksum, 891 new_dav_props, 892 eb->record_ancestor_abspath, 893 eb->recorded_repos_relpath, 894 eb->recorded_peg_revision, 895 eb->recorded_revision, 896 TRUE, new_actual_props, 897 FALSE /* keep_recorded_info */, 898 conflict_skel, 899 all_work_items, 900 pool)); 901 902 /* close_edit may also update iprops for switched files, catching 903 those for which close_file is never called (e.g. an update of a 904 file external with no changes). So as a minor optimization we 905 clear the iprops so as not to set them again in close_edit. */ 906 eb->iprops = NULL; 907 908 /* Run the work queue to complete the installation */ 909 SVN_ERR(svn_wc__wq_run(eb->db, eb->wri_abspath, 910 eb->cancel_func, eb->cancel_baton, pool)); 911 } 912 913 /* Notify */ 914 if (eb->notify_func) 915 { 916 svn_wc_notify_action_t action; 917 svn_wc_notify_t *notify; 918 919 if (!eb->added) 920 action = obstructed ? svn_wc_notify_update_shadowed_update 921 : svn_wc_notify_update_update; 922 else 923 action = obstructed ? svn_wc_notify_update_shadowed_add 924 : svn_wc_notify_update_add; 925 926 notify = svn_wc_create_notify(eb->local_abspath, action, pool); 927 notify->kind = svn_node_file; 928 929 notify->revision = *eb->target_revision; 930 notify->prop_state = prop_state; 931 notify->content_state = content_state; 932 933 notify->old_revision = eb->original_revision; 934 935 eb->notify_func(eb->notify_baton, notify, pool); 936 } 937 938 return SVN_NO_ERROR; 939} 940 941/* svn_delta_editor_t function for svn_wc__get_file_external_editor */ 942static svn_error_t * 943close_edit(void *edit_baton, 944 apr_pool_t *pool) 945{ 946 struct edit_baton *eb = edit_baton; 947 948 if (!eb->file_closed 949 || eb->iprops) 950 { 951 apr_hash_t *wcroot_iprops = NULL; 952 953 if (eb->iprops) 954 { 955 wcroot_iprops = apr_hash_make(pool); 956 svn_hash_sets(wcroot_iprops, eb->local_abspath, eb->iprops); 957 } 958 959 /* The node wasn't updated, so we just have to bump its revision */ 960 SVN_ERR(svn_wc__db_op_bump_revisions_post_update(eb->db, 961 eb->local_abspath, 962 svn_depth_infinity, 963 NULL, NULL, NULL, 964 *eb->target_revision, 965 apr_hash_make(pool), 966 wcroot_iprops, 967 eb->notify_func, 968 eb->notify_baton, 969 pool)); 970 } 971 972 return SVN_NO_ERROR; 973} 974 975svn_error_t * 976svn_wc__get_file_external_editor(const svn_delta_editor_t **editor, 977 void **edit_baton, 978 svn_revnum_t *target_revision, 979 svn_wc_context_t *wc_ctx, 980 const char *local_abspath, 981 const char *wri_abspath, 982 const char *url, 983 const char *repos_root_url, 984 const char *repos_uuid, 985 apr_array_header_t *iprops, 986 svn_boolean_t use_commit_times, 987 const char *diff3_cmd, 988 const apr_array_header_t *preserved_exts, 989 const char *record_ancestor_abspath, 990 const char *recorded_url, 991 const svn_opt_revision_t *recorded_peg_rev, 992 const svn_opt_revision_t *recorded_rev, 993 svn_wc_conflict_resolver_func2_t conflict_func, 994 void *conflict_baton, 995 svn_cancel_func_t cancel_func, 996 void *cancel_baton, 997 svn_wc_notify_func2_t notify_func, 998 void *notify_baton, 999 apr_pool_t *result_pool, 1000 apr_pool_t *scratch_pool) 1001{ 1002 svn_wc__db_t *db = wc_ctx->db; 1003 apr_pool_t *edit_pool = result_pool; 1004 struct edit_baton *eb = apr_pcalloc(edit_pool, sizeof(*eb)); 1005 svn_delta_editor_t *tree_editor = svn_delta_default_editor(edit_pool); 1006 1007 eb->pool = edit_pool; 1008 eb->db = db; 1009 eb->local_abspath = apr_pstrdup(edit_pool, local_abspath); 1010 if (wri_abspath) 1011 eb->wri_abspath = apr_pstrdup(edit_pool, wri_abspath); 1012 else 1013 eb->wri_abspath = svn_dirent_dirname(local_abspath, edit_pool); 1014 eb->name = svn_dirent_basename(eb->local_abspath, NULL); 1015 eb->target_revision = target_revision; 1016 1017 eb->url = apr_pstrdup(edit_pool, url); 1018 eb->repos_root_url = apr_pstrdup(edit_pool, repos_root_url); 1019 eb->repos_uuid = apr_pstrdup(edit_pool, repos_uuid); 1020 1021 eb->iprops = iprops; 1022 1023 eb->use_commit_times = use_commit_times; 1024 eb->ext_patterns = preserved_exts; 1025 eb->diff3cmd = diff3_cmd; 1026 1027 eb->record_ancestor_abspath = apr_pstrdup(edit_pool,record_ancestor_abspath); 1028 eb->recorded_repos_relpath = svn_uri_skip_ancestor(repos_root_url, recorded_url, 1029 edit_pool); 1030 1031 eb->changed_rev = SVN_INVALID_REVNUM; 1032 1033 if (recorded_peg_rev->kind == svn_opt_revision_number) 1034 eb->recorded_peg_revision = recorded_peg_rev->value.number; 1035 else 1036 eb->recorded_peg_revision = SVN_INVALID_REVNUM; /* Not fixed/HEAD */ 1037 1038 if (recorded_rev->kind == svn_opt_revision_number) 1039 eb->recorded_revision = recorded_rev->value.number; 1040 else 1041 eb->recorded_revision = SVN_INVALID_REVNUM; /* Not fixed/HEAD */ 1042 1043 eb->conflict_func = conflict_func; 1044 eb->conflict_baton = conflict_baton; 1045 eb->cancel_func = cancel_func; 1046 eb->cancel_baton = cancel_baton; 1047 eb->notify_func = notify_func; 1048 eb->notify_baton = notify_baton; 1049 1050 eb->propchanges = apr_array_make(edit_pool, 1, sizeof(svn_prop_t)); 1051 1052 tree_editor->open_root = open_root; 1053 tree_editor->set_target_revision = set_target_revision; 1054 tree_editor->add_file = add_file; 1055 tree_editor->open_file = open_file; 1056 tree_editor->apply_textdelta = apply_textdelta; 1057 tree_editor->change_file_prop = change_file_prop; 1058 tree_editor->close_file = close_file; 1059 tree_editor->close_edit = close_edit; 1060 1061 return svn_delta_get_cancellation_editor(cancel_func, cancel_baton, 1062 tree_editor, eb, 1063 editor, edit_baton, 1064 result_pool); 1065} 1066 1067svn_error_t * 1068svn_wc__crawl_file_external(svn_wc_context_t *wc_ctx, 1069 const char *local_abspath, 1070 const svn_ra_reporter3_t *reporter, 1071 void *report_baton, 1072 svn_boolean_t restore_files, 1073 svn_boolean_t use_commit_times, 1074 svn_cancel_func_t cancel_func, 1075 void *cancel_baton, 1076 svn_wc_notify_func2_t notify_func, 1077 void *notify_baton, 1078 apr_pool_t *scratch_pool) 1079{ 1080 svn_wc__db_t *db = wc_ctx->db; 1081 svn_error_t *err; 1082 svn_node_kind_t kind; 1083 svn_wc__db_lock_t *lock; 1084 svn_revnum_t revision; 1085 const char *repos_root_url; 1086 const char *repos_relpath; 1087 svn_boolean_t update_root; 1088 1089 err = svn_wc__db_base_get_info(NULL, &kind, &revision, 1090 &repos_relpath, &repos_root_url, NULL, NULL, 1091 NULL, NULL, NULL, NULL, NULL, &lock, 1092 NULL, NULL, &update_root, 1093 db, local_abspath, 1094 scratch_pool, scratch_pool); 1095 1096 if (err 1097 || kind == svn_node_dir 1098 || !update_root) 1099 { 1100 if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 1101 return svn_error_trace(err); 1102 1103 svn_error_clear(err); 1104 1105 /* We don't know about this node, so all we have to do is tell 1106 the reporter that we don't know this node. 1107 1108 But first we have to start the report by sending some basic 1109 information for the root. */ 1110 1111 SVN_ERR(reporter->set_path(report_baton, "", 0, svn_depth_infinity, 1112 FALSE, NULL, scratch_pool)); 1113 SVN_ERR(reporter->delete_path(report_baton, "", scratch_pool)); 1114 1115 /* Finish the report, which causes the update editor to be 1116 driven. */ 1117 SVN_ERR(reporter->finish_report(report_baton, scratch_pool)); 1118 1119 return SVN_NO_ERROR; 1120 } 1121 else 1122 { 1123 if (restore_files) 1124 { 1125 svn_node_kind_t disk_kind; 1126 SVN_ERR(svn_io_check_path(local_abspath, &disk_kind, scratch_pool)); 1127 1128 if (disk_kind == svn_node_none) 1129 { 1130 err = svn_wc_restore(wc_ctx, local_abspath, use_commit_times, 1131 scratch_pool); 1132 1133 if (err) 1134 { 1135 if (err->apr_err != SVN_ERR_WC_PATH_UNEXPECTED_STATUS) 1136 return svn_error_trace(err); 1137 1138 svn_error_clear(err); 1139 } 1140 } 1141 } 1142 1143 /* Report that we know the path */ 1144 SVN_ERR(reporter->set_path(report_baton, "", revision, 1145 svn_depth_infinity, FALSE, NULL, 1146 scratch_pool)); 1147 1148 /* For compatibility with the normal update editor report we report 1149 the target as switched. 1150 1151 ### We can probably report a parent url and unswitched later */ 1152 SVN_ERR(reporter->link_path(report_baton, "", 1153 svn_path_url_add_component2(repos_root_url, 1154 repos_relpath, 1155 scratch_pool), 1156 revision, 1157 svn_depth_infinity, 1158 FALSE /* start_empty*/, 1159 lock ? lock->token : NULL, 1160 scratch_pool)); 1161 } 1162 1163 return svn_error_trace(reporter->finish_report(report_baton, scratch_pool)); 1164} 1165 1166svn_error_t * 1167svn_wc__read_external_info(svn_node_kind_t *external_kind, 1168 const char **defining_abspath, 1169 const char **defining_url, 1170 svn_revnum_t *defining_operational_revision, 1171 svn_revnum_t *defining_revision, 1172 svn_wc_context_t *wc_ctx, 1173 const char *wri_abspath, 1174 const char *local_abspath, 1175 svn_boolean_t ignore_enoent, 1176 apr_pool_t *result_pool, 1177 apr_pool_t *scratch_pool) 1178{ 1179 const char *repos_root_url; 1180 svn_wc__db_status_t status; 1181 svn_node_kind_t kind; 1182 svn_error_t *err; 1183 1184 err = svn_wc__db_external_read(&status, &kind, defining_abspath, 1185 defining_url ? &repos_root_url : NULL, NULL, 1186 defining_url, defining_operational_revision, 1187 defining_revision, 1188 wc_ctx->db, local_abspath, wri_abspath, 1189 result_pool, scratch_pool); 1190 1191 if (err) 1192 { 1193 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND || !ignore_enoent) 1194 return svn_error_trace(err); 1195 1196 svn_error_clear(err); 1197 1198 if (external_kind) 1199 *external_kind = svn_node_none; 1200 1201 if (defining_abspath) 1202 *defining_abspath = NULL; 1203 1204 if (defining_url) 1205 *defining_url = NULL; 1206 1207 if (defining_operational_revision) 1208 *defining_operational_revision = SVN_INVALID_REVNUM; 1209 1210 if (defining_revision) 1211 *defining_revision = SVN_INVALID_REVNUM; 1212 1213 return SVN_NO_ERROR; 1214 } 1215 1216 if (external_kind) 1217 { 1218 if (status != svn_wc__db_status_normal) 1219 *external_kind = svn_node_unknown; 1220 else 1221 switch(kind) 1222 { 1223 case svn_node_file: 1224 case svn_node_symlink: 1225 *external_kind = svn_node_file; 1226 break; 1227 case svn_node_dir: 1228 *external_kind = svn_node_dir; 1229 break; 1230 default: 1231 *external_kind = svn_node_none; 1232 } 1233 } 1234 1235 if (defining_url && *defining_url) 1236 *defining_url = svn_path_url_add_component2(repos_root_url, *defining_url, 1237 result_pool); 1238 1239 return SVN_NO_ERROR; 1240} 1241 1242/* Return TRUE in *IS_ROLLED_OUT iff a node exists at XINFO->LOCAL_ABSPATH and 1243 * if that node's origin corresponds with XINFO->REPOS_ROOT_URL and 1244 * XINFO->REPOS_RELPATH. All allocations are made in SCRATCH_POOL. */ 1245static svn_error_t * 1246is_external_rolled_out(svn_boolean_t *is_rolled_out, 1247 svn_wc_context_t *wc_ctx, 1248 svn_wc__committable_external_info_t *xinfo, 1249 apr_pool_t *scratch_pool) 1250{ 1251 const char *repos_relpath; 1252 const char *repos_root_url; 1253 svn_error_t *err; 1254 1255 *is_rolled_out = FALSE; 1256 1257 err = svn_wc__db_base_get_info(NULL, NULL, NULL, &repos_relpath, 1258 &repos_root_url, NULL, NULL, NULL, NULL, 1259 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1260 wc_ctx->db, xinfo->local_abspath, 1261 scratch_pool, scratch_pool); 1262 1263 if (err) 1264 { 1265 if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) 1266 { 1267 svn_error_clear(err); 1268 return SVN_NO_ERROR; 1269 } 1270 SVN_ERR(err); 1271 } 1272 1273 *is_rolled_out = (strcmp(xinfo->repos_root_url, repos_root_url) == 0 && 1274 strcmp(xinfo->repos_relpath, repos_relpath) == 0); 1275 return SVN_NO_ERROR; 1276} 1277 1278svn_error_t * 1279svn_wc__committable_externals_below(apr_array_header_t **externals, 1280 svn_wc_context_t *wc_ctx, 1281 const char *local_abspath, 1282 svn_depth_t depth, 1283 apr_pool_t *result_pool, 1284 apr_pool_t *scratch_pool) 1285{ 1286 apr_array_header_t *orig_externals; 1287 int i; 1288 apr_pool_t *iterpool; 1289 1290 /* For svn_depth_files, this also fetches dirs. They are filtered later. */ 1291 SVN_ERR(svn_wc__db_committable_externals_below(&orig_externals, 1292 wc_ctx->db, 1293 local_abspath, 1294 depth != svn_depth_infinity, 1295 result_pool, scratch_pool)); 1296 1297 if (orig_externals == NULL) 1298 return SVN_NO_ERROR; 1299 1300 iterpool = svn_pool_create(scratch_pool); 1301 1302 for (i = 0; i < orig_externals->nelts; i++) 1303 { 1304 svn_boolean_t is_rolled_out; 1305 1306 svn_wc__committable_external_info_t *xinfo = 1307 APR_ARRAY_IDX(orig_externals, i, 1308 svn_wc__committable_external_info_t *); 1309 1310 /* Discard dirs for svn_depth_files (s.a.). */ 1311 if (depth == svn_depth_files 1312 && xinfo->kind == svn_node_dir) 1313 continue; 1314 1315 svn_pool_clear(iterpool); 1316 1317 /* Discard those externals that are not currently checked out. */ 1318 SVN_ERR(is_external_rolled_out(&is_rolled_out, wc_ctx, xinfo, 1319 iterpool)); 1320 if (! is_rolled_out) 1321 continue; 1322 1323 if (*externals == NULL) 1324 *externals = apr_array_make( 1325 result_pool, 0, 1326 sizeof(svn_wc__committable_external_info_t *)); 1327 1328 APR_ARRAY_PUSH(*externals, 1329 svn_wc__committable_external_info_t *) = xinfo; 1330 1331 if (depth != svn_depth_infinity) 1332 continue; 1333 1334 /* Are there any nested externals? */ 1335 SVN_ERR(svn_wc__committable_externals_below(externals, wc_ctx, 1336 xinfo->local_abspath, 1337 svn_depth_infinity, 1338 result_pool, iterpool)); 1339 } 1340 1341 return SVN_NO_ERROR; 1342} 1343 1344svn_error_t * 1345svn_wc__externals_defined_below(apr_hash_t **externals, 1346 svn_wc_context_t *wc_ctx, 1347 const char *local_abspath, 1348 apr_pool_t *result_pool, 1349 apr_pool_t *scratch_pool) 1350{ 1351 return svn_error_trace( 1352 svn_wc__db_externals_defined_below(externals, 1353 wc_ctx->db, local_abspath, 1354 result_pool, scratch_pool)); 1355} 1356 1357svn_error_t * 1358svn_wc__external_register(svn_wc_context_t *wc_ctx, 1359 const char *defining_abspath, 1360 const char *local_abspath, 1361 svn_node_kind_t kind, 1362 const char *repos_root_url, 1363 const char *repos_uuid, 1364 const char *repos_relpath, 1365 svn_revnum_t operational_revision, 1366 svn_revnum_t revision, 1367 apr_pool_t *scratch_pool) 1368{ 1369 SVN_ERR_ASSERT(kind == svn_node_dir); 1370 return svn_error_trace( 1371 svn_wc__db_external_add_dir(wc_ctx->db, local_abspath, 1372 defining_abspath, 1373 repos_root_url, 1374 repos_uuid, 1375 defining_abspath, 1376 repos_relpath, 1377 operational_revision, 1378 revision, 1379 NULL, 1380 scratch_pool)); 1381} 1382 1383svn_error_t * 1384svn_wc__external_remove(svn_wc_context_t *wc_ctx, 1385 const char *wri_abspath, 1386 const char *local_abspath, 1387 svn_boolean_t declaration_only, 1388 svn_cancel_func_t cancel_func, 1389 void *cancel_baton, 1390 apr_pool_t *scratch_pool) 1391{ 1392 svn_wc__db_status_t status; 1393 svn_node_kind_t kind; 1394 1395 SVN_ERR(svn_wc__db_external_read(&status, &kind, NULL, NULL, NULL, NULL, 1396 NULL, NULL, 1397 wc_ctx->db, local_abspath, wri_abspath, 1398 scratch_pool, scratch_pool)); 1399 1400 SVN_ERR(svn_wc__db_external_remove(wc_ctx->db, local_abspath, wri_abspath, 1401 NULL, scratch_pool)); 1402 1403 if (declaration_only) 1404 return SVN_NO_ERROR; 1405 1406 if (kind == svn_node_dir) 1407 SVN_ERR(svn_wc_remove_from_revision_control2(wc_ctx, local_abspath, 1408 TRUE, TRUE, 1409 cancel_func, cancel_baton, 1410 scratch_pool)); 1411 else 1412 { 1413 SVN_ERR(svn_wc__db_base_remove(wc_ctx->db, local_abspath, 1414 FALSE /* keep_as_working */, 1415 TRUE /* queue_deletes */, 1416 FALSE /* remove_locks */, 1417 SVN_INVALID_REVNUM, 1418 NULL, NULL, scratch_pool)); 1419 SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, 1420 cancel_func, cancel_baton, 1421 scratch_pool)); 1422 } 1423 1424 return SVN_NO_ERROR; 1425} 1426 1427svn_error_t * 1428svn_wc__externals_gather_definitions(apr_hash_t **externals, 1429 apr_hash_t **depths, 1430 svn_wc_context_t *wc_ctx, 1431 const char *local_abspath, 1432 svn_depth_t depth, 1433 apr_pool_t *result_pool, 1434 apr_pool_t *scratch_pool) 1435{ 1436 if (depth == svn_depth_infinity 1437 || depth == svn_depth_unknown) 1438 { 1439 return svn_error_trace( 1440 svn_wc__db_externals_gather_definitions(externals, depths, 1441 wc_ctx->db, local_abspath, 1442 result_pool, scratch_pool)); 1443 } 1444 else 1445 { 1446 const svn_string_t *value; 1447 svn_error_t *err; 1448 *externals = apr_hash_make(result_pool); 1449 1450 local_abspath = apr_pstrdup(result_pool, local_abspath); 1451 1452 err = svn_wc_prop_get2(&value, wc_ctx, local_abspath, 1453 SVN_PROP_EXTERNALS, result_pool, scratch_pool); 1454 1455 if (err) 1456 { 1457 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 1458 return svn_error_trace(err); 1459 1460 svn_error_clear(err); 1461 value = NULL; 1462 } 1463 1464 if (value) 1465 svn_hash_sets(*externals, local_abspath, value->data); 1466 1467 if (value && depths) 1468 { 1469 svn_depth_t node_depth; 1470 *depths = apr_hash_make(result_pool); 1471 1472 SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, 1473 NULL, NULL, NULL, &node_depth, NULL, 1474 NULL, NULL, NULL, NULL, NULL, NULL, 1475 NULL, NULL, NULL, NULL, NULL, NULL, 1476 NULL, NULL, NULL, NULL, 1477 wc_ctx->db, local_abspath, 1478 scratch_pool, scratch_pool)); 1479 1480 svn_hash_sets(*depths, local_abspath, svn_depth_to_word(node_depth)); 1481 } 1482 1483 return SVN_NO_ERROR; 1484 } 1485} 1486 1487svn_error_t * 1488svn_wc__close_db(const char *external_abspath, 1489 svn_wc_context_t *wc_ctx, 1490 apr_pool_t *scratch_pool) 1491{ 1492 SVN_ERR(svn_wc__db_drop_root(wc_ctx->db, external_abspath, 1493 scratch_pool)); 1494 return SVN_NO_ERROR; 1495} 1496 1497/* Return the scheme of @a uri in @a scheme allocated from @a pool. 1498 If @a uri does not appear to be a valid URI, then @a scheme will 1499 not be updated. */ 1500static svn_error_t * 1501uri_scheme(const char **scheme, const char *uri, apr_pool_t *pool) 1502{ 1503 apr_size_t i; 1504 1505 for (i = 0; uri[i] && uri[i] != ':'; ++i) 1506 if (uri[i] == '/') 1507 goto error; 1508 1509 if (i > 0 && uri[i] == ':' && uri[i+1] == '/' && uri[i+2] == '/') 1510 { 1511 *scheme = apr_pstrmemdup(pool, uri, i); 1512 return SVN_NO_ERROR; 1513 } 1514 1515error: 1516 return svn_error_createf(SVN_ERR_BAD_URL, 0, 1517 _("URL '%s' does not begin with a scheme"), 1518 uri); 1519} 1520 1521svn_error_t * 1522svn_wc__resolve_relative_external_url(const char **resolved_url, 1523 const svn_wc_external_item2_t *item, 1524 const char *repos_root_url, 1525 const char *parent_dir_url, 1526 apr_pool_t *result_pool, 1527 apr_pool_t *scratch_pool) 1528{ 1529 const char *url = item->url; 1530 apr_uri_t parent_dir_uri; 1531 apr_status_t status; 1532 1533 *resolved_url = item->url; 1534 1535 /* If the URL is already absolute, there is nothing to do. */ 1536 if (svn_path_is_url(url)) 1537 { 1538 /* "http://server/path" */ 1539 *resolved_url = svn_uri_canonicalize(url, result_pool); 1540 return SVN_NO_ERROR; 1541 } 1542 1543 if (url[0] == '/') 1544 { 1545 /* "/path", "//path", and "///path" */ 1546 int num_leading_slashes = 1; 1547 if (url[1] == '/') 1548 { 1549 num_leading_slashes++; 1550 if (url[2] == '/') 1551 num_leading_slashes++; 1552 } 1553 1554 /* "//schema-relative" and in some cases "///schema-relative". 1555 This last format is supported on file:// schema relative. */ 1556 url = apr_pstrcat(scratch_pool, 1557 apr_pstrndup(scratch_pool, url, num_leading_slashes), 1558 svn_relpath_canonicalize(url + num_leading_slashes, 1559 scratch_pool), 1560 (char*)NULL); 1561 } 1562 else 1563 { 1564 /* "^/path" and "../path" */ 1565 url = svn_relpath_canonicalize(url, scratch_pool); 1566 } 1567 1568 /* Parse the parent directory URL into its parts. */ 1569 status = apr_uri_parse(scratch_pool, parent_dir_url, &parent_dir_uri); 1570 if (status) 1571 return svn_error_createf(SVN_ERR_BAD_URL, 0, 1572 _("Illegal parent directory URL '%s'"), 1573 parent_dir_url); 1574 1575 /* If the parent directory URL is at the server root, then the URL 1576 may have no / after the hostname so apr_uri_parse() will leave 1577 the URL's path as NULL. */ 1578 if (! parent_dir_uri.path) 1579 parent_dir_uri.path = apr_pstrmemdup(scratch_pool, "/", 1); 1580 parent_dir_uri.query = NULL; 1581 parent_dir_uri.fragment = NULL; 1582 1583 /* Handle URLs relative to the current directory or to the 1584 repository root. The backpaths may only remove path elements, 1585 not the hostname. This allows an external to refer to another 1586 repository in the same server relative to the location of this 1587 repository, say using SVNParentPath. */ 1588 if ((0 == strncmp("../", url, 3)) || 1589 (0 == strncmp("^/", url, 2))) 1590 { 1591 apr_array_header_t *base_components; 1592 apr_array_header_t *relative_components; 1593 int i; 1594 1595 /* Decompose either the parent directory's URL path or the 1596 repository root's URL path into components. */ 1597 if (0 == strncmp("../", url, 3)) 1598 { 1599 base_components = svn_path_decompose(parent_dir_uri.path, 1600 scratch_pool); 1601 relative_components = svn_path_decompose(url, scratch_pool); 1602 } 1603 else 1604 { 1605 apr_uri_t repos_root_uri; 1606 1607 status = apr_uri_parse(scratch_pool, repos_root_url, 1608 &repos_root_uri); 1609 if (status) 1610 return svn_error_createf(SVN_ERR_BAD_URL, 0, 1611 _("Illegal repository root URL '%s'"), 1612 repos_root_url); 1613 1614 /* If the repository root URL is at the server root, then 1615 the URL may have no / after the hostname so 1616 apr_uri_parse() will leave the URL's path as NULL. */ 1617 if (! repos_root_uri.path) 1618 repos_root_uri.path = apr_pstrmemdup(scratch_pool, "/", 1); 1619 1620 base_components = svn_path_decompose(repos_root_uri.path, 1621 scratch_pool); 1622 relative_components = svn_path_decompose(url + 2, scratch_pool); 1623 } 1624 1625 for (i = 0; i < relative_components->nelts; ++i) 1626 { 1627 const char *component = APR_ARRAY_IDX(relative_components, 1628 i, 1629 const char *); 1630 if (0 == strcmp("..", component)) 1631 { 1632 /* Constructing the final absolute URL together with 1633 apr_uri_unparse() requires that the path be absolute, 1634 so only pop a component if the component being popped 1635 is not the component for the root directory. */ 1636 if (base_components->nelts > 1) 1637 apr_array_pop(base_components); 1638 } 1639 else 1640 APR_ARRAY_PUSH(base_components, const char *) = component; 1641 } 1642 1643 parent_dir_uri.path = (char *)svn_path_compose(base_components, 1644 scratch_pool); 1645 *resolved_url = svn_uri_canonicalize(apr_uri_unparse(scratch_pool, 1646 &parent_dir_uri, 0), 1647 result_pool); 1648 return SVN_NO_ERROR; 1649 } 1650 1651 /* The remaining URLs are relative to either the scheme or server root 1652 and can only refer to locations inside that scope, so backpaths are 1653 not allowed. */ 1654 if (svn_path_is_backpath_present(url)) 1655 return svn_error_createf(SVN_ERR_BAD_URL, 0, 1656 _("The external relative URL '%s' cannot have " 1657 "backpaths, i.e. '..'"), 1658 item->url); 1659 1660 /* Relative to the scheme: Build a new URL from the parts we know. */ 1661 if (0 == strncmp("//", url, 2)) 1662 { 1663 const char *scheme; 1664 1665 SVN_ERR(uri_scheme(&scheme, repos_root_url, scratch_pool)); 1666 *resolved_url = svn_uri_canonicalize(apr_pstrcat(scratch_pool, scheme, 1667 ":", url, (char *)NULL), 1668 result_pool); 1669 return SVN_NO_ERROR; 1670 } 1671 1672 /* Relative to the server root: Just replace the path portion of the 1673 parent's URL. */ 1674 if (url[0] == '/') 1675 { 1676 parent_dir_uri.path = (char *)url; 1677 *resolved_url = svn_uri_canonicalize(apr_uri_unparse(scratch_pool, 1678 &parent_dir_uri, 0), 1679 result_pool); 1680 return SVN_NO_ERROR; 1681 } 1682 1683 return svn_error_createf(SVN_ERR_BAD_URL, 0, 1684 _("Unrecognized format for the relative external " 1685 "URL '%s'"), 1686 item->url); 1687} 1688