adm_ops.c revision 299742
1/* 2 * adm_ops.c: routines for affecting working copy administrative 3 * information. NOTE: this code doesn't know where the adm 4 * info is actually stored. Instead, generic handles to 5 * adm data are requested via a reference to some PATH 6 * (PATH being a regular, non-administrative directory or 7 * file in the working copy). 8 * 9 * ==================================================================== 10 * Licensed to the Apache Software Foundation (ASF) under one 11 * or more contributor license agreements. See the NOTICE file 12 * distributed with this work for additional information 13 * regarding copyright ownership. The ASF licenses this file 14 * to you under the Apache License, Version 2.0 (the 15 * "License"); you may not use this file except in compliance 16 * with the License. You may obtain a copy of the License at 17 * 18 * http://www.apache.org/licenses/LICENSE-2.0 19 * 20 * Unless required by applicable law or agreed to in writing, 21 * software distributed under the License is distributed on an 22 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 * KIND, either express or implied. See the License for the 24 * specific language governing permissions and limitations 25 * under the License. 26 * ==================================================================== 27 */ 28 29 30 31#include <string.h> 32#include <stdlib.h> 33 34#include <apr_pools.h> 35#include <apr_hash.h> 36#include <apr_time.h> 37#include <apr_errno.h> 38 39#include "svn_private_config.h" 40#include "svn_types.h" 41#include "svn_pools.h" 42#include "svn_string.h" 43#include "svn_error.h" 44#include "svn_dirent_uri.h" 45#include "svn_path.h" 46#include "svn_hash.h" 47#include "svn_wc.h" 48#include "svn_io.h" 49#include "svn_time.h" 50#include "svn_sorts.h" 51 52#include "wc.h" 53#include "adm_files.h" 54#include "conflicts.h" 55#include "workqueue.h" 56 57#include "private/svn_dep_compat.h" 58#include "private/svn_sorts_private.h" 59#include "private/svn_subr_private.h" 60 61 62struct svn_wc_committed_queue_t 63{ 64 /* The pool in which ->queue is allocated. */ 65 apr_pool_t *pool; 66 /* Mapping (const char *) wcroot_abspath to svn_wc__db_commit_queue_t * */ 67 apr_hash_t *wc_queues; 68}; 69 70typedef struct committed_queue_item_t 71{ 72 const char *local_abspath; 73 svn_boolean_t recurse; /* Use legacy recursion */ 74 svn_boolean_t committed; /* Process the node as committed */ 75 svn_boolean_t remove_lock; /* Remove existing lock on node */ 76 svn_boolean_t remove_changelist; /* Remove changelist on node */ 77 78 /* The pristine text checksum. NULL if the old value should be kept 79 and for directories */ 80 const svn_checksum_t *new_sha1_checksum; 81 82 apr_hash_t *new_dav_cache; /* New DAV cache for the node */ 83} committed_queue_item_t; 84 85 86apr_pool_t * 87svn_wc__get_committed_queue_pool(const struct svn_wc_committed_queue_t *queue) 88{ 89 return queue->pool; 90} 91 92apr_hash_t * 93svn_wc__prop_array_to_hash(const apr_array_header_t *props, 94 apr_pool_t *result_pool) 95{ 96 int i; 97 apr_hash_t *prophash; 98 99 if (props == NULL || props->nelts == 0) 100 return NULL; 101 102 prophash = apr_hash_make(result_pool); 103 104 for (i = 0; i < props->nelts; i++) 105 { 106 const svn_prop_t *prop = APR_ARRAY_IDX(props, i, const svn_prop_t *); 107 if (prop->value != NULL) 108 svn_hash_sets(prophash, prop->name, prop->value); 109 } 110 111 return prophash; 112} 113 114 115svn_wc_committed_queue_t * 116svn_wc_committed_queue_create(apr_pool_t *pool) 117{ 118 svn_wc_committed_queue_t *q; 119 120 q = apr_palloc(pool, sizeof(*q)); 121 q->pool = pool; 122 q->wc_queues = apr_hash_make(pool); 123 124 return q; 125} 126 127 128svn_error_t * 129svn_wc_queue_committed4(svn_wc_committed_queue_t *queue, 130 svn_wc_context_t *wc_ctx, 131 const char *local_abspath, 132 svn_boolean_t recurse, 133 svn_boolean_t is_committed, 134 const apr_array_header_t *wcprop_changes, 135 svn_boolean_t remove_lock, 136 svn_boolean_t remove_changelist, 137 const svn_checksum_t *sha1_checksum, 138 apr_pool_t *scratch_pool) 139{ 140 const char *wcroot_abspath; 141 svn_wc__db_commit_queue_t *db_queue; 142 143 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 144 145 /* Use the same pool as the one QUEUE was allocated in, 146 to prevent lifetime issues. Intermediate operations 147 should use SCRATCH_POOL. */ 148 149 SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, 150 wc_ctx->db, local_abspath, 151 scratch_pool, scratch_pool)); 152 153 db_queue = svn_hash_gets(queue->wc_queues, wcroot_abspath); 154 if (! db_queue) 155 { 156 wcroot_abspath = apr_pstrdup(queue->pool, wcroot_abspath); 157 158 SVN_ERR(svn_wc__db_create_commit_queue(&db_queue, 159 wc_ctx->db, wcroot_abspath, 160 queue->pool, scratch_pool)); 161 162 svn_hash_sets(queue->wc_queues, wcroot_abspath, db_queue); 163 } 164 165 return svn_error_trace( 166 svn_wc__db_commit_queue_add(db_queue, local_abspath, recurse, 167 is_committed, remove_lock, 168 remove_changelist, sha1_checksum, 169 svn_wc__prop_array_to_hash(wcprop_changes, 170 queue->pool), 171 queue->pool, scratch_pool)); 172} 173 174 175svn_error_t * 176svn_wc_process_committed_queue2(svn_wc_committed_queue_t *queue, 177 svn_wc_context_t *wc_ctx, 178 svn_revnum_t new_revnum, 179 const char *rev_date, 180 const char *rev_author, 181 svn_cancel_func_t cancel_func, 182 void *cancel_baton, 183 apr_pool_t *scratch_pool) 184{ 185 apr_array_header_t *wcs; 186 int i; 187 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 188 apr_time_t new_date; 189 190 if (rev_date) 191 SVN_ERR(svn_time_from_cstring(&new_date, rev_date, iterpool)); 192 else 193 new_date = 0; 194 195 /* Process the wc's in order of their paths. */ 196 wcs = svn_sort__hash(queue->wc_queues, svn_sort_compare_items_as_paths, 197 scratch_pool); 198 for (i = 0; i < wcs->nelts; i++) 199 { 200 const svn_sort__item_t *sort_item 201 = &APR_ARRAY_IDX(wcs, i, svn_sort__item_t); 202 svn_wc__db_commit_queue_t *db_queue = sort_item->value; 203 204 svn_pool_clear(iterpool); 205 206 SVN_ERR(svn_wc__db_process_commit_queue(wc_ctx->db, db_queue, 207 new_revnum, new_date, rev_author, 208 iterpool)); 209 } 210 211 /* Make sure nothing happens if this function is called again. */ 212 apr_hash_clear(queue->wc_queues); 213 214 /* Ok; everything is committed now. Now we can start calling callbacks */ 215 if (cancel_func) 216 SVN_ERR(cancel_func(cancel_baton)); 217 218 for (i = 0; i < wcs->nelts; i++) 219 { 220 const svn_sort__item_t *sort_item 221 = &APR_ARRAY_IDX(wcs, i, svn_sort__item_t); 222 const char *wcroot_abspath = sort_item->key; 223 224 svn_pool_clear(iterpool); 225 226 SVN_ERR(svn_wc__wq_run(wc_ctx->db, wcroot_abspath, 227 cancel_func, cancel_baton, 228 iterpool)); 229 } 230 231 svn_pool_destroy(iterpool); 232 233 return SVN_NO_ERROR; 234} 235 236/* Schedule the single node at LOCAL_ABSPATH, of kind KIND, for addition in 237 * its parent directory in the WC. It will have the regular properties 238 * provided in PROPS, or none if that is NULL. 239 * 240 * If the node is a file, set its on-disk executable and read-only bits to 241 * match its properties and lock state, 242 * ### only if it has an svn:executable or svn:needs-lock property. 243 * ### This is to match the previous behaviour of setting its props 244 * afterwards by calling svn_wc_prop_set4(), but is not very clean. 245 * 246 * Sync the on-disk executable and read-only bits accordingly. 247 */ 248static svn_error_t * 249add_from_disk(svn_wc__db_t *db, 250 const char *local_abspath, 251 svn_node_kind_t kind, 252 const apr_hash_t *props, 253 apr_pool_t *scratch_pool) 254{ 255 if (kind == svn_node_file) 256 { 257 svn_skel_t *work_item = NULL; 258 259 if (props && (svn_prop_get_value(props, SVN_PROP_EXECUTABLE) 260 || svn_prop_get_value(props, SVN_PROP_NEEDS_LOCK))) 261 SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, db, local_abspath, 262 scratch_pool, scratch_pool)); 263 264 SVN_ERR(svn_wc__db_op_add_file(db, local_abspath, props, work_item, 265 scratch_pool)); 266 if (work_item) 267 SVN_ERR(svn_wc__wq_run(db, local_abspath, NULL, NULL, scratch_pool)); 268 } 269 else 270 { 271 SVN_ERR(svn_wc__db_op_add_directory(db, local_abspath, props, NULL, 272 scratch_pool)); 273 } 274 275 return SVN_NO_ERROR; 276} 277 278 279/* Set *REPOS_ROOT_URL and *REPOS_UUID to the repository of the parent of 280 LOCAL_ABSPATH. REPOS_ROOT_URL and/or REPOS_UUID may be NULL if not 281 wanted. Check that the parent of LOCAL_ABSPATH is a versioned directory 282 in a state in which a new child node can be scheduled for addition; 283 return an error if not. */ 284static svn_error_t * 285check_can_add_to_parent(const char **repos_root_url, 286 const char **repos_uuid, 287 svn_wc__db_t *db, 288 const char *local_abspath, 289 apr_pool_t *result_pool, 290 apr_pool_t *scratch_pool) 291{ 292 const char *parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool); 293 svn_wc__db_status_t parent_status; 294 svn_node_kind_t parent_kind; 295 svn_error_t *err; 296 297 SVN_ERR(svn_wc__write_check(db, parent_abspath, scratch_pool)); 298 299 err = svn_wc__db_read_info(&parent_status, &parent_kind, NULL, 300 NULL, repos_root_url, repos_uuid, NULL, NULL, 301 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 302 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 303 NULL, NULL, NULL, 304 db, parent_abspath, result_pool, scratch_pool); 305 306 if (err 307 || parent_status == svn_wc__db_status_not_present 308 || parent_status == svn_wc__db_status_excluded 309 || parent_status == svn_wc__db_status_server_excluded) 310 { 311 return 312 svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, err, 313 _("Can't find parent directory's node while" 314 " trying to add '%s'"), 315 svn_dirent_local_style(local_abspath, 316 scratch_pool)); 317 } 318 else if (parent_status == svn_wc__db_status_deleted) 319 { 320 return 321 svn_error_createf(SVN_ERR_WC_SCHEDULE_CONFLICT, NULL, 322 _("Can't add '%s' to a parent directory" 323 " scheduled for deletion"), 324 svn_dirent_local_style(local_abspath, 325 scratch_pool)); 326 } 327 else if (parent_kind != svn_node_dir) 328 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, 329 _("Can't schedule an addition of '%s'" 330 " below a not-directory node"), 331 svn_dirent_local_style(local_abspath, 332 scratch_pool)); 333 334 /* If we haven't found the repository info yet, find it now. */ 335 if ((repos_root_url && ! *repos_root_url) 336 || (repos_uuid && ! *repos_uuid)) 337 { 338 if (parent_status == svn_wc__db_status_added) 339 SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL, 340 repos_root_url, repos_uuid, NULL, 341 NULL, NULL, NULL, 342 db, parent_abspath, 343 result_pool, scratch_pool)); 344 else 345 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, 346 repos_root_url, repos_uuid, NULL, 347 NULL, NULL, NULL, NULL, NULL, NULL, 348 NULL, NULL, NULL, 349 db, parent_abspath, 350 result_pool, scratch_pool)); 351 } 352 353 return SVN_NO_ERROR; 354} 355 356 357/* Check that the on-disk item at LOCAL_ABSPATH can be scheduled for 358 * addition to its WC parent directory. 359 * 360 * Set *KIND_P to the kind of node to be added, *DB_ROW_EXISTS_P to whether 361 * it is already a versioned path, and if so, *IS_WC_ROOT_P to whether it's 362 * a WC root. 363 * 364 * ### The checks here, and the outputs, are geared towards svn_wc_add4(). 365 */ 366static svn_error_t * 367check_can_add_node(svn_node_kind_t *kind_p, 368 svn_boolean_t *db_row_exists_p, 369 svn_boolean_t *is_wc_root_p, 370 svn_wc__db_t *db, 371 const char *local_abspath, 372 const char *copyfrom_url, 373 svn_revnum_t copyfrom_rev, 374 apr_pool_t *scratch_pool) 375{ 376 const char *base_name = svn_dirent_basename(local_abspath, scratch_pool); 377 svn_boolean_t is_wc_root; 378 svn_node_kind_t kind; 379 svn_boolean_t is_special; 380 381 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 382 SVN_ERR_ASSERT(!copyfrom_url || (svn_uri_is_canonical(copyfrom_url, 383 scratch_pool) 384 && SVN_IS_VALID_REVNUM(copyfrom_rev))); 385 386 /* Check that the proposed node has an acceptable name. */ 387 if (svn_wc_is_adm_dir(base_name, scratch_pool)) 388 return svn_error_createf 389 (SVN_ERR_ENTRY_FORBIDDEN, NULL, 390 _("Can't create an entry with a reserved name while trying to add '%s'"), 391 svn_dirent_local_style(local_abspath, scratch_pool)); 392 393 SVN_ERR(svn_path_check_valid(local_abspath, scratch_pool)); 394 395 /* Make sure something's there; set KIND and *KIND_P. */ 396 SVN_ERR(svn_io_check_special_path(local_abspath, &kind, &is_special, 397 scratch_pool)); 398 if (kind == svn_node_none) 399 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, 400 _("'%s' not found"), 401 svn_dirent_local_style(local_abspath, 402 scratch_pool)); 403 if (kind == svn_node_unknown) 404 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 405 _("Unsupported node kind for path '%s'"), 406 svn_dirent_local_style(local_abspath, 407 scratch_pool)); 408 if (kind_p) 409 *kind_p = kind; 410 411 /* Determine whether a DB row for this node EXISTS, and whether it 412 IS_WC_ROOT. If it exists, check that it is in an acceptable state for 413 adding the new node; if not, return an error. */ 414 { 415 svn_wc__db_status_t status; 416 svn_boolean_t conflicted; 417 svn_boolean_t exists; 418 svn_error_t *err 419 = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL, 420 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 421 NULL, NULL, NULL, NULL, NULL, NULL, 422 &conflicted, 423 NULL, NULL, NULL, NULL, NULL, NULL, 424 db, local_abspath, 425 scratch_pool, scratch_pool); 426 427 if (err) 428 { 429 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 430 return svn_error_trace(err); 431 432 svn_error_clear(err); 433 exists = FALSE; 434 is_wc_root = FALSE; 435 } 436 else 437 { 438 is_wc_root = FALSE; 439 exists = TRUE; 440 441 /* Note that the node may be in conflict even if it does not 442 * exist on disk (certain tree conflict scenarios). */ 443 if (conflicted) 444 return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL, 445 _("'%s' is an existing item in conflict; " 446 "please mark the conflict as resolved " 447 "before adding a new item here"), 448 svn_dirent_local_style(local_abspath, 449 scratch_pool)); 450 switch (status) 451 { 452 case svn_wc__db_status_not_present: 453 break; 454 case svn_wc__db_status_deleted: 455 /* A working copy root should never have a WORKING_NODE */ 456 SVN_ERR_ASSERT(!is_wc_root); 457 break; 458 case svn_wc__db_status_normal: 459 SVN_ERR(svn_wc__db_is_wcroot(&is_wc_root, db, local_abspath, 460 scratch_pool)); 461 462 if (is_wc_root && copyfrom_url) 463 { 464 /* Integrate a sub working copy in a parent working copy 465 (legacy behavior) */ 466 break; 467 } 468 else if (is_wc_root && is_special) 469 { 470 /* Adding a symlink to a working copy root. 471 (special_tests.py 23: externals as symlink targets) */ 472 break; 473 } 474 /* else: Fall through in default error */ 475 476 default: 477 return svn_error_createf( 478 SVN_ERR_ENTRY_EXISTS, NULL, 479 _("'%s' is already under version control"), 480 svn_dirent_local_style(local_abspath, 481 scratch_pool)); 482 } 483 } /* err */ 484 485 if (db_row_exists_p) 486 *db_row_exists_p = exists; 487 if (is_wc_root_p) 488 *is_wc_root_p = is_wc_root; 489 } 490 491 return SVN_NO_ERROR; 492} 493 494 495/* Convert the nested pristine working copy rooted at LOCAL_ABSPATH into 496 * a copied subtree in the outer working copy. 497 * 498 * LOCAL_ABSPATH must be the root of a nested working copy that has no 499 * local modifications. The parent directory of LOCAL_ABSPATH must be a 500 * versioned directory in the outer WC, and must belong to the same 501 * repository as the nested WC. The nested WC will be integrated into the 502 * parent's WC, and will no longer be a separate WC. */ 503static svn_error_t * 504integrate_nested_wc_as_copy(svn_wc_context_t *wc_ctx, 505 const char *local_abspath, 506 apr_pool_t *scratch_pool) 507{ 508 svn_wc__db_t *db = wc_ctx->db; 509 const char *moved_abspath; 510 511 /* Drop any references to the wc that is to be rewritten */ 512 SVN_ERR(svn_wc__db_drop_root(db, local_abspath, scratch_pool)); 513 514 /* Move the admin dir from the wc to a temporary location: MOVED_ABSPATH */ 515 { 516 const char *tmpdir_abspath; 517 const char *moved_adm_abspath; 518 const char *adm_abspath; 519 520 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmpdir_abspath, db, 521 svn_dirent_dirname(local_abspath, 522 scratch_pool), 523 scratch_pool, scratch_pool)); 524 SVN_ERR(svn_io_open_unique_file3(NULL, &moved_abspath, tmpdir_abspath, 525 svn_io_file_del_on_close, 526 scratch_pool, scratch_pool)); 527 SVN_ERR(svn_io_dir_make(moved_abspath, APR_OS_DEFAULT, scratch_pool)); 528 529 adm_abspath = svn_wc__adm_child(local_abspath, "", scratch_pool); 530 moved_adm_abspath = svn_wc__adm_child(moved_abspath, "", scratch_pool); 531 SVN_ERR(svn_io_file_move(adm_abspath, moved_adm_abspath, scratch_pool)); 532 } 533 534 /* Copy entries from temporary location into the main db */ 535 SVN_ERR(svn_wc_copy3(wc_ctx, moved_abspath, local_abspath, 536 TRUE /* metadata_only */, 537 NULL, NULL, NULL, NULL, scratch_pool)); 538 539 /* Cleanup the temporary admin dir */ 540 SVN_ERR(svn_wc__db_drop_root(db, moved_abspath, scratch_pool)); 541 SVN_ERR(svn_io_remove_dir2(moved_abspath, FALSE, NULL, NULL, 542 scratch_pool)); 543 544 /* The subdir is now part of our parent working copy. Our caller assumes 545 that we return the new node locked, so obtain a lock if we didn't 546 receive the lock via our depth infinity lock */ 547 { 548 svn_boolean_t owns_lock; 549 550 SVN_ERR(svn_wc__db_wclock_owns_lock(&owns_lock, db, local_abspath, 551 FALSE, scratch_pool)); 552 if (!owns_lock) 553 SVN_ERR(svn_wc__db_wclock_obtain(db, local_abspath, 0, FALSE, 554 scratch_pool)); 555 } 556 557 return SVN_NO_ERROR; 558} 559 560 561svn_error_t * 562svn_wc_add4(svn_wc_context_t *wc_ctx, 563 const char *local_abspath, 564 svn_depth_t depth, 565 const char *copyfrom_url, 566 svn_revnum_t copyfrom_rev, 567 svn_cancel_func_t cancel_func, 568 void *cancel_baton, 569 svn_wc_notify_func2_t notify_func, 570 void *notify_baton, 571 apr_pool_t *scratch_pool) 572{ 573 svn_wc__db_t *db = wc_ctx->db; 574 svn_node_kind_t kind; 575 svn_boolean_t db_row_exists; 576 svn_boolean_t is_wc_root; 577 const char *repos_root_url; 578 const char *repos_uuid; 579 580 SVN_ERR(check_can_add_node(&kind, &db_row_exists, &is_wc_root, 581 db, local_abspath, copyfrom_url, copyfrom_rev, 582 scratch_pool)); 583 584 /* Get REPOS_ROOT_URL and REPOS_UUID. Check that the 585 parent is a versioned directory in an acceptable state. */ 586 SVN_ERR(check_can_add_to_parent(&repos_root_url, &repos_uuid, 587 db, local_abspath, scratch_pool, 588 scratch_pool)); 589 590 /* If we're performing a repos-to-WC copy, check that the copyfrom 591 repository is the same as the parent dir's repository. */ 592 if (copyfrom_url && !svn_uri__is_ancestor(repos_root_url, copyfrom_url)) 593 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 594 _("The URL '%s' has a different repository " 595 "root than its parent"), copyfrom_url); 596 597 /* Verify that we can actually integrate the inner working copy */ 598 if (is_wc_root) 599 { 600 const char *repos_relpath, *inner_repos_root_url, *inner_repos_uuid; 601 const char *inner_url; 602 603 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, &repos_relpath, 604 &inner_repos_root_url, 605 &inner_repos_uuid, NULL, NULL, NULL, 606 NULL, NULL, NULL, NULL, NULL, NULL, 607 NULL, 608 db, local_abspath, 609 scratch_pool, scratch_pool)); 610 611 if (strcmp(inner_repos_uuid, repos_uuid) 612 || strcmp(repos_root_url, inner_repos_root_url)) 613 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 614 _("Can't schedule the working copy at '%s' " 615 "from repository '%s' with uuid '%s' " 616 "for addition under a working copy from " 617 "repository '%s' with uuid '%s'."), 618 svn_dirent_local_style(local_abspath, 619 scratch_pool), 620 inner_repos_root_url, inner_repos_uuid, 621 repos_root_url, repos_uuid); 622 623 inner_url = svn_path_url_add_component2(repos_root_url, repos_relpath, 624 scratch_pool); 625 626 if (strcmp(copyfrom_url, inner_url)) 627 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 628 _("Can't add '%s' with URL '%s', but with " 629 "the data from '%s'"), 630 svn_dirent_local_style(local_abspath, 631 scratch_pool), 632 copyfrom_url, inner_url); 633 } 634 635 if (!copyfrom_url) /* Case 2a: It's a simple add */ 636 { 637 SVN_ERR(add_from_disk(db, local_abspath, kind, NULL, 638 scratch_pool)); 639 if (kind == svn_node_dir && !db_row_exists) 640 { 641 /* If using the legacy 1.6 interface the parent lock may not 642 be recursive and add is expected to lock the new dir. 643 644 ### Perhaps the lock should be created in the same 645 transaction that adds the node? */ 646 svn_boolean_t owns_lock; 647 648 SVN_ERR(svn_wc__db_wclock_owns_lock(&owns_lock, db, local_abspath, 649 FALSE, scratch_pool)); 650 if (!owns_lock) 651 SVN_ERR(svn_wc__db_wclock_obtain(db, local_abspath, 0, FALSE, 652 scratch_pool)); 653 } 654 } 655 else if (!is_wc_root) /* Case 2b: It's a copy from the repository */ 656 { 657 if (kind == svn_node_file) 658 { 659 /* This code should never be used, as it doesn't install proper 660 pristine and/or properties. But it was not an error in the old 661 version of this function. 662 663 ===> Use svn_wc_add_repos_file4() directly! */ 664 svn_stream_t *content = svn_stream_empty(scratch_pool); 665 666 SVN_ERR(svn_wc_add_repos_file4(wc_ctx, local_abspath, 667 content, NULL, NULL, NULL, 668 copyfrom_url, copyfrom_rev, 669 cancel_func, cancel_baton, 670 scratch_pool)); 671 } 672 else 673 { 674 const char *repos_relpath = 675 svn_uri_skip_ancestor(repos_root_url, copyfrom_url, scratch_pool); 676 677 SVN_ERR(svn_wc__db_op_copy_dir(db, local_abspath, 678 apr_hash_make(scratch_pool), 679 copyfrom_rev, 0, NULL, 680 repos_relpath, 681 repos_root_url, repos_uuid, 682 copyfrom_rev, 683 NULL /* children */, depth, 684 FALSE /* is_move */, 685 NULL /* conflicts */, 686 NULL /* work items */, 687 scratch_pool)); 688 } 689 } 690 else /* Case 1: Integrating a separate WC into this one, in place */ 691 { 692 SVN_ERR(integrate_nested_wc_as_copy(wc_ctx, local_abspath, 693 scratch_pool)); 694 } 695 696 /* Report the addition to the caller. */ 697 if (notify_func != NULL) 698 { 699 svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath, 700 svn_wc_notify_add, 701 scratch_pool); 702 notify->kind = kind; 703 (*notify_func)(notify_baton, notify, scratch_pool); 704 } 705 706 return SVN_NO_ERROR; 707} 708 709 710svn_error_t * 711svn_wc_add_from_disk3(svn_wc_context_t *wc_ctx, 712 const char *local_abspath, 713 const apr_hash_t *props, 714 svn_boolean_t skip_checks, 715 svn_wc_notify_func2_t notify_func, 716 void *notify_baton, 717 apr_pool_t *scratch_pool) 718{ 719 svn_node_kind_t kind; 720 721 SVN_ERR(check_can_add_node(&kind, NULL, NULL, wc_ctx->db, local_abspath, 722 NULL, SVN_INVALID_REVNUM, scratch_pool)); 723 SVN_ERR(check_can_add_to_parent(NULL, NULL, wc_ctx->db, local_abspath, 724 scratch_pool, scratch_pool)); 725 726 /* Canonicalize and check the props */ 727 if (props) 728 { 729 apr_hash_t *new_props; 730 731 SVN_ERR(svn_wc__canonicalize_props( 732 &new_props, 733 local_abspath, kind, props, skip_checks, 734 scratch_pool, scratch_pool)); 735 props = new_props; 736 } 737 738 /* Add to the DB and maybe update on-disk executable read-only bits */ 739 SVN_ERR(add_from_disk(wc_ctx->db, local_abspath, kind, props, 740 scratch_pool)); 741 742 /* Report the addition to the caller. */ 743 if (notify_func != NULL) 744 { 745 svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath, 746 svn_wc_notify_add, 747 scratch_pool); 748 notify->kind = kind; 749 notify->mime_type = svn_prop_get_value(props, SVN_PROP_MIME_TYPE); 750 (*notify_func)(notify_baton, notify, scratch_pool); 751 } 752 753 return SVN_NO_ERROR; 754} 755 756/* Return a path where nothing exists on disk, within the admin directory 757 belonging to the WCROOT_ABSPATH directory. */ 758static const char * 759nonexistent_path(const char *wcroot_abspath, apr_pool_t *scratch_pool) 760{ 761 return svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_NONEXISTENT_PATH, 762 scratch_pool); 763} 764 765 766svn_error_t * 767svn_wc_get_pristine_copy_path(const char *path, 768 const char **pristine_path, 769 apr_pool_t *pool) 770{ 771 svn_wc__db_t *db; 772 const char *local_abspath; 773 svn_error_t *err; 774 775 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool)); 776 777 SVN_ERR(svn_wc__db_open(&db, NULL, FALSE, TRUE, pool, pool)); 778 /* DB is now open. This is seemingly a "light" function that a caller 779 may use repeatedly despite error return values. The rest of this 780 function should aggressively close DB, even in the error case. */ 781 782 err = svn_wc__text_base_path_to_read(pristine_path, db, local_abspath, 783 pool, pool); 784 if (err && err->apr_err == SVN_ERR_WC_PATH_UNEXPECTED_STATUS) 785 { 786 /* The node doesn't exist, so return a non-existent path located 787 in WCROOT/.svn/ */ 788 const char *wcroot_abspath; 789 790 svn_error_clear(err); 791 792 err = svn_wc__db_get_wcroot(&wcroot_abspath, db, local_abspath, 793 pool, pool); 794 if (err == NULL) 795 *pristine_path = nonexistent_path(wcroot_abspath, pool); 796 } 797 798 return svn_error_compose_create(err, svn_wc__db_close(db)); 799} 800 801 802svn_error_t * 803svn_wc_get_pristine_contents2(svn_stream_t **contents, 804 svn_wc_context_t *wc_ctx, 805 const char *local_abspath, 806 apr_pool_t *result_pool, 807 apr_pool_t *scratch_pool) 808{ 809 return svn_error_trace(svn_wc__get_pristine_contents(contents, NULL, 810 wc_ctx->db, 811 local_abspath, 812 result_pool, 813 scratch_pool)); 814} 815 816 817typedef struct get_pristine_lazyopen_baton_t 818{ 819 svn_wc_context_t *wc_ctx; 820 const char *wri_abspath; 821 const svn_checksum_t *checksum; 822 823} get_pristine_lazyopen_baton_t; 824 825 826/* Implements svn_stream_lazyopen_func_t */ 827static svn_error_t * 828get_pristine_lazyopen_func(svn_stream_t **stream, 829 void *baton, 830 apr_pool_t *result_pool, 831 apr_pool_t *scratch_pool) 832{ 833 get_pristine_lazyopen_baton_t *b = baton; 834 const svn_checksum_t *sha1_checksum; 835 836 /* svn_wc__db_pristine_read() wants a SHA1, so if we have an MD5, 837 we'll use it to lookup the SHA1. */ 838 if (b->checksum->kind == svn_checksum_sha1) 839 sha1_checksum = b->checksum; 840 else 841 SVN_ERR(svn_wc__db_pristine_get_sha1(&sha1_checksum, b->wc_ctx->db, 842 b->wri_abspath, b->checksum, 843 scratch_pool, scratch_pool)); 844 845 SVN_ERR(svn_wc__db_pristine_read(stream, NULL, b->wc_ctx->db, 846 b->wri_abspath, sha1_checksum, 847 result_pool, scratch_pool)); 848 return SVN_NO_ERROR; 849} 850 851svn_error_t * 852svn_wc__get_pristine_contents_by_checksum(svn_stream_t **contents, 853 svn_wc_context_t *wc_ctx, 854 const char *wri_abspath, 855 const svn_checksum_t *checksum, 856 apr_pool_t *result_pool, 857 apr_pool_t *scratch_pool) 858{ 859 svn_boolean_t present; 860 861 *contents = NULL; 862 863 SVN_ERR(svn_wc__db_pristine_check(&present, wc_ctx->db, wri_abspath, 864 checksum, scratch_pool)); 865 866 if (present) 867 { 868 get_pristine_lazyopen_baton_t *gpl_baton; 869 870 gpl_baton = apr_pcalloc(result_pool, sizeof(*gpl_baton)); 871 gpl_baton->wc_ctx = wc_ctx; 872 gpl_baton->wri_abspath = wri_abspath; 873 gpl_baton->checksum = checksum; 874 875 *contents = svn_stream_lazyopen_create(get_pristine_lazyopen_func, 876 gpl_baton, FALSE, result_pool); 877 } 878 879 return SVN_NO_ERROR; 880} 881 882 883 884svn_error_t * 885svn_wc_add_lock2(svn_wc_context_t *wc_ctx, 886 const char *local_abspath, 887 const svn_lock_t *lock, 888 apr_pool_t *scratch_pool) 889{ 890 svn_wc__db_lock_t db_lock; 891 svn_error_t *err; 892 const svn_string_t *needs_lock; 893 894 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 895 896 SVN_ERR(svn_wc__write_check(wc_ctx->db, 897 svn_dirent_dirname(local_abspath, scratch_pool), 898 scratch_pool)); 899 900 db_lock.token = lock->token; 901 db_lock.owner = lock->owner; 902 db_lock.comment = lock->comment; 903 db_lock.date = lock->creation_date; 904 err = svn_wc__db_lock_add(wc_ctx->db, local_abspath, &db_lock, scratch_pool); 905 if (err) 906 { 907 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 908 return svn_error_trace(err); 909 910 /* Remap the error. */ 911 svn_error_clear(err); 912 return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL, 913 _("'%s' is not under version control"), 914 svn_dirent_local_style(local_abspath, 915 scratch_pool)); 916 } 917 918 /* if svn:needs-lock is present, then make the file read-write. */ 919 err = svn_wc__internal_propget(&needs_lock, wc_ctx->db, local_abspath, 920 SVN_PROP_NEEDS_LOCK, scratch_pool, 921 scratch_pool); 922 923 if (err && err->apr_err == SVN_ERR_WC_PATH_UNEXPECTED_STATUS) 924 { 925 /* The node has non wc representation (e.g. deleted), so 926 we don't want to touch the in-wc file */ 927 svn_error_clear(err); 928 return SVN_NO_ERROR; 929 } 930 SVN_ERR(err); 931 932 if (needs_lock) 933 SVN_ERR(svn_io_set_file_read_write(local_abspath, FALSE, scratch_pool)); 934 935 return SVN_NO_ERROR; 936} 937 938 939svn_error_t * 940svn_wc_remove_lock2(svn_wc_context_t *wc_ctx, 941 const char *local_abspath, 942 apr_pool_t *scratch_pool) 943{ 944 svn_error_t *err; 945 svn_skel_t *work_item; 946 947 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 948 949 SVN_ERR(svn_wc__write_check(wc_ctx->db, 950 svn_dirent_dirname(local_abspath, scratch_pool), 951 scratch_pool)); 952 953 SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, 954 wc_ctx->db, local_abspath, 955 scratch_pool, scratch_pool)); 956 957 err = svn_wc__db_lock_remove(wc_ctx->db, local_abspath, work_item, 958 scratch_pool); 959 if (err) 960 { 961 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 962 return svn_error_trace(err); 963 964 /* Remap the error. */ 965 svn_error_clear(err); 966 return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL, 967 _("'%s' is not under version control"), 968 svn_dirent_local_style(local_abspath, 969 scratch_pool)); 970 } 971 972 return svn_error_trace(svn_wc__wq_run(wc_ctx->db, local_abspath, 973 NULL, NULL /* cancel*/, 974 scratch_pool)); 975} 976 977 978svn_error_t * 979svn_wc_set_changelist2(svn_wc_context_t *wc_ctx, 980 const char *local_abspath, 981 const char *new_changelist, 982 svn_depth_t depth, 983 const apr_array_header_t *changelist_filter, 984 svn_cancel_func_t cancel_func, 985 void *cancel_baton, 986 svn_wc_notify_func2_t notify_func, 987 void *notify_baton, 988 apr_pool_t *scratch_pool) 989{ 990 /* Assert that we aren't being asked to set an empty changelist. */ 991 SVN_ERR_ASSERT(! (new_changelist && new_changelist[0] == '\0')); 992 993 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 994 995 SVN_ERR(svn_wc__db_op_set_changelist(wc_ctx->db, local_abspath, 996 new_changelist, changelist_filter, 997 depth, notify_func, notify_baton, 998 cancel_func, cancel_baton, 999 scratch_pool)); 1000 1001 return SVN_NO_ERROR; 1002} 1003 1004struct get_cl_fn_baton 1005{ 1006 svn_wc__db_t *db; 1007 apr_hash_t *clhash; 1008 svn_changelist_receiver_t callback_func; 1009 void *callback_baton; 1010}; 1011 1012 1013static svn_error_t * 1014get_node_changelist(const char *local_abspath, 1015 svn_node_kind_t kind, 1016 void *baton, 1017 apr_pool_t *scratch_pool) 1018{ 1019 struct get_cl_fn_baton *b = baton; 1020 const char *changelist; 1021 1022 SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1023 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1024 NULL, NULL, NULL, NULL, NULL, &changelist, 1025 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1026 b->db, local_abspath, 1027 scratch_pool, scratch_pool)); 1028 if (!b->clhash 1029 || (changelist && svn_hash_gets(b->clhash, changelist) != NULL)) 1030 SVN_ERR(b->callback_func(b->callback_baton, local_abspath, 1031 changelist, scratch_pool)); 1032 1033 return SVN_NO_ERROR; 1034} 1035 1036 1037svn_error_t * 1038svn_wc_get_changelists(svn_wc_context_t *wc_ctx, 1039 const char *local_abspath, 1040 svn_depth_t depth, 1041 const apr_array_header_t *changelist_filter, 1042 svn_changelist_receiver_t callback_func, 1043 void *callback_baton, 1044 svn_cancel_func_t cancel_func, 1045 void *cancel_baton, 1046 apr_pool_t *scratch_pool) 1047{ 1048 struct get_cl_fn_baton gnb; 1049 1050 gnb.db = wc_ctx->db; 1051 gnb.clhash = NULL; 1052 gnb.callback_func = callback_func; 1053 gnb.callback_baton = callback_baton; 1054 1055 if (changelist_filter) 1056 SVN_ERR(svn_hash_from_cstring_keys(&gnb.clhash, changelist_filter, 1057 scratch_pool)); 1058 1059 return svn_error_trace( 1060 svn_wc__internal_walk_children(wc_ctx->db, local_abspath, FALSE, 1061 changelist_filter, get_node_changelist, 1062 &gnb, depth, 1063 cancel_func, cancel_baton, 1064 scratch_pool)); 1065 1066} 1067 1068 1069svn_boolean_t 1070svn_wc__internal_changelist_match(svn_wc__db_t *db, 1071 const char *local_abspath, 1072 const apr_hash_t *clhash, 1073 apr_pool_t *scratch_pool) 1074{ 1075 svn_error_t *err; 1076 const char *changelist; 1077 1078 if (clhash == NULL) 1079 return TRUE; 1080 1081 err = svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1082 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1083 NULL, NULL, NULL, NULL, NULL, &changelist, 1084 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1085 db, local_abspath, scratch_pool, scratch_pool); 1086 if (err) 1087 { 1088 svn_error_clear(err); 1089 return FALSE; 1090 } 1091 1092 return (changelist 1093 && svn_hash_gets((apr_hash_t *)clhash, changelist) != NULL); 1094} 1095 1096 1097svn_boolean_t 1098svn_wc__changelist_match(svn_wc_context_t *wc_ctx, 1099 const char *local_abspath, 1100 const apr_hash_t *clhash, 1101 apr_pool_t *scratch_pool) 1102{ 1103 return svn_wc__internal_changelist_match(wc_ctx->db, local_abspath, clhash, 1104 scratch_pool); 1105} 1106