2 * wc_db_wcroot.c : supporting datastructures for the administrative database
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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
21 * ====================================================================
24 #define SVN_WC__I_AM_WC_DB
28 #include "svn_dirent_uri.h"
31 #include "svn_version.h"
34 #include "adm_files.h"
35 #include "wc_db_private.h"
36 #include "wc-queries.h"
38 #include "svn_private_config.h"
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)
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. */
50 get_old_version(int *version,
52 apr_pool_t *scratch_pool)
55 const char *format_file_path;
58 /* Try reading the format number from the entries file. */
59 format_file_path = svn_wc__adm_child(abspath, SVN_WC__ADM_ENTRIES,
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)
71 err = svn_io_read_version_file(version, format_file_path, scratch_pool);
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));
81 /* This must be a really old working copy! Fall back to reading the
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,
89 err = svn_io_read_version_file(version, format_file_path, scratch_pool);
93 /* Whatever error may have occurred... we can just ignore. This is not
94 a working copy directory. Signal the caller. */
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.
105 This function may do strange things, but at long as it comes up with the
106 Right Answer, we should be happy. */
108 get_path_kind(svn_node_kind_t *kind,
110 const char *local_abspath,
111 apr_pool_t *scratch_pool)
113 svn_boolean_t special;
114 svn_node_kind_t node_kind;
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. */
120 if (db->parse_cache.abspath
121 && strcmp(db->parse_cache.abspath->data, local_abspath) == 0)
124 *kind = db->parse_cache.kind;
128 if (!db->parse_cache.abspath)
130 db->parse_cache.abspath = svn_stringbuf_create(local_abspath,
135 svn_stringbuf_set(db->parse_cache.abspath, local_abspath);
138 SVN_ERR(svn_io_check_special_path(local_abspath, &node_kind,
139 &special, scratch_pool));
141 db->parse_cache.kind = (special ? svn_node_symlink : node_kind);
142 *kind = db->parse_cache.kind;
148 /* Return an error if the work queue in SDB is non-empty. */
150 verify_no_work(svn_sqlite__db_t *sdb)
152 svn_sqlite__stmt_t *stmt;
153 svn_boolean_t have_row;
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));
160 return svn_error_create(SVN_ERR_WC_CLEANUP_REQUIRED, NULL,
161 NULL /* nothing to add. */);
169 close_wcroot(void *data)
171 svn_wc__db_wcroot_t *wcroot = data;
174 SVN_ERR_ASSERT_NO_RETURN(wcroot->sdb != NULL);
176 err = svn_sqlite__close(wcroot->sdb);
180 apr_status_t result = err->apr_err;
181 svn_error_clear(err);
190 svn_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)
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);
203 (*db)->state_pool = result_pool;
205 /* Don't need to initialize (*db)->parse_cache, due to the calloc above */
209 svn_boolean_t sqlite_exclusive = FALSE;
211 err = svn_config_get_bool(config, &sqlite_exclusive,
212 SVN_CONFIG_SECTION_WORKING_COPY,
213 SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE,
217 svn_error_clear(err);
220 (*db)->exclusive = sqlite_exclusive;
228 svn_wc__db_close(svn_wc__db_t *db)
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;
234 /* Collect all the unique WCROOT structures, and empty out DIR_DATA. */
235 for (hi = apr_hash_first(scratch_pool, db->dir_data);
237 hi = apr_hash_next(hi))
239 svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi);
240 const char *local_abspath = svn__apr_hash_index_key(hi);
243 svn_hash_sets(roots, wcroot->abspath, wcroot);
245 svn_hash_sets(db->dir_data, local_abspath, NULL);
248 /* Run the cleanup for each WCROOT. */
249 return svn_error_trace(svn_wc__db_close_many_wcroots(roots, db->state_pool,
255 svn_wc__db_pdh_create_wcroot(svn_wc__db_wcroot_t **wcroot,
256 const char *wcroot_abspath,
257 svn_sqlite__db_t *sdb,
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)
266 SVN_ERR(svn_sqlite__read_schema_version(&format, sdb, scratch_pool));
268 /* If we construct a wcroot, then we better have a format. */
269 SVN_ERR_ASSERT(format >= 1);
271 /* If this working copy is PRE-1.0, then simply bail out. */
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);
281 /* If this working copy is from a future version, then bail out. */
282 if (format > SVN_WC__VERSION)
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"
291 svn_dirent_local_style(wcroot_abspath, scratch_pool),
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)))
300 svn_error_t *err = verify_no_work(sdb);
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 "
310 return svn_error_trace(err);
314 /* Auto-upgrade the SDB if possible. */
315 if (format < SVN_WC__VERSION && verify_format)
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,
324 format, SVN_VERSION, SVN_WC__VERSION);
327 *wcroot = apr_palloc(result_pool, sizeof(**wcroot));
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
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);
339 /* SDB will be NULL for pre-NG working copies. We only need to run a
340 cleanup when the SDB is present. */
342 apr_pool_cleanup_register(result_pool, *wcroot, close_wcroot,
343 apr_pool_cleanup_null);
349 svn_wc__db_close_many_wcroots(apr_hash_t *roots,
350 apr_pool_t *state_pool,
351 apr_pool_t *scratch_pool)
353 apr_hash_index_t *hi;
355 for (hi = apr_hash_first(scratch_pool, roots); hi; hi = apr_hash_next(hi))
357 svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi);
360 result = apr_pool_cleanup_run(state_pool, wcroot, close_wcroot);
361 if (result != APR_SUCCESS)
362 return svn_error_wrap_apr(result, NULL);
369 /* POOL may be NULL if the lifetime of LOCAL_ABSPATH is sufficient. */
371 compute_relpath(const svn_wc__db_wcroot_t *wcroot,
372 const char *local_abspath,
373 apr_pool_t *result_pool)
375 const char *relpath = svn_dirent_is_child(wcroot->abspath, local_abspath,
383 /* Return in *LINK_TARGET_ABSPATH the absolute path the symlink at
384 * LOCAL_ABSPATH is pointing to. Perform all allocations in POOL. */
386 read_link_target(const char **link_target_abspath,
387 const char *local_abspath,
390 svn_string_t *link_target;
391 const char *canon_link_target;
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));
399 canon_link_target = svn_dirent_canonicalize(link_target->data, pool);
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,
405 canon_link_target, pool);
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,
412 *link_target_abspath = canon_link_target;
418 svn_wc__db_wcroot_parse_local_abspath(svn_wc__db_wcroot_t **wcroot,
419 const char **local_relpath,
421 const char *local_abspath,
422 apr_pool_t *result_pool,
423 apr_pool_t *scratch_pool)
425 const char *local_dir_abspath;
426 const char *original_abspath = local_abspath;
427 svn_node_kind_t kind;
428 const char *build_relpath;
429 svn_wc__db_wcroot_t *probe_wcroot;
430 svn_wc__db_wcroot_t *found_wcroot = NULL;
431 const char *scan_abspath;
432 svn_sqlite__db_t *sdb = NULL;
433 svn_boolean_t moved_upwards = FALSE;
434 svn_boolean_t always_check = FALSE;
436 const char *adm_relpath;
437 /* Non-NULL if WCROOT is found through a symlink: */
438 const char *symlink_wcroot_abspath = NULL;
440 /* ### we need more logic for finding the database (if it is located
441 ### outside of the wcroot) and then managing all of that within DB.
442 ### for now: play quick & dirty. */
444 probe_wcroot = svn_hash_gets(db->dir_data, local_abspath);
445 if (probe_wcroot != NULL)
447 *wcroot = probe_wcroot;
449 /* We got lucky. Just return the thing BEFORE performing any I/O. */
450 /* ### validate SMODE against how we opened wcroot->sdb? and against
451 ### DB->mode? (will we record per-dir mode?) */
453 /* ### for most callers, we could pass NULL for result_pool. */
454 *local_relpath = compute_relpath(probe_wcroot, local_abspath,
460 /* ### at some point in the future, we may need to find a way to get
461 ### rid of this stat() call. it is going to happen for EVERY call
462 ### into wc_db which references a file. calls for directories could
463 ### get an early-exit in the hash lookup just above. */
464 SVN_ERR(get_path_kind(&kind, db, local_abspath, scratch_pool));
465 if (kind != svn_node_dir)
467 /* If the node specified by the path is NOT present, then it cannot
468 possibly be a directory containing ".svn/wc.db".
470 If it is a file, then it cannot contain ".svn/wc.db".
472 For both of these cases, strip the basename off of the path and
473 move up one level. Keep record of what we strip, though, since
474 we'll need it later to construct local_relpath. */
475 svn_dirent_split(&local_dir_abspath, &build_relpath, local_abspath,
478 /* Is this directory in our hash? */
479 probe_wcroot = svn_hash_gets(db->dir_data, local_dir_abspath);
480 if (probe_wcroot != NULL)
482 const char *dir_relpath;
484 *wcroot = probe_wcroot;
486 /* Stashed directory's local_relpath + basename. */
487 dir_relpath = compute_relpath(probe_wcroot, local_dir_abspath,
489 *local_relpath = svn_relpath_join(dir_relpath,
495 /* If the requested path is not on the disk, then we don't know how
496 many ancestors need to be scanned until we start hitting content
497 on the disk. Set ALWAYS_CHECK to keep looking for .svn/entries
498 rather than bailing out after the first check. */
499 if (kind == svn_node_none)
502 /* Start the scanning at LOCAL_DIR_ABSPATH. */
503 local_abspath = local_dir_abspath;
507 /* Start the local_relpath empty. If *this* directory contains the
508 wc.db, then relpath will be the empty string. */
511 /* Remember the dir containing LOCAL_ABSPATH (they're the same). */
512 local_dir_abspath = local_abspath;
515 /* LOCAL_ABSPATH refers to a directory at this point. At this point,
516 we've determined that an associated WCROOT is NOT in the DB's hash
517 table for this directory. Let's find an existing one in the ancestors,
518 or create one when we find the actual wcroot. */
520 /* Assume that LOCAL_ABSPATH is a directory, and look for the SQLite
521 database in the right place. If we find it... great! If not, then
522 peel off some components, and try again. */
524 adm_relpath = svn_wc_get_adm_dir(scratch_pool);
528 svn_node_kind_t adm_subdir_kind;
530 const char *adm_subdir = svn_dirent_join(local_abspath, adm_relpath,
533 SVN_ERR(svn_io_check_path(adm_subdir, &adm_subdir_kind, scratch_pool));
535 if (adm_subdir_kind == svn_node_dir)
537 /* We always open the database in read/write mode. If the database
538 isn't writable in the filesystem, SQLite will internally open
539 it as read-only, and we'll get an error if we try to do a write
542 We could decide what to do on a per-operation basis, but since
543 we're caching database handles, it make sense to be as permissive
544 as the filesystem allows. */
545 err = svn_wc__db_util_open_db(&sdb, local_abspath, SDB_FILE,
546 svn_sqlite__mode_readwrite,
548 db->state_pool, scratch_pool);
552 /* Install self-verification trigger statements. */
553 err = svn_sqlite__exec_statements(sdb,
554 STMT_VERIFICATION_TRIGGERS);
555 if (err && err->apr_err == SVN_ERR_SQLITE_ERROR)
557 /* Verification triggers can fail to install on old 1.7-dev
558 * formats which didn't have a NODES table yet. Ignore sqlite
559 * errors so such working copies can be upgraded. */
560 svn_error_clear(err);
567 if (err->apr_err != SVN_ERR_SQLITE_ERROR
568 && !APR_STATUS_IS_ENOENT(err->apr_err))
569 return svn_error_trace(err);
570 svn_error_clear(err);
572 /* If we have not moved upwards, then check for a wc-1 working copy.
573 Since wc-1 has a .svn in every directory, and we didn't find one
574 in the original directory, then we aren't looking at a wc-1.
576 If the original path is not present, then we have to check on every
577 iteration. The content may be the immediate parent, or possibly
578 five ancetors higher. We don't test for directory presence (just
579 for the presence of subdirs/files), so we don't know when we can
580 stop checking ... so just check always. */
581 if (!moved_upwards || always_check)
583 SVN_ERR(get_old_version(&wc_format, local_abspath,
590 /* We couldn't open the SDB within the specified directory, so
591 move up one more directory. */
592 if (svn_dirent_is_root(local_abspath, strlen(local_abspath)))
594 /* Hit the root without finding a wcroot. */
596 /* The wcroot could be a symlink to a directory.
597 * (Issue #2557, #3987). If so, try again, this time scanning
598 * for a db within the directory the symlink points to,
599 * rather than within the symlink's parent directory. */
600 if (kind == svn_node_symlink)
602 svn_node_kind_t resolved_kind;
604 local_abspath = original_abspath;
606 SVN_ERR(svn_io_check_resolved_path(local_abspath,
609 if (resolved_kind == svn_node_dir)
611 /* Is this directory recorded in our hash? */
612 found_wcroot = svn_hash_gets(db->dir_data, local_abspath);
616 symlink_wcroot_abspath = local_abspath;
617 SVN_ERR(read_link_target(&local_abspath, local_abspath,
621 moved_upwards = FALSE;
622 local_dir_abspath = local_abspath;
629 return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
630 _("'%s' is not a working copy"),
631 svn_dirent_local_style(original_abspath,
635 local_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
637 moved_upwards = TRUE;
638 symlink_wcroot_abspath = NULL;
640 /* Is the parent directory recorded in our hash? */
641 found_wcroot = svn_hash_gets(db->dir_data, local_abspath);
642 if (found_wcroot != NULL)
646 if (found_wcroot != NULL)
648 /* We found a hash table entry for an ancestor, so we stopped scanning
649 since all subdirectories use the same WCROOT. */
650 *wcroot = found_wcroot;
652 else if (wc_format == 0)
654 /* We finally found the database. Construct a wcroot_t for it. */
659 err = svn_wc__db_util_fetch_wc_id(&wc_id, sdb, scratch_pool);
662 if (err->apr_err == SVN_ERR_WC_CORRUPT)
663 return svn_error_quick_wrap(
664 err, apr_psprintf(scratch_pool,
665 _("Missing a row in WCROOT for '%s'."),
666 svn_dirent_local_style(original_abspath,
668 return svn_error_trace(err);
671 /* WCROOT.local_abspath may be NULL when the database is stored
672 inside the wcroot, but we know the abspath is this directory
673 (ie. where we found it). */
675 err = svn_wc__db_pdh_create_wcroot(wcroot,
676 apr_pstrdup(db->state_pool,
677 symlink_wcroot_abspath
678 ? symlink_wcroot_abspath
680 sdb, wc_id, FORMAT_FROM_SDB,
681 db->verify_format, db->enforce_empty_wq,
682 db->state_pool, scratch_pool);
683 if (err && (err->apr_err == SVN_ERR_WC_UNSUPPORTED_FORMAT ||
684 err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED) &&
685 kind == svn_node_symlink)
687 /* We found an unsupported WC after traversing upwards from a
688 * symlink. Fall through to code below to check if the symlink
689 * points at a supported WC. */
690 svn_error_clear(err);
695 /* Close handle if we are not going to use it to support
696 upgrading with exclusive wc locking. */
697 return svn_error_compose_create(err, svn_sqlite__close(sdb));
702 /* We found something that looks like a wc-1 working copy directory.
703 However, if the format version is 12 and the .svn/entries file
704 is only 3 bytes long, then it's a breadcrumb in a wc-ng working
705 copy that's missing an .svn/wc.db, or its .svn/wc.db is corrupt. */
706 if (wc_format == SVN_WC__WC_NG_VERSION /* 12 */)
710 /* Check attributes of .svn/entries */
711 const char *admin_abspath = svn_wc__adm_child(
712 local_abspath, SVN_WC__ADM_ENTRIES, scratch_pool);
713 svn_error_t *err = svn_io_stat(&info, admin_abspath, APR_FINFO_SIZE,
716 /* If the former does not succeed, something is seriously wrong. */
718 return svn_error_createf(
719 SVN_ERR_WC_CORRUPT, err,
720 _("The working copy at '%s' is corrupt."),
721 svn_dirent_local_style(local_abspath, scratch_pool));
722 svn_error_clear(err);
726 /* Check existence of .svn/wc.db */
727 admin_abspath = svn_wc__adm_child(local_abspath, SDB_FILE,
729 err = svn_io_stat(&info, admin_abspath, APR_FINFO_SIZE,
731 if (err && APR_STATUS_IS_ENOENT(err->apr_err))
733 svn_error_clear(err);
734 return svn_error_createf(
735 SVN_ERR_WC_CORRUPT, NULL,
736 _("The working copy database at '%s' is missing."),
737 svn_dirent_local_style(local_abspath, scratch_pool));
740 /* We should never have reached this point in the code
741 if .svn/wc.db exists; therefore it's best to assume
743 return svn_error_createf(
744 SVN_ERR_WC_CORRUPT, err,
745 _("The working copy database at '%s' is corrupt."),
746 svn_dirent_local_style(local_abspath, scratch_pool));
750 SVN_ERR(svn_wc__db_pdh_create_wcroot(wcroot,
751 apr_pstrdup(db->state_pool,
752 symlink_wcroot_abspath
753 ? symlink_wcroot_abspath
755 NULL, UNKNOWN_WC_ID, wc_format,
756 db->verify_format, db->enforce_empty_wq,
757 db->state_pool, scratch_pool));
762 const char *dir_relpath;
764 if (symlink_wcroot_abspath)
766 /* The WCROOT was found through a symlink pointing at the root of
767 * the WC. Cache the WCROOT under the symlink's path. */
768 local_dir_abspath = symlink_wcroot_abspath;
771 /* The subdirectory's relpath is easily computed relative to the
772 wcroot that we just found. */
773 dir_relpath = compute_relpath(*wcroot, local_dir_abspath, NULL);
775 /* And the result local_relpath may include a filename. */
776 *local_relpath = svn_relpath_join(dir_relpath, build_relpath, result_pool);
779 if (kind == svn_node_symlink)
781 svn_boolean_t retry_if_dir = FALSE;
782 svn_wc__db_status_t status;
783 svn_boolean_t conflicted;
786 /* Check if the symlink is versioned or obstructs a versioned node
787 * in this DB -- in that case, use this wcroot. Else, if the symlink
788 * points to a directory, try to find a wcroot in that directory
793 err = svn_wc__db_read_info_internal(&status, NULL, NULL, NULL, NULL,
794 NULL, NULL, NULL, NULL, NULL,
795 NULL, NULL, NULL, NULL, NULL,
796 NULL, NULL, NULL, &conflicted,
797 NULL, NULL, NULL, NULL, NULL,
798 NULL, *wcroot, *local_relpath,
799 scratch_pool, scratch_pool);
802 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
803 && !SVN_WC__ERR_IS_NOT_CURRENT_WC(err))
804 return svn_error_trace(err);
806 svn_error_clear(err);
807 retry_if_dir = TRUE; /* The symlink is unversioned. */
811 /* The symlink is versioned, or obstructs a versioned node.
812 * Ignore non-conflicted not-present/excluded nodes.
813 * This allows the symlink to redirect the wcroot query to a
814 * directory, regardless of 'invisible' nodes in this WC. */
815 retry_if_dir = ((status == svn_wc__db_status_not_present ||
816 status == svn_wc__db_status_excluded ||
817 status == svn_wc__db_status_server_excluded)
826 svn_node_kind_t resolved_kind;
828 SVN_ERR(svn_io_check_resolved_path(original_abspath,
831 if (resolved_kind == svn_node_dir)
833 symlink_wcroot_abspath = original_abspath;
834 SVN_ERR(read_link_target(&local_abspath, original_abspath,
836 /* This handle was opened in this function but is not going
837 to be used further so close it. */
839 SVN_ERR(svn_sqlite__close(sdb));
840 goto try_symlink_as_dir;
845 /* We've found the appropriate WCROOT for the requested path. Stash
846 it into that path's directory. */
847 svn_hash_sets(db->dir_data,
848 apr_pstrdup(db->state_pool, local_dir_abspath),
851 /* Did we traverse up to parent directories? */
854 /* We did NOT move to a parent of the original requested directory.
855 We've constructed and filled in a WCROOT for the request, so we
860 /* The WCROOT that we just found/built was for the LOCAL_ABSPATH originally
861 passed into this function. We stepped *at least* one directory above that.
862 We should now associate the WROOT for each parent directory that does
863 not (yet) have one. */
865 scan_abspath = local_dir_abspath;
869 const char *parent_dir = svn_dirent_dirname(scan_abspath, scratch_pool);
870 svn_wc__db_wcroot_t *parent_wcroot;
872 parent_wcroot = svn_hash_gets(db->dir_data, parent_dir);
873 if (parent_wcroot == NULL)
875 svn_hash_sets(db->dir_data, apr_pstrdup(db->state_pool, parent_dir),
879 /* Move up a directory, stopping when we reach the directory where
880 we found/built the WCROOT. */
881 scan_abspath = parent_dir;
883 while (strcmp(scan_abspath, local_abspath) != 0);
890 svn_wc__db_drop_root(svn_wc__db_t *db,
891 const char *local_abspath,
892 apr_pool_t *scratch_pool)
894 svn_wc__db_wcroot_t *root_wcroot = svn_hash_gets(db->dir_data, local_abspath);
895 apr_hash_index_t *hi;
901 if (strcmp(root_wcroot->abspath, local_abspath) != 0)
902 return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
903 _("'%s' is not a working copy root"),
904 svn_dirent_local_style(local_abspath,
907 for (hi = apr_hash_first(scratch_pool, db->dir_data);
909 hi = apr_hash_next(hi))
911 svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi);
913 if (wcroot == root_wcroot)
914 svn_hash_sets(db->dir_data, svn__apr_hash_index_key(hi), NULL);
917 result = apr_pool_cleanup_run(db->state_pool, root_wcroot, close_wcroot);
918 if (result != APR_SUCCESS)
919 return svn_error_wrap_apr(result, NULL);