1/* 2 * lock.c: routines for locking working copy subdirectories. 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#define SVN_DEPRECATED 25 26#include <apr_pools.h> 27#include <apr_time.h> 28 29#include "svn_pools.h" 30#include "svn_dirent_uri.h" 31#include "svn_path.h" 32#include "svn_sorts.h" 33#include "svn_hash.h" 34#include "svn_types.h" 35 36#include "wc.h" 37#include "adm_files.h" 38#include "lock.h" 39#include "props.h" 40#include "wc_db.h" 41 42#include "svn_private_config.h" 43#include "private/svn_wc_private.h" 44 45 46 47 48struct svn_wc_adm_access_t 49{ 50 /* PATH to directory which contains the administrative area */ 51 const char *path; 52 53 /* And the absolute form of the path. */ 54 const char *abspath; 55 56 /* Indicates that the baton has been closed. */ 57 svn_boolean_t closed; 58 59 /* Handle to the administrative database. */ 60 svn_wc__db_t *db; 61 62 /* Was the DB provided to us? If so, then we'll never close it. */ 63 svn_boolean_t db_provided; 64 65 /* ENTRIES_HIDDEN is all cached entries including those in 66 state deleted or state absent. It may be NULL. */ 67 apr_hash_t *entries_all; 68 69 /* POOL is used to allocate cached items, they need to persist for the 70 lifetime of this access baton */ 71 apr_pool_t *pool; 72 73}; 74 75 76/* This is a placeholder used in the set hash to represent missing 77 directories. Only its address is important, it contains no useful 78 data. */ 79static const svn_wc_adm_access_t missing = { 0 }; 80#define IS_MISSING(lock) ((lock) == &missing) 81 82/* ### hack for now. future functionality coming in a future revision. */ 83#define svn_wc__db_is_closed(db) FALSE 84 85 86svn_error_t * 87svn_wc__internal_check_wc(int *wc_format, 88 svn_wc__db_t *db, 89 const char *local_abspath, 90 svn_boolean_t check_path, 91 apr_pool_t *scratch_pool) 92{ 93 svn_error_t *err; 94 95 err = svn_wc__db_temp_get_format(wc_format, db, local_abspath, scratch_pool); 96 if (err) 97 { 98 svn_node_kind_t kind; 99 100 if (err->apr_err != SVN_ERR_WC_MISSING && 101 err->apr_err != SVN_ERR_WC_UNSUPPORTED_FORMAT && 102 err->apr_err != SVN_ERR_WC_UPGRADE_REQUIRED) 103 return svn_error_trace(err); 104 svn_error_clear(err); 105 106 /* ### the stuff below seems to be redundant. get_format() probably 107 ### does all this. 108 ### 109 ### investigate all callers. DEFINITELY keep in mind the 110 ### svn_wc_check_wc() entrypoint. 111 */ 112 113 /* If the format file does not exist or path not directory, then for 114 our purposes this is not a working copy, so return 0. */ 115 *wc_format = 0; 116 117 /* Check path itself exists. */ 118 SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool)); 119 if (kind == svn_node_none) 120 { 121 return svn_error_createf(APR_ENOENT, NULL, _("'%s' does not exist"), 122 svn_dirent_local_style(local_abspath, 123 scratch_pool)); 124 } 125 } 126 127 if (*wc_format >= SVN_WC__WC_NG_VERSION) 128 { 129 svn_wc__db_status_t db_status; 130 svn_node_kind_t db_kind; 131 132 if (check_path) 133 { 134 /* If a node is not a directory, it is not a working copy 135 directory. This allows creating new working copies as 136 a path below an existing working copy. */ 137 svn_node_kind_t wc_kind; 138 139 SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool)); 140 if (wc_kind != svn_node_dir) 141 { 142 *wc_format = 0; /* Not a directory, so not a wc-directory */ 143 return SVN_NO_ERROR; 144 } 145 } 146 147 err = svn_wc__db_read_info(&db_status, &db_kind, NULL, NULL, NULL, 148 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 149 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 150 NULL, NULL, NULL, NULL, NULL, 151 NULL, NULL, NULL, 152 db, local_abspath, 153 scratch_pool, scratch_pool); 154 155 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) 156 { 157 svn_error_clear(err); 158 *wc_format = 0; 159 return SVN_NO_ERROR; 160 } 161 else 162 SVN_ERR(err); 163 164 if (db_kind != svn_node_dir) 165 { 166 /* The WC thinks there must be a file, so this is not 167 a wc-directory */ 168 *wc_format = 0; 169 return SVN_NO_ERROR; 170 } 171 172 switch (db_status) 173 { 174 case svn_wc__db_status_not_present: 175 case svn_wc__db_status_server_excluded: 176 case svn_wc__db_status_excluded: 177 /* If there is a directory here, it is not related to the parent 178 working copy: Obstruction */ 179 *wc_format = 0; 180 return SVN_NO_ERROR; 181 default: 182 break; 183 } 184 } 185 186 return SVN_NO_ERROR; 187} 188 189 190svn_error_t * 191svn_wc_check_wc2(int *wc_format, 192 svn_wc_context_t *wc_ctx, 193 const char *local_abspath, 194 apr_pool_t *scratch_pool) 195{ 196 /* ### Should we pass TRUE for check_path to find obstructions and 197 missing directories? */ 198 return svn_error_trace( 199 svn_wc__internal_check_wc(wc_format, wc_ctx->db, local_abspath, FALSE, 200 scratch_pool)); 201} 202 203 204/* */ 205static svn_error_t * 206add_to_shared(svn_wc_adm_access_t *lock, apr_pool_t *scratch_pool) 207{ 208 /* ### sometimes we replace &missing with a now-valid lock. */ 209 { 210 svn_wc_adm_access_t *prior = svn_wc__db_temp_get_access(lock->db, 211 lock->abspath, 212 scratch_pool); 213 if (IS_MISSING(prior)) 214 SVN_ERR(svn_wc__db_temp_close_access(lock->db, lock->abspath, 215 prior, scratch_pool)); 216 } 217 218 svn_wc__db_temp_set_access(lock->db, lock->abspath, lock, 219 scratch_pool); 220 221 return SVN_NO_ERROR; 222} 223 224 225/* */ 226static svn_wc_adm_access_t * 227get_from_shared(const char *abspath, 228 svn_wc__db_t *db, 229 apr_pool_t *scratch_pool) 230{ 231 /* We closed the DB when it became empty. ABSPATH is not present. */ 232 if (db == NULL) 233 return NULL; 234 return svn_wc__db_temp_get_access(db, abspath, scratch_pool); 235} 236 237 238/* */ 239static svn_error_t * 240close_single(svn_wc_adm_access_t *adm_access, 241 svn_boolean_t preserve_lock, 242 apr_pool_t *scratch_pool) 243{ 244 svn_boolean_t locked; 245 246 if (adm_access->closed) 247 return SVN_NO_ERROR; 248 249 /* Physically unlock if required */ 250 SVN_ERR(svn_wc__db_wclock_owns_lock(&locked, adm_access->db, 251 adm_access->abspath, TRUE, 252 scratch_pool)); 253 if (locked) 254 { 255 if (!preserve_lock) 256 { 257 /* Remove the physical lock in the admin directory for 258 PATH. It is acceptable for the administrative area to 259 have disappeared, such as when the directory is removed 260 from the working copy. It is an error for the lock to 261 have disappeared if the administrative area still exists. */ 262 263 svn_error_t *err = svn_wc__db_wclock_release(adm_access->db, 264 adm_access->abspath, 265 scratch_pool); 266 if (err) 267 { 268 if (svn_wc__adm_area_exists(adm_access->abspath, scratch_pool)) 269 return err; 270 svn_error_clear(err); 271 } 272 } 273 } 274 275 /* Reset to prevent further use of the lock. */ 276 adm_access->closed = TRUE; 277 278 /* Detach from set */ 279 SVN_ERR(svn_wc__db_temp_close_access(adm_access->db, adm_access->abspath, 280 adm_access, scratch_pool)); 281 282 /* Possibly close the underlying wc_db. */ 283 if (!adm_access->db_provided) 284 { 285 apr_hash_t *opened = svn_wc__db_temp_get_all_access(adm_access->db, 286 scratch_pool); 287 if (apr_hash_count(opened) == 0) 288 { 289 SVN_ERR(svn_wc__db_close(adm_access->db)); 290 adm_access->db = NULL; 291 } 292 } 293 294 return SVN_NO_ERROR; 295} 296 297 298/* Cleanup for a locked access baton. 299 300 This handles closing access batons when their pool gets destroyed. 301 The physical locks associated with such batons remain in the working 302 copy if they are protecting work items in the workqueue. */ 303static apr_status_t 304pool_cleanup_locked(void *p) 305{ 306 svn_wc_adm_access_t *lock = p; 307 apr_uint64_t id; 308 svn_skel_t *work_item; 309 svn_error_t *err; 310 311 if (lock->closed) 312 return APR_SUCCESS; 313 314 /* If the DB is closed, then we have a bunch of extra work to do. */ 315 if (svn_wc__db_is_closed(lock->db)) 316 { 317 apr_pool_t *scratch_pool; 318 svn_wc__db_t *db; 319 320 lock->closed = TRUE; 321 322 /* If there is no ADM area, then we definitely have no work items 323 or physical locks to worry about. Bail out. */ 324 if (!svn_wc__adm_area_exists(lock->abspath, lock->pool)) 325 return APR_SUCCESS; 326 327 /* Creating a subpool is safe within a pool cleanup, as long as 328 we're absolutely sure to destroy it before we exit this function. 329 330 We avoid using LOCK->POOL to keep the following functions from 331 hanging cleanups or subpools from it. (the cleanups *might* get 332 run, but the subpools will NOT be destroyed) */ 333 scratch_pool = svn_pool_create(lock->pool); 334 335 err = svn_wc__db_open(&db, NULL /* ### config. need! */, FALSE, TRUE, 336 scratch_pool, scratch_pool); 337 if (!err) 338 { 339 err = svn_wc__db_wq_fetch_next(&id, &work_item, db, lock->abspath, 0, 340 scratch_pool, scratch_pool); 341 if (!err && work_item == NULL) 342 { 343 /* There is no remaining work, so we're good to remove any 344 potential "physical" lock. */ 345 err = svn_wc__db_wclock_release(db, lock->abspath, scratch_pool); 346 } 347 } 348 svn_error_clear(err); 349 350 /* Closes the DB, too. */ 351 svn_pool_destroy(scratch_pool); 352 353 return APR_SUCCESS; 354 } 355 356 /* ### should we create an API that just looks, but doesn't return? */ 357 err = svn_wc__db_wq_fetch_next(&id, &work_item, lock->db, lock->abspath, 0, 358 lock->pool, lock->pool); 359 360 /* Close just this access baton. The pool cleanup will close the rest. */ 361 if (!err) 362 err = close_single(lock, 363 work_item != NULL /* preserve_lock */, 364 lock->pool); 365 366 if (err) 367 { 368 apr_status_t apr_err = err->apr_err; 369 svn_error_clear(err); 370 return apr_err; 371 } 372 373 return APR_SUCCESS; 374} 375 376 377/* Cleanup for a readonly access baton. */ 378static apr_status_t 379pool_cleanup_readonly(void *data) 380{ 381 svn_wc_adm_access_t *lock = data; 382 svn_error_t *err; 383 384 if (lock->closed) 385 return APR_SUCCESS; 386 387 /* If the DB is closed, then we have nothing to do. There are no 388 "physical" locks to remove, and we don't care whether this baton 389 is registered with the DB. */ 390 if (svn_wc__db_is_closed(lock->db)) 391 return APR_SUCCESS; 392 393 /* Close this baton. No lock to preserve. Since this is part of the 394 pool cleanup, we don't need to close children -- the cleanup process 395 will close all children. */ 396 err = close_single(lock, FALSE /* preserve_lock */, lock->pool); 397 if (err) 398 { 399 apr_status_t result = err->apr_err; 400 svn_error_clear(err); 401 return result; 402 } 403 404 return APR_SUCCESS; 405} 406 407 408/* An APR pool cleanup handler. This is a child handler, it removes the 409 main pool handler. */ 410static apr_status_t 411pool_cleanup_child(void *p) 412{ 413 svn_wc_adm_access_t *lock = p; 414 415 apr_pool_cleanup_kill(lock->pool, lock, pool_cleanup_locked); 416 apr_pool_cleanup_kill(lock->pool, lock, pool_cleanup_readonly); 417 418 return APR_SUCCESS; 419} 420 421 422/* Allocate from POOL, initialise and return an access baton. TYPE and PATH 423 are used to initialise the baton. If STEAL_LOCK, steal the lock if path 424 is already locked */ 425static svn_error_t * 426adm_access_alloc(svn_wc_adm_access_t **adm_access, 427 const char *path, 428 svn_wc__db_t *db, 429 svn_boolean_t db_provided, 430 svn_boolean_t write_lock, 431 apr_pool_t *result_pool, 432 apr_pool_t *scratch_pool) 433{ 434 svn_error_t *err; 435 svn_wc_adm_access_t *lock = apr_palloc(result_pool, sizeof(*lock)); 436 437 lock->closed = FALSE; 438 lock->entries_all = NULL; 439 lock->db = db; 440 lock->db_provided = db_provided; 441 lock->path = apr_pstrdup(result_pool, path); 442 lock->pool = result_pool; 443 444 SVN_ERR(svn_dirent_get_absolute(&lock->abspath, path, result_pool)); 445 446 *adm_access = lock; 447 448 if (write_lock) 449 { 450 svn_boolean_t owns_lock; 451 452 /* If the db already owns a lock, we can't add an extra lock record */ 453 SVN_ERR(svn_wc__db_wclock_owns_lock(&owns_lock, db, path, FALSE, 454 scratch_pool)); 455 456 /* If DB owns the lock, but when there is no access baton open for this 457 directory, old access baton based code is trying to access data that 458 was previously locked by new code. Just hand them the lock, or 459 important code paths like svn_wc_add3() will start failing */ 460 if (!owns_lock 461 || svn_wc__adm_retrieve_internal2(db, lock->abspath, scratch_pool)) 462 { 463 SVN_ERR(svn_wc__db_wclock_obtain(db, lock->abspath, 0, FALSE, 464 scratch_pool)); 465 } 466 } 467 468 err = add_to_shared(lock, scratch_pool); 469 470 if (err) 471 return svn_error_compose_create( 472 err, 473 svn_wc__db_wclock_release(db, lock->abspath, scratch_pool)); 474 475 /* ### does this utf8 thing really/still apply?? */ 476 /* It's important that the cleanup handler is registered *after* at least 477 one UTF8 conversion has been done, since such a conversion may create 478 the apr_xlate_t object in the pool, and that object must be around 479 when the cleanup handler runs. If the apr_xlate_t cleanup handler 480 were to run *before* the access baton cleanup handler, then the access 481 baton's handler won't work. */ 482 483 /* Register an appropriate cleanup handler, based on the whether this 484 access baton is locked or not. */ 485 apr_pool_cleanup_register(lock->pool, lock, 486 write_lock 487 ? pool_cleanup_locked 488 : pool_cleanup_readonly, 489 pool_cleanup_child); 490 491 return SVN_NO_ERROR; 492} 493 494 495/* */ 496static svn_error_t * 497probe(svn_wc__db_t *db, 498 const char **dir, 499 const char *path, 500 apr_pool_t *pool) 501{ 502 svn_node_kind_t kind; 503 int wc_format = 0; 504 505 SVN_ERR(svn_io_check_path(path, &kind, pool)); 506 if (kind == svn_node_dir) 507 { 508 const char *local_abspath; 509 510 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool)); 511 SVN_ERR(svn_wc__internal_check_wc(&wc_format, db, local_abspath, 512 FALSE, pool)); 513 } 514 515 /* a "version" of 0 means a non-wc directory */ 516 if (kind != svn_node_dir || wc_format == 0) 517 { 518 /* Passing a path ending in "." or ".." to svn_dirent_dirname() is 519 probably always a bad idea; certainly it is in this case. 520 Unfortunately, svn_dirent_dirname()'s current signature can't 521 return an error, so we have to insert the protection in this 522 caller, ideally the API needs a change. See issue #1617. */ 523 const char *base_name = svn_dirent_basename(path, pool); 524 if ((strcmp(base_name, "..") == 0) 525 || (strcmp(base_name, ".") == 0)) 526 { 527 return svn_error_createf 528 (SVN_ERR_WC_BAD_PATH, NULL, 529 _("Path '%s' ends in '%s', " 530 "which is unsupported for this operation"), 531 svn_dirent_local_style(path, pool), base_name); 532 } 533 534 *dir = svn_dirent_dirname(path, pool); 535 } 536 else 537 *dir = path; 538 539 return SVN_NO_ERROR; 540} 541 542 543/* */ 544static svn_error_t * 545open_single(svn_wc_adm_access_t **adm_access, 546 const char *path, 547 svn_boolean_t write_lock, 548 svn_wc__db_t *db, 549 svn_boolean_t db_provided, 550 apr_pool_t *result_pool, 551 apr_pool_t *scratch_pool) 552{ 553 const char *local_abspath; 554 int wc_format = 0; 555 svn_error_t *err; 556 svn_wc_adm_access_t *lock; 557 558 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool)); 559 err = svn_wc__internal_check_wc(&wc_format, db, local_abspath, FALSE, 560 scratch_pool); 561 if (wc_format == 0 || (err && APR_STATUS_IS_ENOENT(err->apr_err))) 562 { 563 return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, err, 564 _("'%s' is not a working copy"), 565 svn_dirent_local_style(path, scratch_pool)); 566 } 567 SVN_ERR(err); 568 569 /* The format version must match exactly. Note that wc_db will perform 570 an auto-upgrade if allowed. If it does *not*, then it has decided a 571 manual upgrade is required and it should have raised an error. */ 572 SVN_ERR_ASSERT(wc_format == SVN_WC__VERSION); 573 574 /* Need to create a new lock */ 575 SVN_ERR(adm_access_alloc(&lock, path, db, db_provided, write_lock, 576 result_pool, scratch_pool)); 577 578 /* ### recurse was here */ 579 *adm_access = lock; 580 581 return SVN_NO_ERROR; 582} 583 584 585/* Retrieves the KIND of LOCAL_ABSPATH and whether its administrative data is 586 available in the working copy. 587 588 *AVAILABLE is set to TRUE when the node and its metadata are available, 589 otherwise to FALSE (due to obstruction, missing, absence, exclusion, 590 or a "not-present" child). 591 592 KIND can be NULL. 593 594 ### note: this function should go away when we move to a single 595 ### adminstrative area. */ 596static svn_error_t * 597adm_available(svn_boolean_t *available, 598 svn_node_kind_t *kind, 599 svn_wc__db_t *db, 600 const char *local_abspath, 601 apr_pool_t *scratch_pool) 602{ 603 svn_wc__db_status_t status; 604 605 if (kind) 606 *kind = svn_node_unknown; 607 608 SVN_ERR(svn_wc__db_read_info(&status, kind, NULL, NULL, NULL, NULL, NULL, 609 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 610 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 611 NULL, NULL, NULL, NULL, NULL, NULL, 612 db, local_abspath, scratch_pool, scratch_pool)); 613 614 *available = !(status == svn_wc__db_status_server_excluded 615 || status == svn_wc__db_status_excluded 616 || status == svn_wc__db_status_not_present); 617 618 return SVN_NO_ERROR; 619} 620/* This is essentially the guts of svn_wc_adm_open3. 621 * 622 * If the working copy is already locked, return SVN_ERR_WC_LOCKED; if 623 * it is not a versioned directory, return SVN_ERR_WC_NOT_WORKING_COPY. 624 */ 625static svn_error_t * 626do_open(svn_wc_adm_access_t **adm_access, 627 const char *path, 628 svn_wc__db_t *db, 629 svn_boolean_t db_provided, 630 apr_array_header_t *rollback, 631 svn_boolean_t write_lock, 632 int levels_to_lock, 633 svn_cancel_func_t cancel_func, 634 void *cancel_baton, 635 apr_pool_t *result_pool, 636 apr_pool_t *scratch_pool) 637{ 638 svn_wc_adm_access_t *lock; 639 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 640 641 SVN_ERR(open_single(&lock, path, write_lock, db, db_provided, 642 result_pool, iterpool)); 643 644 /* Add self to the rollback list in case of error. */ 645 APR_ARRAY_PUSH(rollback, svn_wc_adm_access_t *) = lock; 646 647 if (levels_to_lock != 0) 648 { 649 const apr_array_header_t *children; 650 const char *local_abspath = svn_wc__adm_access_abspath(lock); 651 int i; 652 653 /* Reduce levels_to_lock since we are about to recurse */ 654 if (levels_to_lock > 0) 655 levels_to_lock--; 656 657 SVN_ERR(svn_wc__db_read_children(&children, db, local_abspath, 658 scratch_pool, iterpool)); 659 660 /* Open the tree */ 661 for (i = 0; i < children->nelts; i++) 662 { 663 const char *node_abspath; 664 svn_node_kind_t kind; 665 svn_boolean_t available; 666 const char *name = APR_ARRAY_IDX(children, i, const char *); 667 668 svn_pool_clear(iterpool); 669 670 /* See if someone wants to cancel this operation. */ 671 if (cancel_func) 672 SVN_ERR(cancel_func(cancel_baton)); 673 674 node_abspath = svn_dirent_join(local_abspath, name, iterpool); 675 676 SVN_ERR(adm_available(&available, 677 &kind, 678 db, 679 node_abspath, 680 scratch_pool)); 681 682 if (kind != svn_node_dir) 683 continue; 684 685 if (available) 686 { 687 const char *node_path = svn_dirent_join(path, name, iterpool); 688 svn_wc_adm_access_t *node_access; 689 690 SVN_ERR(do_open(&node_access, node_path, db, db_provided, 691 rollback, write_lock, levels_to_lock, 692 cancel_func, cancel_baton, 693 lock->pool, iterpool)); 694 /* node_access has been registered in DB, so we don't need 695 to do anything with it. */ 696 } 697 } 698 } 699 svn_pool_destroy(iterpool); 700 701 *adm_access = lock; 702 703 return SVN_NO_ERROR; 704} 705 706 707/* */ 708static svn_error_t * 709open_all(svn_wc_adm_access_t **adm_access, 710 const char *path, 711 svn_wc__db_t *db, 712 svn_boolean_t db_provided, 713 svn_boolean_t write_lock, 714 int levels_to_lock, 715 svn_cancel_func_t cancel_func, 716 void *cancel_baton, 717 apr_pool_t *pool) 718{ 719 apr_array_header_t *rollback; 720 svn_error_t *err; 721 722 rollback = apr_array_make(pool, 10, sizeof(svn_wc_adm_access_t *)); 723 724 err = do_open(adm_access, path, db, db_provided, rollback, 725 write_lock, levels_to_lock, 726 cancel_func, cancel_baton, pool, pool); 727 if (err) 728 { 729 int i; 730 731 for (i = rollback->nelts; i--; ) 732 { 733 svn_wc_adm_access_t *lock = APR_ARRAY_IDX(rollback, i, 734 svn_wc_adm_access_t *); 735 SVN_ERR_ASSERT(!IS_MISSING(lock)); 736 737 svn_error_clear(close_single(lock, FALSE /* preserve_lock */, pool)); 738 } 739 } 740 741 return svn_error_trace(err); 742} 743 744 745svn_error_t * 746svn_wc_adm_open3(svn_wc_adm_access_t **adm_access, 747 svn_wc_adm_access_t *associated, 748 const char *path, 749 svn_boolean_t write_lock, 750 int levels_to_lock, 751 svn_cancel_func_t cancel_func, 752 void *cancel_baton, 753 apr_pool_t *pool) 754{ 755 svn_wc__db_t *db; 756 svn_boolean_t db_provided; 757 758 /* Make sure that ASSOCIATED has a set of access batons, so that we can 759 glom a reference to self into it. */ 760 if (associated) 761 { 762 const char *abspath; 763 svn_wc_adm_access_t *lock; 764 765 SVN_ERR(svn_dirent_get_absolute(&abspath, path, pool)); 766 lock = get_from_shared(abspath, associated->db, pool); 767 if (lock && !IS_MISSING(lock)) 768 /* Already locked. The reason we don't return the existing baton 769 here is that the user is supposed to know whether a directory is 770 locked: if it's not locked call svn_wc_adm_open, if it is locked 771 call svn_wc_adm_retrieve. */ 772 return svn_error_createf(SVN_ERR_WC_LOCKED, NULL, 773 _("Working copy '%s' locked"), 774 svn_dirent_local_style(path, pool)); 775 db = associated->db; 776 db_provided = associated->db_provided; 777 } 778 else 779 { 780 /* Any baton creation is going to need a shared structure for holding 781 data across the entire set. The caller isn't providing one, so we 782 do it here. */ 783 /* ### we could optimize around levels_to_lock==0, but much of this 784 ### is going to be simplified soon anyways. */ 785 SVN_ERR(svn_wc__db_open(&db, NULL /* ### config. need! */, FALSE, TRUE, 786 pool, pool)); 787 db_provided = FALSE; 788 } 789 790 return svn_error_trace(open_all(adm_access, path, db, db_provided, 791 write_lock, levels_to_lock, 792 cancel_func, cancel_baton, pool)); 793} 794 795 796svn_error_t * 797svn_wc_adm_probe_open3(svn_wc_adm_access_t **adm_access, 798 svn_wc_adm_access_t *associated, 799 const char *path, 800 svn_boolean_t write_lock, 801 int levels_to_lock, 802 svn_cancel_func_t cancel_func, 803 void *cancel_baton, 804 apr_pool_t *pool) 805{ 806 svn_error_t *err; 807 const char *dir; 808 809 if (associated == NULL) 810 { 811 svn_wc__db_t *db; 812 813 /* Ugh. Too bad about having to open a DB. */ 814 SVN_ERR(svn_wc__db_open(&db, 815 NULL /* ### config */, FALSE, TRUE, pool, pool)); 816 err = probe(db, &dir, path, pool); 817 svn_error_clear(svn_wc__db_close(db)); 818 SVN_ERR(err); 819 } 820 else 821 { 822 SVN_ERR(probe(associated->db, &dir, path, pool)); 823 } 824 825 /* If we moved up a directory, then the path is not a directory, or it 826 is not under version control. In either case, the notion of 827 levels_to_lock does not apply to the provided path. Disable it so 828 that we don't end up trying to lock more than we need. */ 829 if (dir != path) 830 levels_to_lock = 0; 831 832 err = svn_wc_adm_open3(adm_access, associated, dir, write_lock, 833 levels_to_lock, cancel_func, cancel_baton, pool); 834 if (err) 835 { 836 svn_error_t *err2; 837 838 /* If we got an error on the parent dir, that means we failed to 839 get an access baton for the child in the first place. And if 840 the reason we couldn't get the child access baton is that the 841 child is not a versioned directory, then return an error 842 about the child, not the parent. */ 843 svn_node_kind_t child_kind; 844 if ((err2 = svn_io_check_path(path, &child_kind, pool))) 845 { 846 svn_error_compose(err, err2); 847 return err; 848 } 849 850 if ((dir != path) 851 && (child_kind == svn_node_dir) 852 && (err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY)) 853 { 854 svn_error_clear(err); 855 return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL, 856 _("'%s' is not a working copy"), 857 svn_dirent_local_style(path, pool)); 858 } 859 860 return err; 861 } 862 863 return SVN_NO_ERROR; 864} 865 866 867svn_wc_adm_access_t * 868svn_wc__adm_retrieve_internal2(svn_wc__db_t *db, 869 const char *abspath, 870 apr_pool_t *scratch_pool) 871{ 872 svn_wc_adm_access_t *adm_access = get_from_shared(abspath, db, scratch_pool); 873 874 /* If the entry is marked as "missing", then return nothing. */ 875 if (IS_MISSING(adm_access)) 876 adm_access = NULL; 877 878 return adm_access; 879} 880 881 882/* SVN_DEPRECATED */ 883svn_error_t * 884svn_wc_adm_retrieve(svn_wc_adm_access_t **adm_access, 885 svn_wc_adm_access_t *associated, 886 const char *path, 887 apr_pool_t *pool) 888{ 889 const char *local_abspath; 890 svn_node_kind_t kind = svn_node_unknown; 891 svn_node_kind_t wckind; 892 svn_error_t *err; 893 894 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool)); 895 896 if (strcmp(associated->path, path) == 0) 897 *adm_access = associated; 898 else 899 *adm_access = svn_wc__adm_retrieve_internal2(associated->db, local_abspath, 900 pool); 901 902 /* We found what we're looking for, so bail. */ 903 if (*adm_access) 904 return SVN_NO_ERROR; 905 906 /* Most of the code expects access batons to exist, so returning an error 907 generally makes the calling code simpler as it doesn't need to check 908 for NULL batons. */ 909 /* We are going to send a SVN_ERR_WC_NOT_LOCKED, but let's provide 910 a bit more information to our caller */ 911 912 err = svn_io_check_path(path, &wckind, pool); 913 914 /* If we can't check the path, we can't make a good error message. */ 915 if (err) 916 { 917 return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, err, 918 _("Unable to check path existence for '%s'"), 919 svn_dirent_local_style(path, pool)); 920 } 921 922 if (associated) 923 { 924 err = svn_wc__db_read_kind(&kind, svn_wc__adm_get_db(associated), 925 local_abspath, 926 TRUE /* allow_missing */, 927 TRUE /* show_deleted */, 928 FALSE /* show_hidden */, pool); 929 930 if (err) 931 { 932 kind = svn_node_unknown; 933 svn_error_clear(err); 934 } 935 } 936 937 if (kind == svn_node_dir && wckind == svn_node_file) 938 { 939 err = svn_error_createf( 940 SVN_ERR_WC_NOT_WORKING_COPY, NULL, 941 _("Expected '%s' to be a directory but found a file"), 942 svn_dirent_local_style(path, pool)); 943 944 return svn_error_create(SVN_ERR_WC_NOT_LOCKED, err, err->message); 945 } 946 947 if (kind != svn_node_dir && kind != svn_node_unknown) 948 { 949 err = svn_error_createf( 950 SVN_ERR_WC_NOT_WORKING_COPY, NULL, 951 _("Can't retrieve an access baton for non-directory '%s'"), 952 svn_dirent_local_style(path, pool)); 953 954 return svn_error_create(SVN_ERR_WC_NOT_LOCKED, err, err->message); 955 } 956 957 if (kind == svn_node_unknown || wckind == svn_node_none) 958 { 959 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, 960 _("Directory '%s' is missing"), 961 svn_dirent_local_style(path, pool)); 962 963 return svn_error_create(SVN_ERR_WC_NOT_LOCKED, err, err->message); 964 } 965 966 /* If all else fails, return our useless generic error. */ 967 return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL, 968 _("Working copy '%s' is not locked"), 969 svn_dirent_local_style(path, pool)); 970} 971 972 973/* SVN_DEPRECATED */ 974svn_error_t * 975svn_wc_adm_probe_retrieve(svn_wc_adm_access_t **adm_access, 976 svn_wc_adm_access_t *associated, 977 const char *path, 978 apr_pool_t *pool) 979{ 980 const char *dir; 981 const char *local_abspath; 982 svn_node_kind_t kind; 983 svn_error_t *err; 984 985 SVN_ERR_ASSERT(associated != NULL); 986 987 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool)); 988 SVN_ERR(svn_wc__db_read_kind(&kind, associated->db, local_abspath, 989 TRUE /* allow_missing */, 990 TRUE /* show_deleted */, 991 FALSE /* show_hidden*/, 992 pool)); 993 994 if (kind == svn_node_dir) 995 dir = path; 996 else if (kind != svn_node_unknown) 997 dir = svn_dirent_dirname(path, pool); 998 else 999 /* Not a versioned item, probe it */ 1000 SVN_ERR(probe(associated->db, &dir, path, pool)); 1001 1002 err = svn_wc_adm_retrieve(adm_access, associated, dir, pool); 1003 if (err && err->apr_err == SVN_ERR_WC_NOT_LOCKED) 1004 { 1005 /* We'll receive a NOT LOCKED error for various reasons, 1006 including the reason we'll actually want to test for: 1007 The path is a versioned directory, but missing, in which case 1008 we want its parent's adm_access (which holds minimal data 1009 on the child) */ 1010 svn_error_clear(err); 1011 SVN_ERR(probe(associated->db, &dir, path, pool)); 1012 SVN_ERR(svn_wc_adm_retrieve(adm_access, associated, dir, pool)); 1013 } 1014 else 1015 return svn_error_trace(err); 1016 1017 return SVN_NO_ERROR; 1018} 1019 1020 1021/* SVN_DEPRECATED */ 1022svn_error_t * 1023svn_wc_adm_probe_try3(svn_wc_adm_access_t **adm_access, 1024 svn_wc_adm_access_t *associated, 1025 const char *path, 1026 svn_boolean_t write_lock, 1027 int levels_to_lock, 1028 svn_cancel_func_t cancel_func, 1029 void *cancel_baton, 1030 apr_pool_t *pool) 1031{ 1032 svn_error_t *err; 1033 1034 err = svn_wc_adm_probe_retrieve(adm_access, associated, path, pool); 1035 1036 /* SVN_ERR_WC_NOT_LOCKED would mean there was no access baton for 1037 path in associated, in which case we want to open an access 1038 baton and add it to associated. */ 1039 if (err && (err->apr_err == SVN_ERR_WC_NOT_LOCKED)) 1040 { 1041 svn_error_clear(err); 1042 err = svn_wc_adm_probe_open3(adm_access, associated, 1043 path, write_lock, levels_to_lock, 1044 cancel_func, cancel_baton, 1045 svn_wc_adm_access_pool(associated)); 1046 1047 /* If the path is not a versioned directory, we just return a 1048 null access baton with no error. Note that of the errors we 1049 do report, the most important (and probably most likely) is 1050 SVN_ERR_WC_LOCKED. That error would mean that someone else 1051 has this area locked, and we definitely want to bail in that 1052 case. */ 1053 if (err && (err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY)) 1054 { 1055 svn_error_clear(err); 1056 *adm_access = NULL; 1057 err = NULL; 1058 } 1059 } 1060 1061 return err; 1062} 1063 1064 1065/* */ 1066static svn_error_t * 1067child_is_disjoint(svn_boolean_t *disjoint, 1068 svn_wc__db_t *db, 1069 const char *local_abspath, 1070 apr_pool_t *scratch_pool) 1071{ 1072 svn_boolean_t is_switched; 1073 1074 /* Check if the parent directory knows about this node */ 1075 SVN_ERR(svn_wc__db_is_switched(disjoint, &is_switched, NULL, 1076 db, local_abspath, scratch_pool)); 1077 1078 if (*disjoint) 1079 return SVN_NO_ERROR; 1080 1081 if (is_switched) 1082 *disjoint = TRUE; 1083 1084 return SVN_NO_ERROR; 1085} 1086 1087/* */ 1088static svn_error_t * 1089open_anchor(svn_wc_adm_access_t **anchor_access, 1090 svn_wc_adm_access_t **target_access, 1091 const char **target, 1092 svn_wc__db_t *db, 1093 svn_boolean_t db_provided, 1094 const char *path, 1095 svn_boolean_t write_lock, 1096 int levels_to_lock, 1097 svn_cancel_func_t cancel_func, 1098 void *cancel_baton, 1099 apr_pool_t *pool) 1100{ 1101 const char *base_name = svn_dirent_basename(path, pool); 1102 1103 /* Any baton creation is going to need a shared structure for holding 1104 data across the entire set. The caller isn't providing one, so we 1105 do it here. */ 1106 /* ### we could maybe skip the shared struct for levels_to_lock==0, but 1107 ### given that we need DB for format detection, may as well keep this. 1108 ### in any case, much of this is going to be simplified soon anyways. */ 1109 if (!db_provided) 1110 SVN_ERR(svn_wc__db_open(&db, NULL, /* ### config. need! */ FALSE, TRUE, 1111 pool, pool)); 1112 1113 if (svn_path_is_empty(path) 1114 || svn_dirent_is_root(path, strlen(path)) 1115 || ! strcmp(base_name, "..")) 1116 { 1117 SVN_ERR(open_all(anchor_access, path, db, db_provided, 1118 write_lock, levels_to_lock, 1119 cancel_func, cancel_baton, pool)); 1120 *target_access = *anchor_access; 1121 *target = ""; 1122 } 1123 else 1124 { 1125 svn_error_t *err; 1126 svn_wc_adm_access_t *p_access = NULL; 1127 svn_wc_adm_access_t *t_access = NULL; 1128 const char *parent = svn_dirent_dirname(path, pool); 1129 const char *local_abspath; 1130 svn_error_t *p_access_err = SVN_NO_ERROR; 1131 1132 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool)); 1133 1134 /* Try to open parent of PATH to setup P_ACCESS */ 1135 err = open_single(&p_access, parent, write_lock, db, db_provided, 1136 pool, pool); 1137 if (err) 1138 { 1139 const char *abspath = svn_dirent_dirname(local_abspath, pool); 1140 svn_wc_adm_access_t *existing_adm = svn_wc__db_temp_get_access(db, abspath, pool); 1141 1142 if (IS_MISSING(existing_adm)) 1143 svn_wc__db_temp_clear_access(db, abspath, pool); 1144 else 1145 SVN_ERR_ASSERT(existing_adm == NULL); 1146 1147 if (err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY) 1148 { 1149 svn_error_clear(err); 1150 p_access = NULL; 1151 } 1152 else if (write_lock && (err->apr_err == SVN_ERR_WC_LOCKED 1153 || APR_STATUS_IS_EACCES(err->apr_err))) 1154 { 1155 /* If P_ACCESS isn't to be returned then a read-only baton 1156 will do for now, but keep the error in case we need it. */ 1157 svn_error_t *err2 = open_single(&p_access, parent, FALSE, 1158 db, db_provided, pool, pool); 1159 if (err2) 1160 { 1161 svn_error_clear(err2); 1162 return err; 1163 } 1164 p_access_err = err; 1165 } 1166 else 1167 return err; 1168 } 1169 1170 /* Try to open PATH to setup T_ACCESS */ 1171 err = open_all(&t_access, path, db, db_provided, write_lock, 1172 levels_to_lock, cancel_func, cancel_baton, pool); 1173 if (err) 1174 { 1175 if (p_access == NULL) 1176 { 1177 /* Couldn't open the parent or the target. Bail out. */ 1178 svn_error_clear(p_access_err); 1179 return svn_error_trace(err); 1180 } 1181 1182 if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY) 1183 { 1184 if (p_access) 1185 svn_error_clear(svn_wc_adm_close2(p_access, pool)); 1186 svn_error_clear(p_access_err); 1187 return svn_error_trace(err); 1188 } 1189 1190 /* This directory is not under version control. Ignore it. */ 1191 svn_error_clear(err); 1192 t_access = NULL; 1193 } 1194 1195 /* At this stage might have P_ACCESS, T_ACCESS or both */ 1196 1197 /* Check for switched or disjoint P_ACCESS and T_ACCESS */ 1198 if (p_access && t_access) 1199 { 1200 svn_boolean_t disjoint; 1201 1202 err = child_is_disjoint(&disjoint, db, local_abspath, pool); 1203 if (err) 1204 { 1205 svn_error_clear(p_access_err); 1206 svn_error_clear(svn_wc_adm_close2(p_access, pool)); 1207 svn_error_clear(svn_wc_adm_close2(t_access, pool)); 1208 return svn_error_trace(err); 1209 } 1210 1211 if (disjoint) 1212 { 1213 /* Switched or disjoint, so drop P_ACCESS. Don't close any 1214 descendents, or we might blast the child. */ 1215 err = close_single(p_access, FALSE /* preserve_lock */, pool); 1216 if (err) 1217 { 1218 svn_error_clear(p_access_err); 1219 svn_error_clear(svn_wc_adm_close2(t_access, pool)); 1220 return svn_error_trace(err); 1221 } 1222 p_access = NULL; 1223 } 1224 } 1225 1226 /* We have a parent baton *and* we have an error related to opening 1227 the baton. That means we have a readonly baton, but that isn't 1228 going to work for us. (p_access would have been set to NULL if 1229 a writable parent baton is not required) */ 1230 if (p_access && p_access_err) 1231 { 1232 if (t_access) 1233 svn_error_clear(svn_wc_adm_close2(t_access, pool)); 1234 svn_error_clear(svn_wc_adm_close2(p_access, pool)); 1235 return svn_error_trace(p_access_err); 1236 } 1237 svn_error_clear(p_access_err); 1238 1239 if (! t_access) 1240 { 1241 svn_boolean_t available; 1242 svn_node_kind_t kind; 1243 1244 err = adm_available(&available, &kind, db, local_abspath, pool); 1245 1246 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) 1247 svn_error_clear(err); 1248 else if (err) 1249 { 1250 svn_error_clear(svn_wc_adm_close2(p_access, pool)); 1251 return svn_error_trace(err); 1252 } 1253 } 1254 1255 *anchor_access = p_access ? p_access : t_access; 1256 *target_access = t_access ? t_access : p_access; 1257 1258 if (! p_access) 1259 *target = ""; 1260 else 1261 *target = base_name; 1262 } 1263 1264 return SVN_NO_ERROR; 1265} 1266 1267 1268svn_error_t * 1269svn_wc_adm_open_anchor(svn_wc_adm_access_t **anchor_access, 1270 svn_wc_adm_access_t **target_access, 1271 const char **target, 1272 const char *path, 1273 svn_boolean_t write_lock, 1274 int levels_to_lock, 1275 svn_cancel_func_t cancel_func, 1276 void *cancel_baton, 1277 apr_pool_t *pool) 1278{ 1279 return svn_error_trace(open_anchor(anchor_access, target_access, target, 1280 NULL, FALSE, path, write_lock, 1281 levels_to_lock, cancel_func, 1282 cancel_baton, pool)); 1283} 1284 1285 1286/* Does the work of closing the access baton ADM_ACCESS. Any physical 1287 locks are removed from the working copy if PRESERVE_LOCK is FALSE, or 1288 are left if PRESERVE_LOCK is TRUE. Any associated access batons that 1289 are direct descendants will also be closed. 1290 */ 1291static svn_error_t * 1292do_close(svn_wc_adm_access_t *adm_access, 1293 svn_boolean_t preserve_lock, 1294 apr_pool_t *scratch_pool) 1295{ 1296 svn_wc_adm_access_t *look; 1297 1298 if (adm_access->closed) 1299 return SVN_NO_ERROR; 1300 1301 /* If we are part of the shared set, then close descendant batons. */ 1302 look = get_from_shared(adm_access->abspath, adm_access->db, scratch_pool); 1303 if (look != NULL) 1304 { 1305 apr_hash_t *opened; 1306 apr_hash_index_t *hi; 1307 1308 /* Gather all the opened access batons from the DB. */ 1309 opened = svn_wc__db_temp_get_all_access(adm_access->db, scratch_pool); 1310 1311 /* Close any that are descendents of this baton. */ 1312 for (hi = apr_hash_first(scratch_pool, opened); 1313 hi; 1314 hi = apr_hash_next(hi)) 1315 { 1316 const char *abspath = svn__apr_hash_index_key(hi); 1317 svn_wc_adm_access_t *child = svn__apr_hash_index_val(hi); 1318 const char *path = child->path; 1319 1320 if (IS_MISSING(child)) 1321 { 1322 /* We don't close the missing entry, but get rid of it from 1323 the set. */ 1324 svn_wc__db_temp_clear_access(adm_access->db, abspath, 1325 scratch_pool); 1326 continue; 1327 } 1328 1329 if (! svn_dirent_is_ancestor(adm_access->path, path) 1330 || strcmp(adm_access->path, path) == 0) 1331 continue; 1332 1333 SVN_ERR(close_single(child, preserve_lock, scratch_pool)); 1334 } 1335 } 1336 1337 return svn_error_trace(close_single(adm_access, preserve_lock, 1338 scratch_pool)); 1339} 1340 1341 1342/* SVN_DEPRECATED */ 1343svn_error_t * 1344svn_wc_adm_close2(svn_wc_adm_access_t *adm_access, apr_pool_t *scratch_pool) 1345{ 1346 return svn_error_trace(do_close(adm_access, FALSE, scratch_pool)); 1347} 1348 1349 1350/* SVN_DEPRECATED */ 1351svn_boolean_t 1352svn_wc_adm_locked(const svn_wc_adm_access_t *adm_access) 1353{ 1354 svn_boolean_t locked; 1355 apr_pool_t *subpool = svn_pool_create(adm_access->pool); 1356 svn_error_t *err = svn_wc__db_wclock_owns_lock(&locked, adm_access->db, 1357 adm_access->abspath, TRUE, 1358 subpool); 1359 svn_pool_destroy(subpool); 1360 1361 if (err) 1362 { 1363 svn_error_clear(err); 1364 /* ### is this right? */ 1365 return FALSE; 1366 } 1367 1368 return locked; 1369} 1370 1371svn_error_t * 1372svn_wc__write_check(svn_wc__db_t *db, 1373 const char *local_abspath, 1374 apr_pool_t *scratch_pool) 1375{ 1376 svn_boolean_t locked; 1377 1378 SVN_ERR(svn_wc__db_wclock_owns_lock(&locked, db, local_abspath, FALSE, 1379 scratch_pool)); 1380 if (!locked) 1381 return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL, 1382 _("No write-lock in '%s'"), 1383 svn_dirent_local_style(local_abspath, 1384 scratch_pool)); 1385 1386 return SVN_NO_ERROR; 1387} 1388 1389svn_error_t * 1390svn_wc_locked2(svn_boolean_t *locked_here, 1391 svn_boolean_t *locked, 1392 svn_wc_context_t *wc_ctx, 1393 const char *local_abspath, 1394 apr_pool_t *scratch_pool) 1395{ 1396 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 1397 1398 if (locked_here != NULL) 1399 SVN_ERR(svn_wc__db_wclock_owns_lock(locked_here, wc_ctx->db, local_abspath, 1400 FALSE, scratch_pool)); 1401 if (locked != NULL) 1402 SVN_ERR(svn_wc__db_wclocked(locked, wc_ctx->db, local_abspath, 1403 scratch_pool)); 1404 1405 return SVN_NO_ERROR; 1406} 1407 1408 1409/* SVN_DEPRECATED */ 1410const char * 1411svn_wc_adm_access_path(const svn_wc_adm_access_t *adm_access) 1412{ 1413 return adm_access->path; 1414} 1415 1416 1417const char * 1418svn_wc__adm_access_abspath(const svn_wc_adm_access_t *adm_access) 1419{ 1420 return adm_access->abspath; 1421} 1422 1423 1424/* SVN_DEPRECATED */ 1425apr_pool_t * 1426svn_wc_adm_access_pool(const svn_wc_adm_access_t *adm_access) 1427{ 1428 return adm_access->pool; 1429} 1430 1431apr_pool_t * 1432svn_wc__adm_access_pool_internal(const svn_wc_adm_access_t *adm_access) 1433{ 1434 return adm_access->pool; 1435} 1436 1437void 1438svn_wc__adm_access_set_entries(svn_wc_adm_access_t *adm_access, 1439 apr_hash_t *entries) 1440{ 1441 adm_access->entries_all = entries; 1442} 1443 1444 1445apr_hash_t * 1446svn_wc__adm_access_entries(svn_wc_adm_access_t *adm_access) 1447{ 1448 /* Compile with -DSVN_DISABLE_ENTRY_CACHE to disable the in-memory 1449 entry caching. As of 2010-03-18 (r924708) merge_tests 34 and 134 1450 fail during "make check". */ 1451#ifdef SVN_DISABLE_ENTRY_CACHE 1452 return NULL; 1453#else 1454 return adm_access->entries_all; 1455#endif 1456} 1457 1458 1459svn_wc__db_t * 1460svn_wc__adm_get_db(const svn_wc_adm_access_t *adm_access) 1461{ 1462 return adm_access->db; 1463} 1464 1465svn_error_t * 1466svn_wc__acquire_write_lock(const char **lock_root_abspath, 1467 svn_wc_context_t *wc_ctx, 1468 const char *local_abspath, 1469 svn_boolean_t lock_anchor, 1470 apr_pool_t *result_pool, 1471 apr_pool_t *scratch_pool) 1472{ 1473 svn_wc__db_t *db = wc_ctx->db; 1474 svn_boolean_t is_wcroot; 1475 svn_boolean_t is_switched; 1476 svn_node_kind_t kind; 1477 svn_error_t *err; 1478 1479 err = svn_wc__db_is_switched(&is_wcroot, &is_switched, &kind, 1480 db, local_abspath, scratch_pool); 1481 1482 if (err) 1483 { 1484 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 1485 return svn_error_trace(err); 1486 1487 svn_error_clear(err); 1488 1489 kind = svn_node_none; 1490 is_wcroot = FALSE; 1491 is_switched = FALSE; 1492 } 1493 1494 if (!lock_root_abspath && kind != svn_node_dir) 1495 return svn_error_createf(SVN_ERR_WC_NOT_DIRECTORY, NULL, 1496 _("Can't obtain lock on non-directory '%s'."), 1497 svn_dirent_local_style(local_abspath, 1498 scratch_pool)); 1499 1500 if (lock_anchor && kind == svn_node_dir) 1501 { 1502 if (is_wcroot) 1503 lock_anchor = FALSE; 1504 } 1505 1506 if (lock_anchor) 1507 { 1508 const char *parent_abspath; 1509 SVN_ERR_ASSERT(lock_root_abspath != NULL); 1510 1511 parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool); 1512 1513 if (kind == svn_node_dir) 1514 { 1515 if (! is_switched) 1516 local_abspath = parent_abspath; 1517 } 1518 else if (kind != svn_node_none && kind != svn_node_unknown) 1519 { 1520 /* In the single-DB world we know parent exists */ 1521 local_abspath = parent_abspath; 1522 } 1523 else 1524 { 1525 /* Can't lock parents that don't exist */ 1526 svn_node_kind_t parent_kind; 1527 err = svn_wc__db_read_kind(&parent_kind, db, parent_abspath, 1528 TRUE /* allow_missing */, 1529 TRUE /* show_deleted */, 1530 FALSE /* show_hidden */, 1531 scratch_pool); 1532 if (err && SVN_WC__ERR_IS_NOT_CURRENT_WC(err)) 1533 { 1534 svn_error_clear(err); 1535 parent_kind = svn_node_unknown; 1536 } 1537 else 1538 SVN_ERR(err); 1539 1540 if (parent_kind != svn_node_dir) 1541 return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL, 1542 _("'%s' is not a working copy"), 1543 svn_dirent_local_style(local_abspath, 1544 scratch_pool)); 1545 1546 local_abspath = parent_abspath; 1547 } 1548 } 1549 else if (kind != svn_node_dir) 1550 { 1551 local_abspath = svn_dirent_dirname(local_abspath, scratch_pool); 1552 } 1553 1554 if (lock_root_abspath) 1555 *lock_root_abspath = apr_pstrdup(result_pool, local_abspath); 1556 1557 SVN_ERR(svn_wc__db_wclock_obtain(wc_ctx->db, local_abspath, 1558 -1 /* levels_to_lock (infinite) */, 1559 FALSE /* steal_lock */, 1560 scratch_pool)); 1561 1562 return SVN_NO_ERROR; 1563} 1564 1565 1566svn_error_t * 1567svn_wc__release_write_lock(svn_wc_context_t *wc_ctx, 1568 const char *local_abspath, 1569 apr_pool_t *scratch_pool) 1570{ 1571 apr_uint64_t id; 1572 svn_skel_t *work_item; 1573 1574 SVN_ERR(svn_wc__db_wq_fetch_next(&id, &work_item, wc_ctx->db, local_abspath, 1575 0, scratch_pool, scratch_pool)); 1576 if (work_item) 1577 { 1578 /* Do not release locks (here or below) if there is work to do. */ 1579 return SVN_NO_ERROR; 1580 } 1581 1582 SVN_ERR(svn_wc__db_wclock_release(wc_ctx->db, local_abspath, scratch_pool)); 1583 1584 return SVN_NO_ERROR; 1585} 1586 1587svn_error_t * 1588svn_wc__call_with_write_lock(svn_wc__with_write_lock_func_t func, 1589 void *baton, 1590 svn_wc_context_t *wc_ctx, 1591 const char *local_abspath, 1592 svn_boolean_t lock_anchor, 1593 apr_pool_t *result_pool, 1594 apr_pool_t *scratch_pool) 1595{ 1596 svn_error_t *err1, *err2; 1597 const char *lock_root_abspath; 1598 1599 SVN_ERR(svn_wc__acquire_write_lock(&lock_root_abspath, wc_ctx, local_abspath, 1600 lock_anchor, scratch_pool, scratch_pool)); 1601 err1 = svn_error_trace(func(baton, result_pool, scratch_pool)); 1602 err2 = svn_wc__release_write_lock(wc_ctx, lock_root_abspath, scratch_pool); 1603 return svn_error_compose_create(err1, err2); 1604} 1605 1606 1607svn_error_t * 1608svn_wc__acquire_write_lock_for_resolve(const char **lock_root_abspath, 1609 svn_wc_context_t *wc_ctx, 1610 const char *local_abspath, 1611 apr_pool_t *result_pool, 1612 apr_pool_t *scratch_pool) 1613{ 1614 svn_boolean_t locked = FALSE; 1615 const char *obtained_abspath; 1616 const char *requested_abspath = local_abspath; 1617 1618 while (!locked) 1619 { 1620 const char *required_abspath; 1621 const char *child; 1622 1623 SVN_ERR(svn_wc__acquire_write_lock(&obtained_abspath, wc_ctx, 1624 requested_abspath, FALSE, 1625 scratch_pool, scratch_pool)); 1626 locked = TRUE; 1627 1628 SVN_ERR(svn_wc__required_lock_for_resolve(&required_abspath, 1629 wc_ctx->db, local_abspath, 1630 scratch_pool, scratch_pool)); 1631 1632 /* It's possible for the required lock path to be an ancestor 1633 of, a descendent of, or equal to, the obtained lock path. If 1634 it's an ancestor we have to try again, otherwise the obtained 1635 lock will do. */ 1636 child = svn_dirent_skip_ancestor(required_abspath, obtained_abspath); 1637 if (child && child[0]) 1638 { 1639 SVN_ERR(svn_wc__release_write_lock(wc_ctx, obtained_abspath, 1640 scratch_pool)); 1641 locked = FALSE; 1642 requested_abspath = required_abspath; 1643 } 1644 else 1645 { 1646 /* required should be a descendent of, or equal to, obtained */ 1647 SVN_ERR_ASSERT(!strcmp(required_abspath, obtained_abspath) 1648 || svn_dirent_skip_ancestor(obtained_abspath, 1649 required_abspath)); 1650 } 1651 } 1652 1653 *lock_root_abspath = apr_pstrdup(result_pool, obtained_abspath); 1654 1655 return SVN_NO_ERROR; 1656} 1657