1251881Speter/* 2251881Speter * wc_db_wcroot.c : supporting datastructures for the administrative database 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter#define SVN_WC__I_AM_WC_DB 25251881Speter 26251881Speter#include <assert.h> 27251881Speter 28251881Speter#include "svn_dirent_uri.h" 29251881Speter#include "svn_hash.h" 30251881Speter#include "svn_path.h" 31251881Speter#include "svn_version.h" 32251881Speter 33251881Speter#include "wc.h" 34251881Speter#include "adm_files.h" 35251881Speter#include "wc_db_private.h" 36251881Speter#include "wc-queries.h" 37251881Speter 38251881Speter#include "svn_private_config.h" 39251881Speter 40251881Speter/* ### Same values as wc_db.c */ 41251881Speter#define SDB_FILE "wc.db" 42251881Speter#define UNKNOWN_WC_ID ((apr_int64_t) -1) 43251881Speter#define FORMAT_FROM_SDB (-1) 44251881Speter 45251881Speter 46251881Speter 47251881Speter/* Get the format version from a wc-1 directory. If it is not a working copy 48251881Speter directory, then it sets VERSION to zero and returns no error. */ 49251881Speterstatic svn_error_t * 50251881Speterget_old_version(int *version, 51251881Speter const char *abspath, 52251881Speter apr_pool_t *scratch_pool) 53251881Speter{ 54251881Speter svn_error_t *err; 55251881Speter const char *format_file_path; 56251881Speter svn_node_kind_t kind; 57251881Speter 58251881Speter /* Try reading the format number from the entries file. */ 59251881Speter format_file_path = svn_wc__adm_child(abspath, SVN_WC__ADM_ENTRIES, 60251881Speter scratch_pool); 61251881Speter 62251881Speter /* Since trying to open a non-existent file is quite expensive, try a 63251881Speter quick stat call first. In wc-ng w/cs, this will be an early exit. */ 64251881Speter SVN_ERR(svn_io_check_path(format_file_path, &kind, scratch_pool)); 65251881Speter if (kind == svn_node_none) 66251881Speter { 67251881Speter *version = 0; 68251881Speter return SVN_NO_ERROR; 69251881Speter } 70251881Speter 71251881Speter err = svn_io_read_version_file(version, format_file_path, scratch_pool); 72251881Speter if (err == NULL) 73251881Speter return SVN_NO_ERROR; 74251881Speter if (err->apr_err != SVN_ERR_BAD_VERSION_FILE_FORMAT 75251881Speter && !APR_STATUS_IS_ENOENT(err->apr_err) 76251881Speter && !APR_STATUS_IS_ENOTDIR(err->apr_err)) 77251881Speter return svn_error_createf(SVN_ERR_WC_MISSING, err, _("'%s' does not exist"), 78251881Speter svn_dirent_local_style(abspath, scratch_pool)); 79251881Speter svn_error_clear(err); 80251881Speter 81251881Speter /* This must be a really old working copy! Fall back to reading the 82251881Speter format file. 83251881Speter 84251881Speter Note that the format file might not exist in newer working copies 85251881Speter (format 7 and higher), but in that case, the entries file should 86251881Speter have contained the format number. */ 87251881Speter format_file_path = svn_wc__adm_child(abspath, SVN_WC__ADM_FORMAT, 88251881Speter scratch_pool); 89251881Speter err = svn_io_read_version_file(version, format_file_path, scratch_pool); 90251881Speter if (err == NULL) 91251881Speter return SVN_NO_ERROR; 92251881Speter 93251881Speter /* Whatever error may have occurred... we can just ignore. This is not 94251881Speter a working copy directory. Signal the caller. */ 95251881Speter svn_error_clear(err); 96251881Speter 97251881Speter *version = 0; 98251881Speter return SVN_NO_ERROR; 99251881Speter} 100251881Speter 101251881Speter 102251881Speter/* A helper function to parse_local_abspath() which returns the on-disk KIND 103251881Speter of LOCAL_ABSPATH, using DB and SCRATCH_POOL as needed. 104251881Speter 105251881Speter This function may do strange things, but at long as it comes up with the 106251881Speter Right Answer, we should be happy. */ 107251881Speterstatic svn_error_t * 108251881Speterget_path_kind(svn_node_kind_t *kind, 109251881Speter svn_wc__db_t *db, 110251881Speter const char *local_abspath, 111251881Speter apr_pool_t *scratch_pool) 112251881Speter{ 113251881Speter svn_boolean_t special; 114251881Speter svn_node_kind_t node_kind; 115251881Speter 116251881Speter /* This implements a *really* simple LRU cache, where "simple" is defined 117251881Speter as "only one element". In other words, we remember the most recently 118251881Speter queried path, and nothing else. This gives >80% cache hits. */ 119251881Speter 120251881Speter if (db->parse_cache.abspath 121251881Speter && strcmp(db->parse_cache.abspath->data, local_abspath) == 0) 122251881Speter { 123251881Speter /* Cache hit! */ 124251881Speter *kind = db->parse_cache.kind; 125251881Speter return SVN_NO_ERROR; 126251881Speter } 127251881Speter 128251881Speter if (!db->parse_cache.abspath) 129251881Speter { 130251881Speter db->parse_cache.abspath = svn_stringbuf_create(local_abspath, 131251881Speter db->state_pool); 132251881Speter } 133251881Speter else 134251881Speter { 135251881Speter svn_stringbuf_set(db->parse_cache.abspath, local_abspath); 136251881Speter } 137251881Speter 138251881Speter SVN_ERR(svn_io_check_special_path(local_abspath, &node_kind, 139251881Speter &special, scratch_pool)); 140251881Speter 141251881Speter db->parse_cache.kind = (special ? svn_node_symlink : node_kind); 142251881Speter *kind = db->parse_cache.kind; 143251881Speter 144251881Speter return SVN_NO_ERROR; 145251881Speter} 146251881Speter 147251881Speter 148251881Speter/* Return an error if the work queue in SDB is non-empty. */ 149251881Speterstatic svn_error_t * 150251881Speterverify_no_work(svn_sqlite__db_t *sdb) 151251881Speter{ 152251881Speter svn_sqlite__stmt_t *stmt; 153251881Speter svn_boolean_t have_row; 154251881Speter 155251881Speter SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_LOOK_FOR_WORK)); 156251881Speter SVN_ERR(svn_sqlite__step(&have_row, stmt)); 157251881Speter SVN_ERR(svn_sqlite__reset(stmt)); 158251881Speter 159251881Speter if (have_row) 160251881Speter return svn_error_create(SVN_ERR_WC_CLEANUP_REQUIRED, NULL, 161251881Speter NULL /* nothing to add. */); 162251881Speter 163251881Speter return SVN_NO_ERROR; 164251881Speter} 165251881Speter 166251881Speter 167251881Speter/* */ 168251881Speterstatic apr_status_t 169251881Speterclose_wcroot(void *data) 170251881Speter{ 171251881Speter svn_wc__db_wcroot_t *wcroot = data; 172251881Speter svn_error_t *err; 173251881Speter 174251881Speter SVN_ERR_ASSERT_NO_RETURN(wcroot->sdb != NULL); 175251881Speter 176251881Speter err = svn_sqlite__close(wcroot->sdb); 177251881Speter wcroot->sdb = NULL; 178251881Speter if (err) 179251881Speter { 180251881Speter apr_status_t result = err->apr_err; 181251881Speter svn_error_clear(err); 182251881Speter return result; 183251881Speter } 184251881Speter 185251881Speter return APR_SUCCESS; 186251881Speter} 187251881Speter 188251881Speter 189251881Spetersvn_error_t * 190251881Spetersvn_wc__db_open(svn_wc__db_t **db, 191251881Speter svn_config_t *config, 192251881Speter svn_boolean_t open_without_upgrade, 193251881Speter svn_boolean_t enforce_empty_wq, 194251881Speter apr_pool_t *result_pool, 195251881Speter apr_pool_t *scratch_pool) 196251881Speter{ 197251881Speter *db = apr_pcalloc(result_pool, sizeof(**db)); 198251881Speter (*db)->config = config; 199251881Speter (*db)->verify_format = !open_without_upgrade; 200251881Speter (*db)->enforce_empty_wq = enforce_empty_wq; 201251881Speter (*db)->dir_data = apr_hash_make(result_pool); 202251881Speter 203251881Speter (*db)->state_pool = result_pool; 204251881Speter 205251881Speter /* Don't need to initialize (*db)->parse_cache, due to the calloc above */ 206251881Speter if (config) 207251881Speter { 208251881Speter svn_error_t *err; 209251881Speter svn_boolean_t sqlite_exclusive = FALSE; 210251881Speter 211251881Speter err = svn_config_get_bool(config, &sqlite_exclusive, 212251881Speter SVN_CONFIG_SECTION_WORKING_COPY, 213251881Speter SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE, 214251881Speter FALSE); 215251881Speter if (err) 216251881Speter { 217251881Speter svn_error_clear(err); 218251881Speter } 219251881Speter else 220251881Speter (*db)->exclusive = sqlite_exclusive; 221251881Speter } 222251881Speter 223251881Speter return SVN_NO_ERROR; 224251881Speter} 225251881Speter 226251881Speter 227251881Spetersvn_error_t * 228251881Spetersvn_wc__db_close(svn_wc__db_t *db) 229251881Speter{ 230251881Speter apr_pool_t *scratch_pool = db->state_pool; 231251881Speter apr_hash_t *roots = apr_hash_make(scratch_pool); 232251881Speter apr_hash_index_t *hi; 233251881Speter 234251881Speter /* Collect all the unique WCROOT structures, and empty out DIR_DATA. */ 235251881Speter for (hi = apr_hash_first(scratch_pool, db->dir_data); 236251881Speter hi; 237251881Speter hi = apr_hash_next(hi)) 238251881Speter { 239251881Speter svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi); 240251881Speter const char *local_abspath = svn__apr_hash_index_key(hi); 241251881Speter 242251881Speter if (wcroot->sdb) 243251881Speter svn_hash_sets(roots, wcroot->abspath, wcroot); 244251881Speter 245251881Speter svn_hash_sets(db->dir_data, local_abspath, NULL); 246251881Speter } 247251881Speter 248251881Speter /* Run the cleanup for each WCROOT. */ 249251881Speter return svn_error_trace(svn_wc__db_close_many_wcroots(roots, db->state_pool, 250251881Speter scratch_pool)); 251251881Speter} 252251881Speter 253251881Speter 254251881Spetersvn_error_t * 255251881Spetersvn_wc__db_pdh_create_wcroot(svn_wc__db_wcroot_t **wcroot, 256251881Speter const char *wcroot_abspath, 257251881Speter svn_sqlite__db_t *sdb, 258251881Speter apr_int64_t wc_id, 259251881Speter int format, 260251881Speter svn_boolean_t verify_format, 261251881Speter svn_boolean_t enforce_empty_wq, 262251881Speter apr_pool_t *result_pool, 263251881Speter apr_pool_t *scratch_pool) 264251881Speter{ 265251881Speter if (sdb != NULL) 266251881Speter SVN_ERR(svn_sqlite__read_schema_version(&format, sdb, scratch_pool)); 267251881Speter 268251881Speter /* If we construct a wcroot, then we better have a format. */ 269251881Speter SVN_ERR_ASSERT(format >= 1); 270251881Speter 271251881Speter /* If this working copy is PRE-1.0, then simply bail out. */ 272251881Speter if (format < 4) 273251881Speter { 274251881Speter return svn_error_createf( 275251881Speter SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL, 276251881Speter _("Working copy format of '%s' is too old (%d); " 277251881Speter "please check out your working copy again"), 278251881Speter svn_dirent_local_style(wcroot_abspath, scratch_pool), format); 279251881Speter } 280251881Speter 281251881Speter /* If this working copy is from a future version, then bail out. */ 282251881Speter if (format > SVN_WC__VERSION) 283251881Speter { 284251881Speter return svn_error_createf( 285251881Speter SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL, 286251881Speter _("This client is too old to work with the working copy at\n" 287251881Speter "'%s' (format %d).\n" 288251881Speter "You need to get a newer Subversion client. For more details, see\n" 289251881Speter " http://subversion.apache.org/faq.html#working-copy-format-change\n" 290251881Speter ), 291251881Speter svn_dirent_local_style(wcroot_abspath, scratch_pool), 292251881Speter format); 293251881Speter } 294251881Speter 295251881Speter /* Verify that no work items exists. If they do, then our integrity is 296251881Speter suspect and, thus, we cannot use this database. */ 297251881Speter if (format >= SVN_WC__HAS_WORK_QUEUE 298251881Speter && (enforce_empty_wq || (format < SVN_WC__VERSION && verify_format))) 299251881Speter { 300251881Speter svn_error_t *err = verify_no_work(sdb); 301251881Speter if (err) 302251881Speter { 303251881Speter /* Special message for attempts to upgrade a 1.7-dev wc with 304251881Speter outstanding workqueue items. */ 305251881Speter if (err->apr_err == SVN_ERR_WC_CLEANUP_REQUIRED 306251881Speter && format < SVN_WC__VERSION && verify_format) 307251881Speter err = svn_error_quick_wrap(err, _("Cleanup with an older 1.7 " 308251881Speter "client before upgrading with " 309251881Speter "this client")); 310251881Speter return svn_error_trace(err); 311251881Speter } 312251881Speter } 313251881Speter 314251881Speter /* Auto-upgrade the SDB if possible. */ 315251881Speter if (format < SVN_WC__VERSION && verify_format) 316251881Speter { 317251881Speter return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, NULL, 318251881Speter _("The working copy at '%s'\nis too old " 319251881Speter "(format %d) to work with client version " 320251881Speter "'%s' (expects format %d). You need to " 321251881Speter "upgrade the working copy first.\n"), 322251881Speter svn_dirent_local_style(wcroot_abspath, 323251881Speter scratch_pool), 324251881Speter format, SVN_VERSION, SVN_WC__VERSION); 325251881Speter } 326251881Speter 327251881Speter *wcroot = apr_palloc(result_pool, sizeof(**wcroot)); 328251881Speter 329251881Speter (*wcroot)->abspath = wcroot_abspath; 330251881Speter (*wcroot)->sdb = sdb; 331251881Speter (*wcroot)->wc_id = wc_id; 332251881Speter (*wcroot)->format = format; 333251881Speter /* 8 concurrent locks is probably more than a typical wc_ng based svn client 334251881Speter uses. */ 335251881Speter (*wcroot)->owned_locks = apr_array_make(result_pool, 8, 336251881Speter sizeof(svn_wc__db_wclock_t)); 337251881Speter (*wcroot)->access_cache = apr_hash_make(result_pool); 338251881Speter 339251881Speter /* SDB will be NULL for pre-NG working copies. We only need to run a 340251881Speter cleanup when the SDB is present. */ 341251881Speter if (sdb != NULL) 342251881Speter apr_pool_cleanup_register(result_pool, *wcroot, close_wcroot, 343251881Speter apr_pool_cleanup_null); 344251881Speter return SVN_NO_ERROR; 345251881Speter} 346251881Speter 347251881Speter 348251881Spetersvn_error_t * 349251881Spetersvn_wc__db_close_many_wcroots(apr_hash_t *roots, 350251881Speter apr_pool_t *state_pool, 351251881Speter apr_pool_t *scratch_pool) 352251881Speter{ 353251881Speter apr_hash_index_t *hi; 354251881Speter 355251881Speter for (hi = apr_hash_first(scratch_pool, roots); hi; hi = apr_hash_next(hi)) 356251881Speter { 357251881Speter svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi); 358251881Speter apr_status_t result; 359251881Speter 360251881Speter result = apr_pool_cleanup_run(state_pool, wcroot, close_wcroot); 361251881Speter if (result != APR_SUCCESS) 362251881Speter return svn_error_wrap_apr(result, NULL); 363251881Speter } 364251881Speter 365251881Speter return SVN_NO_ERROR; 366251881Speter} 367251881Speter 368251881Speter 369251881Speter/* POOL may be NULL if the lifetime of LOCAL_ABSPATH is sufficient. */ 370251881Speterstatic const char * 371251881Spetercompute_relpath(const svn_wc__db_wcroot_t *wcroot, 372251881Speter const char *local_abspath, 373251881Speter apr_pool_t *result_pool) 374251881Speter{ 375251881Speter const char *relpath = svn_dirent_is_child(wcroot->abspath, local_abspath, 376251881Speter result_pool); 377251881Speter if (relpath == NULL) 378251881Speter return ""; 379251881Speter return relpath; 380251881Speter} 381251881Speter 382251881Speter 383251881Speter/* Return in *LINK_TARGET_ABSPATH the absolute path the symlink at 384251881Speter * LOCAL_ABSPATH is pointing to. Perform all allocations in POOL. */ 385251881Speterstatic svn_error_t * 386251881Speterread_link_target(const char **link_target_abspath, 387251881Speter const char *local_abspath, 388251881Speter apr_pool_t *pool) 389251881Speter{ 390251881Speter svn_string_t *link_target; 391251881Speter const char *canon_link_target; 392251881Speter 393251881Speter SVN_ERR(svn_io_read_link(&link_target, local_abspath, pool)); 394251881Speter if (link_target->len == 0) 395251881Speter return svn_error_createf(SVN_ERR_WC_NOT_SYMLINK, NULL, 396251881Speter _("The symlink at '%s' points nowhere"), 397251881Speter svn_dirent_local_style(local_abspath, pool)); 398251881Speter 399251881Speter canon_link_target = svn_dirent_canonicalize(link_target->data, pool); 400251881Speter 401251881Speter /* Treat relative symlinks as relative to LOCAL_ABSPATH's parent. */ 402251881Speter if (!svn_dirent_is_absolute(canon_link_target)) 403251881Speter canon_link_target = svn_dirent_join(svn_dirent_dirname(local_abspath, 404251881Speter pool), 405251881Speter canon_link_target, pool); 406251881Speter 407251881Speter /* Collapse any .. in the symlink part of the path. */ 408251881Speter if (svn_path_is_backpath_present(canon_link_target)) 409251881Speter SVN_ERR(svn_dirent_get_absolute(link_target_abspath, canon_link_target, 410251881Speter pool)); 411251881Speter else 412251881Speter *link_target_abspath = canon_link_target; 413251881Speter 414251881Speter return SVN_NO_ERROR; 415251881Speter} 416251881Speter 417251881Spetersvn_error_t * 418251881Spetersvn_wc__db_wcroot_parse_local_abspath(svn_wc__db_wcroot_t **wcroot, 419251881Speter const char **local_relpath, 420251881Speter svn_wc__db_t *db, 421251881Speter const char *local_abspath, 422251881Speter apr_pool_t *result_pool, 423251881Speter apr_pool_t *scratch_pool) 424251881Speter{ 425251881Speter const char *local_dir_abspath; 426251881Speter const char *original_abspath = local_abspath; 427251881Speter svn_node_kind_t kind; 428251881Speter const char *build_relpath; 429251881Speter svn_wc__db_wcroot_t *probe_wcroot; 430251881Speter svn_wc__db_wcroot_t *found_wcroot = NULL; 431251881Speter const char *scan_abspath; 432251881Speter svn_sqlite__db_t *sdb = NULL; 433251881Speter svn_boolean_t moved_upwards = FALSE; 434251881Speter svn_boolean_t always_check = FALSE; 435251881Speter int wc_format = 0; 436251881Speter const char *adm_relpath; 437253734Speter /* Non-NULL if WCROOT is found through a symlink: */ 438253734Speter const char *symlink_wcroot_abspath = NULL; 439251881Speter 440251881Speter /* ### we need more logic for finding the database (if it is located 441251881Speter ### outside of the wcroot) and then managing all of that within DB. 442251881Speter ### for now: play quick & dirty. */ 443251881Speter 444251881Speter probe_wcroot = svn_hash_gets(db->dir_data, local_abspath); 445251881Speter if (probe_wcroot != NULL) 446251881Speter { 447251881Speter *wcroot = probe_wcroot; 448251881Speter 449251881Speter /* We got lucky. Just return the thing BEFORE performing any I/O. */ 450251881Speter /* ### validate SMODE against how we opened wcroot->sdb? and against 451251881Speter ### DB->mode? (will we record per-dir mode?) */ 452251881Speter 453251881Speter /* ### for most callers, we could pass NULL for result_pool. */ 454251881Speter *local_relpath = compute_relpath(probe_wcroot, local_abspath, 455251881Speter result_pool); 456251881Speter 457251881Speter return SVN_NO_ERROR; 458251881Speter } 459251881Speter 460251881Speter /* ### at some point in the future, we may need to find a way to get 461251881Speter ### rid of this stat() call. it is going to happen for EVERY call 462251881Speter ### into wc_db which references a file. calls for directories could 463251881Speter ### get an early-exit in the hash lookup just above. */ 464251881Speter SVN_ERR(get_path_kind(&kind, db, local_abspath, scratch_pool)); 465251881Speter if (kind != svn_node_dir) 466251881Speter { 467251881Speter /* If the node specified by the path is NOT present, then it cannot 468251881Speter possibly be a directory containing ".svn/wc.db". 469251881Speter 470251881Speter If it is a file, then it cannot contain ".svn/wc.db". 471251881Speter 472251881Speter For both of these cases, strip the basename off of the path and 473251881Speter move up one level. Keep record of what we strip, though, since 474251881Speter we'll need it later to construct local_relpath. */ 475251881Speter svn_dirent_split(&local_dir_abspath, &build_relpath, local_abspath, 476251881Speter scratch_pool); 477251881Speter 478251881Speter /* Is this directory in our hash? */ 479251881Speter probe_wcroot = svn_hash_gets(db->dir_data, local_dir_abspath); 480251881Speter if (probe_wcroot != NULL) 481251881Speter { 482251881Speter const char *dir_relpath; 483251881Speter 484251881Speter *wcroot = probe_wcroot; 485251881Speter 486251881Speter /* Stashed directory's local_relpath + basename. */ 487251881Speter dir_relpath = compute_relpath(probe_wcroot, local_dir_abspath, 488251881Speter NULL); 489251881Speter *local_relpath = svn_relpath_join(dir_relpath, 490251881Speter build_relpath, 491251881Speter result_pool); 492251881Speter return SVN_NO_ERROR; 493251881Speter } 494251881Speter 495251881Speter /* If the requested path is not on the disk, then we don't know how 496251881Speter many ancestors need to be scanned until we start hitting content 497251881Speter on the disk. Set ALWAYS_CHECK to keep looking for .svn/entries 498251881Speter rather than bailing out after the first check. */ 499251881Speter if (kind == svn_node_none) 500251881Speter always_check = TRUE; 501251881Speter 502251881Speter /* Start the scanning at LOCAL_DIR_ABSPATH. */ 503251881Speter local_abspath = local_dir_abspath; 504251881Speter } 505251881Speter else 506251881Speter { 507251881Speter /* Start the local_relpath empty. If *this* directory contains the 508251881Speter wc.db, then relpath will be the empty string. */ 509251881Speter build_relpath = ""; 510251881Speter 511251881Speter /* Remember the dir containing LOCAL_ABSPATH (they're the same). */ 512251881Speter local_dir_abspath = local_abspath; 513251881Speter } 514251881Speter 515251881Speter /* LOCAL_ABSPATH refers to a directory at this point. At this point, 516251881Speter we've determined that an associated WCROOT is NOT in the DB's hash 517251881Speter table for this directory. Let's find an existing one in the ancestors, 518251881Speter or create one when we find the actual wcroot. */ 519251881Speter 520251881Speter /* Assume that LOCAL_ABSPATH is a directory, and look for the SQLite 521251881Speter database in the right place. If we find it... great! If not, then 522251881Speter peel off some components, and try again. */ 523251881Speter 524251881Speter adm_relpath = svn_wc_get_adm_dir(scratch_pool); 525251881Speter while (TRUE) 526251881Speter { 527251881Speter svn_error_t *err; 528251881Speter svn_node_kind_t adm_subdir_kind; 529251881Speter 530251881Speter const char *adm_subdir = svn_dirent_join(local_abspath, adm_relpath, 531251881Speter scratch_pool); 532251881Speter 533251881Speter SVN_ERR(svn_io_check_path(adm_subdir, &adm_subdir_kind, scratch_pool)); 534251881Speter 535251881Speter if (adm_subdir_kind == svn_node_dir) 536251881Speter { 537251881Speter /* We always open the database in read/write mode. If the database 538251881Speter isn't writable in the filesystem, SQLite will internally open 539251881Speter it as read-only, and we'll get an error if we try to do a write 540251881Speter operation. 541251881Speter 542251881Speter We could decide what to do on a per-operation basis, but since 543251881Speter we're caching database handles, it make sense to be as permissive 544251881Speter as the filesystem allows. */ 545251881Speter err = svn_wc__db_util_open_db(&sdb, local_abspath, SDB_FILE, 546251881Speter svn_sqlite__mode_readwrite, 547251881Speter db->exclusive, NULL, 548251881Speter db->state_pool, scratch_pool); 549251881Speter if (err == NULL) 550251881Speter { 551251881Speter#ifdef SVN_DEBUG 552251881Speter /* Install self-verification trigger statements. */ 553251881Speter err = svn_sqlite__exec_statements(sdb, 554251881Speter STMT_VERIFICATION_TRIGGERS); 555251881Speter if (err && err->apr_err == SVN_ERR_SQLITE_ERROR) 556251881Speter { 557251881Speter /* Verification triggers can fail to install on old 1.7-dev 558251881Speter * formats which didn't have a NODES table yet. Ignore sqlite 559251881Speter * errors so such working copies can be upgraded. */ 560251881Speter svn_error_clear(err); 561251881Speter } 562251881Speter else 563251881Speter SVN_ERR(err); 564251881Speter#endif 565251881Speter break; 566251881Speter } 567251881Speter if (err->apr_err != SVN_ERR_SQLITE_ERROR 568251881Speter && !APR_STATUS_IS_ENOENT(err->apr_err)) 569251881Speter return svn_error_trace(err); 570251881Speter svn_error_clear(err); 571251881Speter 572251881Speter /* If we have not moved upwards, then check for a wc-1 working copy. 573251881Speter Since wc-1 has a .svn in every directory, and we didn't find one 574251881Speter in the original directory, then we aren't looking at a wc-1. 575251881Speter 576251881Speter If the original path is not present, then we have to check on every 577251881Speter iteration. The content may be the immediate parent, or possibly 578251881Speter five ancetors higher. We don't test for directory presence (just 579251881Speter for the presence of subdirs/files), so we don't know when we can 580251881Speter stop checking ... so just check always. */ 581251881Speter if (!moved_upwards || always_check) 582251881Speter { 583251881Speter SVN_ERR(get_old_version(&wc_format, local_abspath, 584251881Speter scratch_pool)); 585251881Speter if (wc_format != 0) 586251881Speter break; 587251881Speter } 588251881Speter } 589251881Speter 590251881Speter /* We couldn't open the SDB within the specified directory, so 591251881Speter move up one more directory. */ 592251881Speter if (svn_dirent_is_root(local_abspath, strlen(local_abspath))) 593251881Speter { 594251881Speter /* Hit the root without finding a wcroot. */ 595251881Speter 596251881Speter /* The wcroot could be a symlink to a directory. 597251881Speter * (Issue #2557, #3987). If so, try again, this time scanning 598251881Speter * for a db within the directory the symlink points to, 599251881Speter * rather than within the symlink's parent directory. */ 600251881Speter if (kind == svn_node_symlink) 601251881Speter { 602251881Speter svn_node_kind_t resolved_kind; 603251881Speter 604251881Speter local_abspath = original_abspath; 605251881Speter 606251881Speter SVN_ERR(svn_io_check_resolved_path(local_abspath, 607251881Speter &resolved_kind, 608251881Speter scratch_pool)); 609251881Speter if (resolved_kind == svn_node_dir) 610251881Speter { 611251881Speter /* Is this directory recorded in our hash? */ 612251881Speter found_wcroot = svn_hash_gets(db->dir_data, local_abspath); 613251881Speter if (found_wcroot) 614251881Speter break; 615251881Speter 616253734Speter symlink_wcroot_abspath = local_abspath; 617251881Speter SVN_ERR(read_link_target(&local_abspath, local_abspath, 618251881Speter scratch_pool)); 619251881Spetertry_symlink_as_dir: 620251881Speter kind = svn_node_dir; 621251881Speter moved_upwards = FALSE; 622251881Speter local_dir_abspath = local_abspath; 623251881Speter build_relpath = ""; 624251881Speter 625251881Speter continue; 626251881Speter } 627251881Speter } 628251881Speter 629251881Speter return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL, 630251881Speter _("'%s' is not a working copy"), 631251881Speter svn_dirent_local_style(original_abspath, 632251881Speter scratch_pool)); 633251881Speter } 634251881Speter 635251881Speter local_abspath = svn_dirent_dirname(local_abspath, scratch_pool); 636251881Speter 637251881Speter moved_upwards = TRUE; 638253734Speter symlink_wcroot_abspath = NULL; 639251881Speter 640251881Speter /* Is the parent directory recorded in our hash? */ 641251881Speter found_wcroot = svn_hash_gets(db->dir_data, local_abspath); 642251881Speter if (found_wcroot != NULL) 643251881Speter break; 644251881Speter } 645251881Speter 646251881Speter if (found_wcroot != NULL) 647251881Speter { 648251881Speter /* We found a hash table entry for an ancestor, so we stopped scanning 649251881Speter since all subdirectories use the same WCROOT. */ 650251881Speter *wcroot = found_wcroot; 651251881Speter } 652251881Speter else if (wc_format == 0) 653251881Speter { 654251881Speter /* We finally found the database. Construct a wcroot_t for it. */ 655251881Speter 656251881Speter apr_int64_t wc_id; 657251881Speter svn_error_t *err; 658251881Speter 659251881Speter err = svn_wc__db_util_fetch_wc_id(&wc_id, sdb, scratch_pool); 660251881Speter if (err) 661251881Speter { 662251881Speter if (err->apr_err == SVN_ERR_WC_CORRUPT) 663251881Speter return svn_error_quick_wrap( 664251881Speter err, apr_psprintf(scratch_pool, 665251881Speter _("Missing a row in WCROOT for '%s'."), 666251881Speter svn_dirent_local_style(original_abspath, 667251881Speter scratch_pool))); 668251881Speter return svn_error_trace(err); 669251881Speter } 670251881Speter 671251881Speter /* WCROOT.local_abspath may be NULL when the database is stored 672251881Speter inside the wcroot, but we know the abspath is this directory 673251881Speter (ie. where we found it). */ 674251881Speter 675251881Speter err = svn_wc__db_pdh_create_wcroot(wcroot, 676253734Speter apr_pstrdup(db->state_pool, 677253734Speter symlink_wcroot_abspath 678253734Speter ? symlink_wcroot_abspath 679253734Speter : local_abspath), 680251881Speter sdb, wc_id, FORMAT_FROM_SDB, 681251881Speter db->verify_format, db->enforce_empty_wq, 682251881Speter db->state_pool, scratch_pool); 683251881Speter if (err && (err->apr_err == SVN_ERR_WC_UNSUPPORTED_FORMAT || 684251881Speter err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED) && 685251881Speter kind == svn_node_symlink) 686251881Speter { 687251881Speter /* We found an unsupported WC after traversing upwards from a 688251881Speter * symlink. Fall through to code below to check if the symlink 689251881Speter * points at a supported WC. */ 690251881Speter svn_error_clear(err); 691251881Speter *wcroot = NULL; 692251881Speter } 693269847Speter else if (err) 694269847Speter { 695269847Speter /* Close handle if we are not going to use it to support 696269847Speter upgrading with exclusive wc locking. */ 697269847Speter return svn_error_compose_create(err, svn_sqlite__close(sdb)); 698269847Speter } 699251881Speter } 700251881Speter else 701251881Speter { 702251881Speter /* We found something that looks like a wc-1 working copy directory. 703251881Speter However, if the format version is 12 and the .svn/entries file 704251881Speter is only 3 bytes long, then it's a breadcrumb in a wc-ng working 705251881Speter copy that's missing an .svn/wc.db, or its .svn/wc.db is corrupt. */ 706251881Speter if (wc_format == SVN_WC__WC_NG_VERSION /* 12 */) 707251881Speter { 708251881Speter apr_finfo_t info; 709251881Speter 710251881Speter /* Check attributes of .svn/entries */ 711251881Speter const char *admin_abspath = svn_wc__adm_child( 712251881Speter local_abspath, SVN_WC__ADM_ENTRIES, scratch_pool); 713251881Speter svn_error_t *err = svn_io_stat(&info, admin_abspath, APR_FINFO_SIZE, 714251881Speter scratch_pool); 715251881Speter 716251881Speter /* If the former does not succeed, something is seriously wrong. */ 717251881Speter if (err) 718251881Speter return svn_error_createf( 719251881Speter SVN_ERR_WC_CORRUPT, err, 720251881Speter _("The working copy at '%s' is corrupt."), 721251881Speter svn_dirent_local_style(local_abspath, scratch_pool)); 722251881Speter svn_error_clear(err); 723251881Speter 724251881Speter if (3 == info.size) 725251881Speter { 726251881Speter /* Check existence of .svn/wc.db */ 727251881Speter admin_abspath = svn_wc__adm_child(local_abspath, SDB_FILE, 728251881Speter scratch_pool); 729251881Speter err = svn_io_stat(&info, admin_abspath, APR_FINFO_SIZE, 730251881Speter scratch_pool); 731251881Speter if (err && APR_STATUS_IS_ENOENT(err->apr_err)) 732251881Speter { 733251881Speter svn_error_clear(err); 734251881Speter return svn_error_createf( 735251881Speter SVN_ERR_WC_CORRUPT, NULL, 736251881Speter _("The working copy database at '%s' is missing."), 737251881Speter svn_dirent_local_style(local_abspath, scratch_pool)); 738251881Speter } 739251881Speter else 740251881Speter /* We should never have reached this point in the code 741251881Speter if .svn/wc.db exists; therefore it's best to assume 742251881Speter it's corrupt. */ 743251881Speter return svn_error_createf( 744251881Speter SVN_ERR_WC_CORRUPT, err, 745251881Speter _("The working copy database at '%s' is corrupt."), 746251881Speter svn_dirent_local_style(local_abspath, scratch_pool)); 747251881Speter } 748251881Speter } 749251881Speter 750251881Speter SVN_ERR(svn_wc__db_pdh_create_wcroot(wcroot, 751253734Speter apr_pstrdup(db->state_pool, 752253734Speter symlink_wcroot_abspath 753253734Speter ? symlink_wcroot_abspath 754253734Speter : local_abspath), 755251881Speter NULL, UNKNOWN_WC_ID, wc_format, 756251881Speter db->verify_format, db->enforce_empty_wq, 757251881Speter db->state_pool, scratch_pool)); 758251881Speter } 759251881Speter 760251881Speter if (*wcroot) 761251881Speter { 762251881Speter const char *dir_relpath; 763251881Speter 764253734Speter if (symlink_wcroot_abspath) 765253734Speter { 766253734Speter /* The WCROOT was found through a symlink pointing at the root of 767253734Speter * the WC. Cache the WCROOT under the symlink's path. */ 768253734Speter local_dir_abspath = symlink_wcroot_abspath; 769253734Speter } 770253734Speter 771251881Speter /* The subdirectory's relpath is easily computed relative to the 772251881Speter wcroot that we just found. */ 773251881Speter dir_relpath = compute_relpath(*wcroot, local_dir_abspath, NULL); 774251881Speter 775251881Speter /* And the result local_relpath may include a filename. */ 776251881Speter *local_relpath = svn_relpath_join(dir_relpath, build_relpath, result_pool); 777251881Speter } 778251881Speter 779251881Speter if (kind == svn_node_symlink) 780251881Speter { 781251881Speter svn_boolean_t retry_if_dir = FALSE; 782251881Speter svn_wc__db_status_t status; 783251881Speter svn_boolean_t conflicted; 784251881Speter svn_error_t *err; 785251881Speter 786251881Speter /* Check if the symlink is versioned or obstructs a versioned node 787251881Speter * in this DB -- in that case, use this wcroot. Else, if the symlink 788251881Speter * points to a directory, try to find a wcroot in that directory 789251881Speter * instead. */ 790251881Speter 791251881Speter if (*wcroot) 792251881Speter { 793251881Speter err = svn_wc__db_read_info_internal(&status, NULL, NULL, NULL, NULL, 794251881Speter NULL, NULL, NULL, NULL, NULL, 795251881Speter NULL, NULL, NULL, NULL, NULL, 796251881Speter NULL, NULL, NULL, &conflicted, 797251881Speter NULL, NULL, NULL, NULL, NULL, 798251881Speter NULL, *wcroot, *local_relpath, 799251881Speter scratch_pool, scratch_pool); 800251881Speter if (err) 801251881Speter { 802251881Speter if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND 803251881Speter && !SVN_WC__ERR_IS_NOT_CURRENT_WC(err)) 804251881Speter return svn_error_trace(err); 805251881Speter 806251881Speter svn_error_clear(err); 807251881Speter retry_if_dir = TRUE; /* The symlink is unversioned. */ 808251881Speter } 809251881Speter else 810251881Speter { 811251881Speter /* The symlink is versioned, or obstructs a versioned node. 812251881Speter * Ignore non-conflicted not-present/excluded nodes. 813251881Speter * This allows the symlink to redirect the wcroot query to a 814251881Speter * directory, regardless of 'invisible' nodes in this WC. */ 815251881Speter retry_if_dir = ((status == svn_wc__db_status_not_present || 816251881Speter status == svn_wc__db_status_excluded || 817251881Speter status == svn_wc__db_status_server_excluded) 818251881Speter && !conflicted); 819251881Speter } 820251881Speter } 821251881Speter else 822251881Speter retry_if_dir = TRUE; 823251881Speter 824251881Speter if (retry_if_dir) 825251881Speter { 826251881Speter svn_node_kind_t resolved_kind; 827251881Speter 828251881Speter SVN_ERR(svn_io_check_resolved_path(original_abspath, 829251881Speter &resolved_kind, 830251881Speter scratch_pool)); 831251881Speter if (resolved_kind == svn_node_dir) 832251881Speter { 833253734Speter symlink_wcroot_abspath = original_abspath; 834251881Speter SVN_ERR(read_link_target(&local_abspath, original_abspath, 835251881Speter scratch_pool)); 836251881Speter /* This handle was opened in this function but is not going 837251881Speter to be used further so close it. */ 838251881Speter if (sdb) 839251881Speter SVN_ERR(svn_sqlite__close(sdb)); 840251881Speter goto try_symlink_as_dir; 841251881Speter } 842251881Speter } 843251881Speter } 844251881Speter 845251881Speter /* We've found the appropriate WCROOT for the requested path. Stash 846251881Speter it into that path's directory. */ 847251881Speter svn_hash_sets(db->dir_data, 848251881Speter apr_pstrdup(db->state_pool, local_dir_abspath), 849251881Speter *wcroot); 850251881Speter 851251881Speter /* Did we traverse up to parent directories? */ 852251881Speter if (!moved_upwards) 853251881Speter { 854251881Speter /* We did NOT move to a parent of the original requested directory. 855251881Speter We've constructed and filled in a WCROOT for the request, so we 856251881Speter are done. */ 857251881Speter return SVN_NO_ERROR; 858251881Speter } 859251881Speter 860251881Speter /* The WCROOT that we just found/built was for the LOCAL_ABSPATH originally 861251881Speter passed into this function. We stepped *at least* one directory above that. 862251881Speter We should now associate the WROOT for each parent directory that does 863251881Speter not (yet) have one. */ 864251881Speter 865251881Speter scan_abspath = local_dir_abspath; 866251881Speter 867251881Speter do 868251881Speter { 869251881Speter const char *parent_dir = svn_dirent_dirname(scan_abspath, scratch_pool); 870251881Speter svn_wc__db_wcroot_t *parent_wcroot; 871251881Speter 872251881Speter parent_wcroot = svn_hash_gets(db->dir_data, parent_dir); 873251881Speter if (parent_wcroot == NULL) 874251881Speter { 875251881Speter svn_hash_sets(db->dir_data, apr_pstrdup(db->state_pool, parent_dir), 876251881Speter *wcroot); 877251881Speter } 878251881Speter 879251881Speter /* Move up a directory, stopping when we reach the directory where 880251881Speter we found/built the WCROOT. */ 881251881Speter scan_abspath = parent_dir; 882251881Speter } 883251881Speter while (strcmp(scan_abspath, local_abspath) != 0); 884251881Speter 885251881Speter return SVN_NO_ERROR; 886251881Speter} 887251881Speter 888251881Speter 889251881Spetersvn_error_t * 890251881Spetersvn_wc__db_drop_root(svn_wc__db_t *db, 891251881Speter const char *local_abspath, 892251881Speter apr_pool_t *scratch_pool) 893251881Speter{ 894251881Speter svn_wc__db_wcroot_t *root_wcroot = svn_hash_gets(db->dir_data, local_abspath); 895251881Speter apr_hash_index_t *hi; 896251881Speter apr_status_t result; 897251881Speter 898251881Speter if (!root_wcroot) 899251881Speter return SVN_NO_ERROR; 900251881Speter 901251881Speter if (strcmp(root_wcroot->abspath, local_abspath) != 0) 902251881Speter return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL, 903251881Speter _("'%s' is not a working copy root"), 904251881Speter svn_dirent_local_style(local_abspath, 905251881Speter scratch_pool)); 906251881Speter 907251881Speter for (hi = apr_hash_first(scratch_pool, db->dir_data); 908251881Speter hi; 909251881Speter hi = apr_hash_next(hi)) 910251881Speter { 911251881Speter svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi); 912251881Speter 913251881Speter if (wcroot == root_wcroot) 914251881Speter svn_hash_sets(db->dir_data, svn__apr_hash_index_key(hi), NULL); 915251881Speter } 916251881Speter 917251881Speter result = apr_pool_cleanup_run(db->state_pool, root_wcroot, close_wcroot); 918251881Speter if (result != APR_SUCCESS) 919251881Speter return svn_error_wrap_apr(result, NULL); 920251881Speter 921251881Speter return SVN_NO_ERROR; 922251881Speter} 923