wc_db_wcroot.c revision 289166
1/* 2 * wc_db_wcroot.c : supporting datastructures for the administrative database 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_WC__I_AM_WC_DB 25 26#include <assert.h> 27 28#include "svn_dirent_uri.h" 29#include "svn_hash.h" 30#include "svn_path.h" 31#include "svn_version.h" 32 33#include "wc.h" 34#include "adm_files.h" 35#include "wc_db_private.h" 36#include "wc-queries.h" 37 38#include "svn_private_config.h" 39 40/* ### Same values as wc_db.c */ 41#define SDB_FILE "wc.db" 42#define UNKNOWN_WC_ID ((apr_int64_t) -1) 43#define FORMAT_FROM_SDB (-1) 44 45 46 47/* Get the format version from a wc-1 directory. If it is not a working copy 48 directory, then it sets VERSION to zero and returns no error. */ 49static svn_error_t * 50get_old_version(int *version, 51 const char *abspath, 52 apr_pool_t *scratch_pool) 53{ 54 svn_error_t *err; 55 const char *format_file_path; 56 svn_node_kind_t kind; 57 58 /* Try reading the format number from the entries file. */ 59 format_file_path = svn_wc__adm_child(abspath, SVN_WC__ADM_ENTRIES, 60 scratch_pool); 61 62 /* Since trying to open a non-existent file is quite expensive, try a 63 quick stat call first. In wc-ng w/cs, this will be an early exit. */ 64 SVN_ERR(svn_io_check_path(format_file_path, &kind, scratch_pool)); 65 if (kind == svn_node_none) 66 { 67 *version = 0; 68 return SVN_NO_ERROR; 69 } 70 71 err = svn_io_read_version_file(version, format_file_path, scratch_pool); 72 if (err == NULL) 73 return SVN_NO_ERROR; 74 if (err->apr_err != SVN_ERR_BAD_VERSION_FILE_FORMAT 75 && !APR_STATUS_IS_ENOENT(err->apr_err) 76 && !APR_STATUS_IS_ENOTDIR(err->apr_err)) 77 return svn_error_createf(SVN_ERR_WC_MISSING, err, _("'%s' does not exist"), 78 svn_dirent_local_style(abspath, scratch_pool)); 79 svn_error_clear(err); 80 81 /* This must be a really old working copy! Fall back to reading the 82 format file. 83 84 Note that the format file might not exist in newer working copies 85 (format 7 and higher), but in that case, the entries file should 86 have contained the format number. */ 87 format_file_path = svn_wc__adm_child(abspath, SVN_WC__ADM_FORMAT, 88 scratch_pool); 89 err = svn_io_read_version_file(version, format_file_path, scratch_pool); 90 if (err == NULL) 91 return SVN_NO_ERROR; 92 93 /* Whatever error may have occurred... we can just ignore. This is not 94 a working copy directory. Signal the caller. */ 95 svn_error_clear(err); 96 97 *version = 0; 98 return SVN_NO_ERROR; 99} 100 101 102/* A helper function to parse_local_abspath() which returns the on-disk KIND 103 of LOCAL_ABSPATH, using DB and SCRATCH_POOL as needed. 104 105 This function may do strange things, but at long as it comes up with the 106 Right Answer, we should be happy. */ 107static svn_error_t * 108get_path_kind(svn_node_kind_t *kind, 109 svn_wc__db_t *db, 110 const char *local_abspath, 111 apr_pool_t *scratch_pool) 112{ 113 svn_boolean_t special; 114 svn_node_kind_t node_kind; 115 116 /* This implements a *really* simple LRU cache, where "simple" is defined 117 as "only one element". In other words, we remember the most recently 118 queried path, and nothing else. This gives >80% cache hits. */ 119 120 if (db->parse_cache.abspath 121 && strcmp(db->parse_cache.abspath->data, local_abspath) == 0) 122 { 123 /* Cache hit! */ 124 *kind = db->parse_cache.kind; 125 return SVN_NO_ERROR; 126 } 127 128 if (!db->parse_cache.abspath) 129 { 130 db->parse_cache.abspath = svn_stringbuf_create(local_abspath, 131 db->state_pool); 132 } 133 else 134 { 135 svn_stringbuf_set(db->parse_cache.abspath, local_abspath); 136 } 137 138 SVN_ERR(svn_io_check_special_path(local_abspath, &node_kind, 139 &special, scratch_pool)); 140 141 db->parse_cache.kind = (special ? svn_node_symlink : node_kind); 142 *kind = db->parse_cache.kind; 143 144 return SVN_NO_ERROR; 145} 146 147 148/* Return an error if the work queue in SDB is non-empty. */ 149static svn_error_t * 150verify_no_work(svn_sqlite__db_t *sdb) 151{ 152 svn_sqlite__stmt_t *stmt; 153 svn_boolean_t have_row; 154 155 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_LOOK_FOR_WORK)); 156 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 157 SVN_ERR(svn_sqlite__reset(stmt)); 158 159 if (have_row) 160 return svn_error_create(SVN_ERR_WC_CLEANUP_REQUIRED, NULL, 161 NULL /* nothing to add. */); 162 163 return SVN_NO_ERROR; 164} 165 166 167/* */ 168static apr_status_t 169close_wcroot(void *data) 170{ 171 svn_wc__db_wcroot_t *wcroot = data; 172 svn_error_t *err; 173 174 SVN_ERR_ASSERT_NO_RETURN(wcroot->sdb != NULL); 175 176 err = svn_sqlite__close(wcroot->sdb); 177 wcroot->sdb = NULL; 178 if (err) 179 { 180 apr_status_t result = err->apr_err; 181 svn_error_clear(err); 182 return result; 183 } 184 185 return APR_SUCCESS; 186} 187 188 189svn_error_t * 190svn_wc__db_open(svn_wc__db_t **db, 191 svn_config_t *config, 192 svn_boolean_t open_without_upgrade, 193 svn_boolean_t enforce_empty_wq, 194 apr_pool_t *result_pool, 195 apr_pool_t *scratch_pool) 196{ 197 *db = apr_pcalloc(result_pool, sizeof(**db)); 198 (*db)->config = config; 199 (*db)->verify_format = !open_without_upgrade; 200 (*db)->enforce_empty_wq = enforce_empty_wq; 201 (*db)->dir_data = apr_hash_make(result_pool); 202 203 (*db)->state_pool = result_pool; 204 205 /* Don't need to initialize (*db)->parse_cache, due to the calloc above */ 206 if (config) 207 { 208 svn_error_t *err; 209 svn_boolean_t sqlite_exclusive = FALSE; 210 211 err = svn_config_get_bool(config, &sqlite_exclusive, 212 SVN_CONFIG_SECTION_WORKING_COPY, 213 SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE, 214 FALSE); 215 if (err) 216 { 217 svn_error_clear(err); 218 } 219 else 220 (*db)->exclusive = sqlite_exclusive; 221 } 222 223 return SVN_NO_ERROR; 224} 225 226 227svn_error_t * 228svn_wc__db_close(svn_wc__db_t *db) 229{ 230 apr_pool_t *scratch_pool = db->state_pool; 231 apr_hash_t *roots = apr_hash_make(scratch_pool); 232 apr_hash_index_t *hi; 233 234 /* Collect all the unique WCROOT structures, and empty out DIR_DATA. */ 235 for (hi = apr_hash_first(scratch_pool, db->dir_data); 236 hi; 237 hi = apr_hash_next(hi)) 238 { 239 svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi); 240 const char *local_abspath = svn__apr_hash_index_key(hi); 241 242 if (wcroot->sdb) 243 svn_hash_sets(roots, wcroot->abspath, wcroot); 244 245 svn_hash_sets(db->dir_data, local_abspath, NULL); 246 } 247 248 /* Run the cleanup for each WCROOT. */ 249 return svn_error_trace(svn_wc__db_close_many_wcroots(roots, db->state_pool, 250 scratch_pool)); 251} 252 253 254svn_error_t * 255svn_wc__db_pdh_create_wcroot(svn_wc__db_wcroot_t **wcroot, 256 const char *wcroot_abspath, 257 svn_sqlite__db_t *sdb, 258 apr_int64_t wc_id, 259 int format, 260 svn_boolean_t verify_format, 261 svn_boolean_t enforce_empty_wq, 262 apr_pool_t *result_pool, 263 apr_pool_t *scratch_pool) 264{ 265 if (sdb && format == FORMAT_FROM_SDB) 266 SVN_ERR(svn_sqlite__read_schema_version(&format, sdb, scratch_pool)); 267 268 /* If we construct a wcroot, then we better have a format. */ 269 SVN_ERR_ASSERT(format >= 1); 270 271 /* If this working copy is PRE-1.0, then simply bail out. */ 272 if (format < 4) 273 { 274 return svn_error_createf( 275 SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL, 276 _("Working copy format of '%s' is too old (%d); " 277 "please check out your working copy again"), 278 svn_dirent_local_style(wcroot_abspath, scratch_pool), format); 279 } 280 281 /* If this working copy is from a future version, then bail out. */ 282 if (format > SVN_WC__VERSION) 283 { 284 return svn_error_createf( 285 SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL, 286 _("This client is too old to work with the working copy at\n" 287 "'%s' (format %d).\n" 288 "You need to get a newer Subversion client. For more details, see\n" 289 " http://subversion.apache.org/faq.html#working-copy-format-change\n" 290 ), 291 svn_dirent_local_style(wcroot_abspath, scratch_pool), 292 format); 293 } 294 295 /* Verify that no work items exists. If they do, then our integrity is 296 suspect and, thus, we cannot use this database. */ 297 if (format >= SVN_WC__HAS_WORK_QUEUE 298 && (enforce_empty_wq || (format < SVN_WC__VERSION && verify_format))) 299 { 300 svn_error_t *err = verify_no_work(sdb); 301 if (err) 302 { 303 /* Special message for attempts to upgrade a 1.7-dev wc with 304 outstanding workqueue items. */ 305 if (err->apr_err == SVN_ERR_WC_CLEANUP_REQUIRED 306 && format < SVN_WC__VERSION && verify_format) 307 err = svn_error_quick_wrap(err, _("Cleanup with an older 1.7 " 308 "client before upgrading with " 309 "this client")); 310 return svn_error_trace(err); 311 } 312 } 313 314 /* Auto-upgrade the SDB if possible. */ 315 if (format < SVN_WC__VERSION && verify_format) 316 { 317 return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, NULL, 318 _("The working copy at '%s'\nis too old " 319 "(format %d) to work with client version " 320 "'%s' (expects format %d). You need to " 321 "upgrade the working copy first.\n"), 322 svn_dirent_local_style(wcroot_abspath, 323 scratch_pool), 324 format, SVN_VERSION, SVN_WC__VERSION); 325 } 326 327 *wcroot = apr_palloc(result_pool, sizeof(**wcroot)); 328 329 (*wcroot)->abspath = wcroot_abspath; 330 (*wcroot)->sdb = sdb; 331 (*wcroot)->wc_id = wc_id; 332 (*wcroot)->format = format; 333 /* 8 concurrent locks is probably more than a typical wc_ng based svn client 334 uses. */ 335 (*wcroot)->owned_locks = apr_array_make(result_pool, 8, 336 sizeof(svn_wc__db_wclock_t)); 337 (*wcroot)->access_cache = apr_hash_make(result_pool); 338 339 /* SDB will be NULL for pre-NG working copies. We only need to run a 340 cleanup when the SDB is present. */ 341 if (sdb != NULL) 342 apr_pool_cleanup_register(result_pool, *wcroot, close_wcroot, 343 apr_pool_cleanup_null); 344 return SVN_NO_ERROR; 345} 346 347 348svn_error_t * 349svn_wc__db_close_many_wcroots(apr_hash_t *roots, 350 apr_pool_t *state_pool, 351 apr_pool_t *scratch_pool) 352{ 353 apr_hash_index_t *hi; 354 355 for (hi = apr_hash_first(scratch_pool, roots); hi; hi = apr_hash_next(hi)) 356 { 357 svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi); 358 apr_status_t result; 359 360 result = apr_pool_cleanup_run(state_pool, wcroot, close_wcroot); 361 if (result != APR_SUCCESS) 362 return svn_error_wrap_apr(result, NULL); 363 } 364 365 return SVN_NO_ERROR; 366} 367 368 369/* POOL may be NULL if the lifetime of LOCAL_ABSPATH is sufficient. */ 370static const char * 371compute_relpath(const svn_wc__db_wcroot_t *wcroot, 372 const char *local_abspath, 373 apr_pool_t *result_pool) 374{ 375 const char *relpath = svn_dirent_is_child(wcroot->abspath, local_abspath, 376 result_pool); 377 if (relpath == NULL) 378 return ""; 379 return relpath; 380} 381 382 383/* Return in *LINK_TARGET_ABSPATH the absolute path the symlink at 384 * LOCAL_ABSPATH is pointing to. Perform all allocations in POOL. */ 385static svn_error_t * 386read_link_target(const char **link_target_abspath, 387 const char *local_abspath, 388 apr_pool_t *pool) 389{ 390 svn_string_t *link_target; 391 const char *canon_link_target; 392 393 SVN_ERR(svn_io_read_link(&link_target, local_abspath, pool)); 394 if (link_target->len == 0) 395 return svn_error_createf(SVN_ERR_WC_NOT_SYMLINK, NULL, 396 _("The symlink at '%s' points nowhere"), 397 svn_dirent_local_style(local_abspath, pool)); 398 399 canon_link_target = svn_dirent_canonicalize(link_target->data, pool); 400 401 /* Treat relative symlinks as relative to LOCAL_ABSPATH's parent. */ 402 if (!svn_dirent_is_absolute(canon_link_target)) 403 canon_link_target = svn_dirent_join(svn_dirent_dirname(local_abspath, 404 pool), 405 canon_link_target, pool); 406 407 /* Collapse any .. in the symlink part of the path. */ 408 if (svn_path_is_backpath_present(canon_link_target)) 409 SVN_ERR(svn_dirent_get_absolute(link_target_abspath, canon_link_target, 410 pool)); 411 else 412 *link_target_abspath = canon_link_target; 413 414 return SVN_NO_ERROR; 415} 416 417/* Verify if the sqlite_stat1 table exists and if not tries to add 418 this table (but ignores errors on adding the schema) */ 419static svn_error_t * 420verify_stats_table(svn_sqlite__db_t *sdb, 421 int format, 422 apr_pool_t *scratch_pool) 423{ 424 svn_sqlite__stmt_t *stmt; 425 svn_boolean_t have_row; 426 427 if (format != SVN_WC__ENSURE_STAT1_TABLE) 428 return SVN_NO_ERROR; 429 430 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 431 STMT_HAVE_STAT1_TABLE)); 432 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 433 SVN_ERR(svn_sqlite__reset(stmt)); 434 435 if (!have_row) 436 { 437 svn_error_clear( 438 svn_wc__db_install_schema_statistics(sdb, scratch_pool)); 439 } 440 441 return SVN_NO_ERROR; 442} 443 444/* Sqlite transaction helper for opening the db in 445 svn_wc__db_wcroot_parse_local_abspath() to avoid multiple 446 db operations that each obtain and release a lock */ 447static svn_error_t * 448fetch_sdb_info(apr_int64_t *wc_id, 449 int *format, 450 svn_sqlite__db_t *sdb, 451 apr_pool_t *scratch_pool) 452{ 453 *wc_id = -1; 454 *format = -1; 455 456 SVN_SQLITE__WITH_LOCK4( 457 svn_wc__db_util_fetch_wc_id(wc_id, sdb, scratch_pool), 458 svn_sqlite__read_schema_version(format, sdb, scratch_pool), 459 verify_stats_table(sdb, *format, scratch_pool), 460 SVN_NO_ERROR, 461 sdb); 462 463 return SVN_NO_ERROR; 464} 465 466 467svn_error_t * 468svn_wc__db_wcroot_parse_local_abspath(svn_wc__db_wcroot_t **wcroot, 469 const char **local_relpath, 470 svn_wc__db_t *db, 471 const char *local_abspath, 472 apr_pool_t *result_pool, 473 apr_pool_t *scratch_pool) 474{ 475 const char *local_dir_abspath; 476 const char *original_abspath = local_abspath; 477 svn_node_kind_t kind; 478 const char *build_relpath; 479 svn_wc__db_wcroot_t *probe_wcroot; 480 svn_wc__db_wcroot_t *found_wcroot = NULL; 481 const char *scan_abspath; 482 svn_sqlite__db_t *sdb = NULL; 483 svn_boolean_t moved_upwards = FALSE; 484 svn_boolean_t always_check = FALSE; 485 int wc_format = 0; 486 const char *adm_relpath; 487 /* Non-NULL if WCROOT is found through a symlink: */ 488 const char *symlink_wcroot_abspath = NULL; 489 490 /* ### we need more logic for finding the database (if it is located 491 ### outside of the wcroot) and then managing all of that within DB. 492 ### for now: play quick & dirty. */ 493 494 probe_wcroot = svn_hash_gets(db->dir_data, local_abspath); 495 if (probe_wcroot != NULL) 496 { 497 *wcroot = probe_wcroot; 498 499 /* We got lucky. Just return the thing BEFORE performing any I/O. */ 500 /* ### validate SMODE against how we opened wcroot->sdb? and against 501 ### DB->mode? (will we record per-dir mode?) */ 502 503 /* ### for most callers, we could pass NULL for result_pool. */ 504 *local_relpath = compute_relpath(probe_wcroot, local_abspath, 505 result_pool); 506 507 return SVN_NO_ERROR; 508 } 509 510 /* ### at some point in the future, we may need to find a way to get 511 ### rid of this stat() call. it is going to happen for EVERY call 512 ### into wc_db which references a file. calls for directories could 513 ### get an early-exit in the hash lookup just above. */ 514 SVN_ERR(get_path_kind(&kind, db, local_abspath, scratch_pool)); 515 if (kind != svn_node_dir) 516 { 517 /* If the node specified by the path is NOT present, then it cannot 518 possibly be a directory containing ".svn/wc.db". 519 520 If it is a file, then it cannot contain ".svn/wc.db". 521 522 For both of these cases, strip the basename off of the path and 523 move up one level. Keep record of what we strip, though, since 524 we'll need it later to construct local_relpath. */ 525 svn_dirent_split(&local_dir_abspath, &build_relpath, local_abspath, 526 scratch_pool); 527 528 /* Is this directory in our hash? */ 529 probe_wcroot = svn_hash_gets(db->dir_data, local_dir_abspath); 530 if (probe_wcroot != NULL) 531 { 532 const char *dir_relpath; 533 534 *wcroot = probe_wcroot; 535 536 /* Stashed directory's local_relpath + basename. */ 537 dir_relpath = compute_relpath(probe_wcroot, local_dir_abspath, 538 NULL); 539 *local_relpath = svn_relpath_join(dir_relpath, 540 build_relpath, 541 result_pool); 542 return SVN_NO_ERROR; 543 } 544 545 /* If the requested path is not on the disk, then we don't know how 546 many ancestors need to be scanned until we start hitting content 547 on the disk. Set ALWAYS_CHECK to keep looking for .svn/entries 548 rather than bailing out after the first check. */ 549 if (kind == svn_node_none) 550 always_check = TRUE; 551 552 /* Start the scanning at LOCAL_DIR_ABSPATH. */ 553 local_abspath = local_dir_abspath; 554 } 555 else 556 { 557 /* Start the local_relpath empty. If *this* directory contains the 558 wc.db, then relpath will be the empty string. */ 559 build_relpath = ""; 560 561 /* Remember the dir containing LOCAL_ABSPATH (they're the same). */ 562 local_dir_abspath = local_abspath; 563 } 564 565 /* LOCAL_ABSPATH refers to a directory at this point. At this point, 566 we've determined that an associated WCROOT is NOT in the DB's hash 567 table for this directory. Let's find an existing one in the ancestors, 568 or create one when we find the actual wcroot. */ 569 570 /* Assume that LOCAL_ABSPATH is a directory, and look for the SQLite 571 database in the right place. If we find it... great! If not, then 572 peel off some components, and try again. */ 573 574 adm_relpath = svn_wc_get_adm_dir(scratch_pool); 575 while (TRUE) 576 { 577 svn_error_t *err; 578 svn_node_kind_t adm_subdir_kind; 579 580 const char *adm_subdir = svn_dirent_join(local_abspath, adm_relpath, 581 scratch_pool); 582 583 SVN_ERR(svn_io_check_path(adm_subdir, &adm_subdir_kind, scratch_pool)); 584 585 if (adm_subdir_kind == svn_node_dir) 586 { 587 /* We always open the database in read/write mode. If the database 588 isn't writable in the filesystem, SQLite will internally open 589 it as read-only, and we'll get an error if we try to do a write 590 operation. 591 592 We could decide what to do on a per-operation basis, but since 593 we're caching database handles, it make sense to be as permissive 594 as the filesystem allows. */ 595 err = svn_wc__db_util_open_db(&sdb, local_abspath, SDB_FILE, 596 svn_sqlite__mode_readwrite, 597 db->exclusive, NULL, 598 db->state_pool, scratch_pool); 599 if (err == NULL) 600 { 601#ifdef SVN_DEBUG 602 /* Install self-verification trigger statements. */ 603 err = svn_sqlite__exec_statements(sdb, 604 STMT_VERIFICATION_TRIGGERS); 605 if (err && err->apr_err == SVN_ERR_SQLITE_ERROR) 606 { 607 /* Verification triggers can fail to install on old 1.7-dev 608 * formats which didn't have a NODES table yet. Ignore sqlite 609 * errors so such working copies can be upgraded. */ 610 svn_error_clear(err); 611 } 612 else 613 SVN_ERR(err); 614#endif 615 break; 616 } 617 if (err->apr_err != SVN_ERR_SQLITE_ERROR 618 && !APR_STATUS_IS_ENOENT(err->apr_err)) 619 return svn_error_trace(err); 620 svn_error_clear(err); 621 622 /* If we have not moved upwards, then check for a wc-1 working copy. 623 Since wc-1 has a .svn in every directory, and we didn't find one 624 in the original directory, then we aren't looking at a wc-1. 625 626 If the original path is not present, then we have to check on every 627 iteration. The content may be the immediate parent, or possibly 628 five ancetors higher. We don't test for directory presence (just 629 for the presence of subdirs/files), so we don't know when we can 630 stop checking ... so just check always. */ 631 if (!moved_upwards || always_check) 632 { 633 SVN_ERR(get_old_version(&wc_format, local_abspath, 634 scratch_pool)); 635 if (wc_format != 0) 636 break; 637 } 638 } 639 640 /* We couldn't open the SDB within the specified directory, so 641 move up one more directory. */ 642 if (svn_dirent_is_root(local_abspath, strlen(local_abspath))) 643 { 644 /* Hit the root without finding a wcroot. */ 645 646 /* The wcroot could be a symlink to a directory. 647 * (Issue #2557, #3987). If so, try again, this time scanning 648 * for a db within the directory the symlink points to, 649 * rather than within the symlink's parent directory. */ 650 if (kind == svn_node_symlink) 651 { 652 svn_node_kind_t resolved_kind; 653 654 local_abspath = original_abspath; 655 656 SVN_ERR(svn_io_check_resolved_path(local_abspath, 657 &resolved_kind, 658 scratch_pool)); 659 if (resolved_kind == svn_node_dir) 660 { 661 /* Is this directory recorded in our hash? */ 662 found_wcroot = svn_hash_gets(db->dir_data, local_abspath); 663 if (found_wcroot) 664 break; 665 666 symlink_wcroot_abspath = local_abspath; 667 SVN_ERR(read_link_target(&local_abspath, local_abspath, 668 scratch_pool)); 669try_symlink_as_dir: 670 kind = svn_node_dir; 671 moved_upwards = FALSE; 672 local_dir_abspath = local_abspath; 673 build_relpath = ""; 674 675 continue; 676 } 677 } 678 679 return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL, 680 _("'%s' is not a working copy"), 681 svn_dirent_local_style(original_abspath, 682 scratch_pool)); 683 } 684 685 local_abspath = svn_dirent_dirname(local_abspath, scratch_pool); 686 687 moved_upwards = TRUE; 688 symlink_wcroot_abspath = NULL; 689 690 /* Is the parent directory recorded in our hash? */ 691 found_wcroot = svn_hash_gets(db->dir_data, local_abspath); 692 if (found_wcroot != NULL) 693 break; 694 } 695 696 if (found_wcroot != NULL) 697 { 698 /* We found a hash table entry for an ancestor, so we stopped scanning 699 since all subdirectories use the same WCROOT. */ 700 *wcroot = found_wcroot; 701 } 702 else if (wc_format == 0) 703 { 704 /* We finally found the database. Construct a wcroot_t for it. */ 705 706 apr_int64_t wc_id; 707 int format; 708 svn_error_t *err; 709 710 err = fetch_sdb_info(&wc_id, &format, sdb, scratch_pool); 711 if (err) 712 { 713 if (err->apr_err == SVN_ERR_WC_CORRUPT) 714 return svn_error_quick_wrap( 715 err, apr_psprintf(scratch_pool, 716 _("Missing a row in WCROOT for '%s'."), 717 svn_dirent_local_style(original_abspath, 718 scratch_pool))); 719 return svn_error_trace(err); 720 } 721 722 /* WCROOT.local_abspath may be NULL when the database is stored 723 inside the wcroot, but we know the abspath is this directory 724 (ie. where we found it). */ 725 726 err = svn_wc__db_pdh_create_wcroot(wcroot, 727 apr_pstrdup(db->state_pool, 728 symlink_wcroot_abspath 729 ? symlink_wcroot_abspath 730 : local_abspath), 731 sdb, wc_id, format, 732 db->verify_format, db->enforce_empty_wq, 733 db->state_pool, scratch_pool); 734 if (err && (err->apr_err == SVN_ERR_WC_UNSUPPORTED_FORMAT || 735 err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED) && 736 kind == svn_node_symlink) 737 { 738 /* We found an unsupported WC after traversing upwards from a 739 * symlink. Fall through to code below to check if the symlink 740 * points at a supported WC. */ 741 svn_error_clear(err); 742 *wcroot = NULL; 743 } 744 else if (err) 745 { 746 /* Close handle if we are not going to use it to support 747 upgrading with exclusive wc locking. */ 748 return svn_error_compose_create(err, svn_sqlite__close(sdb)); 749 } 750 } 751 else 752 { 753 /* We found something that looks like a wc-1 working copy directory. 754 However, if the format version is 12 and the .svn/entries file 755 is only 3 bytes long, then it's a breadcrumb in a wc-ng working 756 copy that's missing an .svn/wc.db, or its .svn/wc.db is corrupt. */ 757 if (wc_format == SVN_WC__WC_NG_VERSION /* 12 */) 758 { 759 apr_finfo_t info; 760 761 /* Check attributes of .svn/entries */ 762 const char *admin_abspath = svn_wc__adm_child( 763 local_abspath, SVN_WC__ADM_ENTRIES, scratch_pool); 764 svn_error_t *err = svn_io_stat(&info, admin_abspath, APR_FINFO_SIZE, 765 scratch_pool); 766 767 /* If the former does not succeed, something is seriously wrong. */ 768 if (err) 769 return svn_error_createf( 770 SVN_ERR_WC_CORRUPT, err, 771 _("The working copy at '%s' is corrupt."), 772 svn_dirent_local_style(local_abspath, scratch_pool)); 773 svn_error_clear(err); 774 775 if (3 == info.size) 776 { 777 /* Check existence of .svn/wc.db */ 778 admin_abspath = svn_wc__adm_child(local_abspath, SDB_FILE, 779 scratch_pool); 780 err = svn_io_stat(&info, admin_abspath, APR_FINFO_SIZE, 781 scratch_pool); 782 if (err && APR_STATUS_IS_ENOENT(err->apr_err)) 783 { 784 svn_error_clear(err); 785 return svn_error_createf( 786 SVN_ERR_WC_CORRUPT, NULL, 787 _("The working copy database at '%s' is missing."), 788 svn_dirent_local_style(local_abspath, scratch_pool)); 789 } 790 else 791 /* We should never have reached this point in the code 792 if .svn/wc.db exists; therefore it's best to assume 793 it's corrupt. */ 794 return svn_error_createf( 795 SVN_ERR_WC_CORRUPT, err, 796 _("The working copy database at '%s' is corrupt."), 797 svn_dirent_local_style(local_abspath, scratch_pool)); 798 } 799 } 800 801 SVN_ERR(svn_wc__db_pdh_create_wcroot(wcroot, 802 apr_pstrdup(db->state_pool, 803 symlink_wcroot_abspath 804 ? symlink_wcroot_abspath 805 : local_abspath), 806 NULL, UNKNOWN_WC_ID, wc_format, 807 db->verify_format, db->enforce_empty_wq, 808 db->state_pool, scratch_pool)); 809 } 810 811 if (*wcroot) 812 { 813 const char *dir_relpath; 814 815 if (symlink_wcroot_abspath) 816 { 817 /* The WCROOT was found through a symlink pointing at the root of 818 * the WC. Cache the WCROOT under the symlink's path. */ 819 local_dir_abspath = symlink_wcroot_abspath; 820 } 821 822 /* The subdirectory's relpath is easily computed relative to the 823 wcroot that we just found. */ 824 dir_relpath = compute_relpath(*wcroot, local_dir_abspath, NULL); 825 826 /* And the result local_relpath may include a filename. */ 827 *local_relpath = svn_relpath_join(dir_relpath, build_relpath, result_pool); 828 } 829 830 if (kind == svn_node_symlink) 831 { 832 svn_boolean_t retry_if_dir = FALSE; 833 svn_wc__db_status_t status; 834 svn_boolean_t conflicted; 835 svn_error_t *err; 836 837 /* Check if the symlink is versioned or obstructs a versioned node 838 * in this DB -- in that case, use this wcroot. Else, if the symlink 839 * points to a directory, try to find a wcroot in that directory 840 * instead. */ 841 842 if (*wcroot) 843 { 844 err = svn_wc__db_read_info_internal(&status, NULL, NULL, NULL, NULL, 845 NULL, NULL, NULL, NULL, NULL, 846 NULL, NULL, NULL, NULL, NULL, 847 NULL, NULL, NULL, &conflicted, 848 NULL, NULL, NULL, NULL, NULL, 849 NULL, *wcroot, *local_relpath, 850 scratch_pool, scratch_pool); 851 if (err) 852 { 853 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND 854 && !SVN_WC__ERR_IS_NOT_CURRENT_WC(err)) 855 return svn_error_trace(err); 856 857 svn_error_clear(err); 858 retry_if_dir = TRUE; /* The symlink is unversioned. */ 859 } 860 else 861 { 862 /* The symlink is versioned, or obstructs a versioned node. 863 * Ignore non-conflicted not-present/excluded nodes. 864 * This allows the symlink to redirect the wcroot query to a 865 * directory, regardless of 'invisible' nodes in this WC. */ 866 retry_if_dir = ((status == svn_wc__db_status_not_present || 867 status == svn_wc__db_status_excluded || 868 status == svn_wc__db_status_server_excluded) 869 && !conflicted); 870 } 871 } 872 else 873 retry_if_dir = TRUE; 874 875 if (retry_if_dir) 876 { 877 svn_node_kind_t resolved_kind; 878 879 SVN_ERR(svn_io_check_resolved_path(original_abspath, 880 &resolved_kind, 881 scratch_pool)); 882 if (resolved_kind == svn_node_dir) 883 { 884 symlink_wcroot_abspath = original_abspath; 885 SVN_ERR(read_link_target(&local_abspath, original_abspath, 886 scratch_pool)); 887 /* This handle was opened in this function but is not going 888 to be used further so close it. */ 889 if (sdb) 890 SVN_ERR(svn_sqlite__close(sdb)); 891 goto try_symlink_as_dir; 892 } 893 } 894 } 895 896 /* We've found the appropriate WCROOT for the requested path. Stash 897 it into that path's directory. */ 898 svn_hash_sets(db->dir_data, 899 apr_pstrdup(db->state_pool, local_dir_abspath), 900 *wcroot); 901 902 /* Did we traverse up to parent directories? */ 903 if (!moved_upwards) 904 { 905 /* We did NOT move to a parent of the original requested directory. 906 We've constructed and filled in a WCROOT for the request, so we 907 are done. */ 908 return SVN_NO_ERROR; 909 } 910 911 /* The WCROOT that we just found/built was for the LOCAL_ABSPATH originally 912 passed into this function. We stepped *at least* one directory above that. 913 We should now associate the WROOT for each parent directory that does 914 not (yet) have one. */ 915 916 scan_abspath = local_dir_abspath; 917 918 do 919 { 920 const char *parent_dir = svn_dirent_dirname(scan_abspath, scratch_pool); 921 svn_wc__db_wcroot_t *parent_wcroot; 922 923 parent_wcroot = svn_hash_gets(db->dir_data, parent_dir); 924 if (parent_wcroot == NULL) 925 { 926 svn_hash_sets(db->dir_data, apr_pstrdup(db->state_pool, parent_dir), 927 *wcroot); 928 } 929 930 /* Move up a directory, stopping when we reach the directory where 931 we found/built the WCROOT. */ 932 scan_abspath = parent_dir; 933 } 934 while (strcmp(scan_abspath, local_abspath) != 0); 935 936 return SVN_NO_ERROR; 937} 938 939 940svn_error_t * 941svn_wc__db_drop_root(svn_wc__db_t *db, 942 const char *local_abspath, 943 apr_pool_t *scratch_pool) 944{ 945 svn_wc__db_wcroot_t *root_wcroot = svn_hash_gets(db->dir_data, local_abspath); 946 apr_hash_index_t *hi; 947 apr_status_t result; 948 949 if (!root_wcroot) 950 return SVN_NO_ERROR; 951 952 if (strcmp(root_wcroot->abspath, local_abspath) != 0) 953 return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL, 954 _("'%s' is not a working copy root"), 955 svn_dirent_local_style(local_abspath, 956 scratch_pool)); 957 958 for (hi = apr_hash_first(scratch_pool, db->dir_data); 959 hi; 960 hi = apr_hash_next(hi)) 961 { 962 svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi); 963 964 if (wcroot == root_wcroot) 965 svn_hash_sets(db->dir_data, svn__apr_hash_index_key(hi), NULL); 966 } 967 968 result = apr_pool_cleanup_run(db->state_pool, root_wcroot, close_wcroot); 969 if (result != APR_SUCCESS) 970 return svn_error_wrap_apr(result, NULL); 971 972 return SVN_NO_ERROR; 973} 974