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