2 * lock.c: routines for locking working copy subdirectories.
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_DEPRECATED
26 #include <apr_pools.h>
29 #include "svn_pools.h"
30 #include "svn_dirent_uri.h"
32 #include "svn_sorts.h"
34 #include "svn_types.h"
37 #include "adm_files.h"
42 #include "svn_private_config.h"
43 #include "private/svn_wc_private.h"
48 struct svn_wc_adm_access_t
50 /* PATH to directory which contains the administrative area */
53 /* And the absolute form of the path. */
56 /* Indicates that the baton has been closed. */
59 /* Handle to the administrative database. */
62 /* Was the DB provided to us? If so, then we'll never close it. */
63 svn_boolean_t db_provided;
65 /* ENTRIES_HIDDEN is all cached entries including those in
66 state deleted or state absent. It may be NULL. */
67 apr_hash_t *entries_all;
69 /* POOL is used to allocate cached items, they need to persist for the
70 lifetime of this access baton */
76 /* This is a placeholder used in the set hash to represent missing
77 directories. Only its address is important, it contains no useful
79 static const svn_wc_adm_access_t missing = { 0 };
80 #define IS_MISSING(lock) ((lock) == &missing)
82 /* ### hack for now. future functionality coming in a future revision. */
83 #define svn_wc__db_is_closed(db) FALSE
87 svn_wc__internal_check_wc(int *wc_format,
89 const char *local_abspath,
90 svn_boolean_t check_path,
91 apr_pool_t *scratch_pool)
95 err = svn_wc__db_temp_get_format(wc_format, db, local_abspath, scratch_pool);
100 if (err->apr_err != SVN_ERR_WC_MISSING &&
101 err->apr_err != SVN_ERR_WC_UNSUPPORTED_FORMAT &&
102 err->apr_err != SVN_ERR_WC_UPGRADE_REQUIRED)
103 return svn_error_trace(err);
104 svn_error_clear(err);
106 /* ### the stuff below seems to be redundant. get_format() probably
109 ### investigate all callers. DEFINITELY keep in mind the
110 ### svn_wc_check_wc() entrypoint.
113 /* If the format file does not exist or path not directory, then for
114 our purposes this is not a working copy, so return 0. */
117 /* Check path itself exists. */
118 SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
119 if (kind == svn_node_none)
121 return svn_error_createf(APR_ENOENT, NULL, _("'%s' does not exist"),
122 svn_dirent_local_style(local_abspath,
127 if (*wc_format >= SVN_WC__WC_NG_VERSION)
129 svn_wc__db_status_t db_status;
130 svn_node_kind_t db_kind;
134 /* If a node is not a directory, it is not a working copy
135 directory. This allows creating new working copies as
136 a path below an existing working copy. */
137 svn_node_kind_t wc_kind;
139 SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool));
140 if (wc_kind != svn_node_dir)
142 *wc_format = 0; /* Not a directory, so not a wc-directory */
147 err = svn_wc__db_read_info(&db_status, &db_kind, NULL, NULL, NULL,
148 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
149 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
150 NULL, NULL, NULL, NULL, NULL,
153 scratch_pool, scratch_pool);
155 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
157 svn_error_clear(err);
164 if (db_kind != svn_node_dir)
166 /* The WC thinks there must be a file, so this is not
174 case svn_wc__db_status_not_present:
175 case svn_wc__db_status_server_excluded:
176 case svn_wc__db_status_excluded:
177 /* If there is a directory here, it is not related to the parent
178 working copy: Obstruction */
191 svn_wc_check_wc2(int *wc_format,
192 svn_wc_context_t *wc_ctx,
193 const char *local_abspath,
194 apr_pool_t *scratch_pool)
196 /* ### Should we pass TRUE for check_path to find obstructions and
197 missing directories? */
198 return svn_error_trace(
199 svn_wc__internal_check_wc(wc_format, wc_ctx->db, local_abspath, FALSE,
206 add_to_shared(svn_wc_adm_access_t *lock, apr_pool_t *scratch_pool)
208 /* ### sometimes we replace &missing with a now-valid lock. */
210 svn_wc_adm_access_t *prior = svn_wc__db_temp_get_access(lock->db,
213 if (IS_MISSING(prior))
214 SVN_ERR(svn_wc__db_temp_close_access(lock->db, lock->abspath,
215 prior, scratch_pool));
218 svn_wc__db_temp_set_access(lock->db, lock->abspath, lock,
226 static svn_wc_adm_access_t *
227 get_from_shared(const char *abspath,
229 apr_pool_t *scratch_pool)
231 /* We closed the DB when it became empty. ABSPATH is not present. */
234 return svn_wc__db_temp_get_access(db, abspath, scratch_pool);
240 close_single(svn_wc_adm_access_t *adm_access,
241 svn_boolean_t preserve_lock,
242 apr_pool_t *scratch_pool)
244 svn_boolean_t locked;
246 if (adm_access->closed)
249 /* Physically unlock if required */
250 SVN_ERR(svn_wc__db_wclock_owns_lock(&locked, adm_access->db,
251 adm_access->abspath, TRUE,
257 /* Remove the physical lock in the admin directory for
258 PATH. It is acceptable for the administrative area to
259 have disappeared, such as when the directory is removed
260 from the working copy. It is an error for the lock to
261 have disappeared if the administrative area still exists. */
263 svn_error_t *err = svn_wc__db_wclock_release(adm_access->db,
268 if (svn_wc__adm_area_exists(adm_access->abspath, scratch_pool))
270 svn_error_clear(err);
275 /* Reset to prevent further use of the lock. */
276 adm_access->closed = TRUE;
278 /* Detach from set */
279 SVN_ERR(svn_wc__db_temp_close_access(adm_access->db, adm_access->abspath,
280 adm_access, scratch_pool));
282 /* Possibly close the underlying wc_db. */
283 if (!adm_access->db_provided)
285 apr_hash_t *opened = svn_wc__db_temp_get_all_access(adm_access->db,
287 if (apr_hash_count(opened) == 0)
289 SVN_ERR(svn_wc__db_close(adm_access->db));
290 adm_access->db = NULL;
298 /* Cleanup for a locked access baton.
300 This handles closing access batons when their pool gets destroyed.
301 The physical locks associated with such batons remain in the working
302 copy if they are protecting work items in the workqueue. */
304 pool_cleanup_locked(void *p)
306 svn_wc_adm_access_t *lock = p;
308 svn_skel_t *work_item;
314 /* If the DB is closed, then we have a bunch of extra work to do. */
315 if (svn_wc__db_is_closed(lock->db))
317 apr_pool_t *scratch_pool;
322 /* If there is no ADM area, then we definitely have no work items
323 or physical locks to worry about. Bail out. */
324 if (!svn_wc__adm_area_exists(lock->abspath, lock->pool))
327 /* Creating a subpool is safe within a pool cleanup, as long as
328 we're absolutely sure to destroy it before we exit this function.
330 We avoid using LOCK->POOL to keep the following functions from
331 hanging cleanups or subpools from it. (the cleanups *might* get
332 run, but the subpools will NOT be destroyed) */
333 scratch_pool = svn_pool_create(lock->pool);
335 err = svn_wc__db_open(&db, NULL /* ### config. need! */, FALSE, TRUE,
336 scratch_pool, scratch_pool);
339 err = svn_wc__db_wq_fetch_next(&id, &work_item, db, lock->abspath, 0,
340 scratch_pool, scratch_pool);
341 if (!err && work_item == NULL)
343 /* There is no remaining work, so we're good to remove any
344 potential "physical" lock. */
345 err = svn_wc__db_wclock_release(db, lock->abspath, scratch_pool);
348 svn_error_clear(err);
350 /* Closes the DB, too. */
351 svn_pool_destroy(scratch_pool);
356 /* ### should we create an API that just looks, but doesn't return? */
357 err = svn_wc__db_wq_fetch_next(&id, &work_item, lock->db, lock->abspath, 0,
358 lock->pool, lock->pool);
360 /* Close just this access baton. The pool cleanup will close the rest. */
362 err = close_single(lock,
363 work_item != NULL /* preserve_lock */,
368 apr_status_t apr_err = err->apr_err;
369 svn_error_clear(err);
377 /* Cleanup for a readonly access baton. */
379 pool_cleanup_readonly(void *data)
381 svn_wc_adm_access_t *lock = data;
387 /* If the DB is closed, then we have nothing to do. There are no
388 "physical" locks to remove, and we don't care whether this baton
389 is registered with the DB. */
390 if (svn_wc__db_is_closed(lock->db))
393 /* Close this baton. No lock to preserve. Since this is part of the
394 pool cleanup, we don't need to close children -- the cleanup process
395 will close all children. */
396 err = close_single(lock, FALSE /* preserve_lock */, lock->pool);
399 apr_status_t result = err->apr_err;
400 svn_error_clear(err);
408 /* An APR pool cleanup handler. This is a child handler, it removes the
409 main pool handler. */
411 pool_cleanup_child(void *p)
413 svn_wc_adm_access_t *lock = p;
415 apr_pool_cleanup_kill(lock->pool, lock, pool_cleanup_locked);
416 apr_pool_cleanup_kill(lock->pool, lock, pool_cleanup_readonly);
422 /* Allocate from POOL, initialise and return an access baton. TYPE and PATH
423 are used to initialise the baton. If STEAL_LOCK, steal the lock if path
426 adm_access_alloc(svn_wc_adm_access_t **adm_access,
429 svn_boolean_t db_provided,
430 svn_boolean_t write_lock,
431 apr_pool_t *result_pool,
432 apr_pool_t *scratch_pool)
435 svn_wc_adm_access_t *lock = apr_palloc(result_pool, sizeof(*lock));
437 lock->closed = FALSE;
438 lock->entries_all = NULL;
440 lock->db_provided = db_provided;
441 lock->path = apr_pstrdup(result_pool, path);
442 lock->pool = result_pool;
444 SVN_ERR(svn_dirent_get_absolute(&lock->abspath, path, result_pool));
450 svn_boolean_t owns_lock;
452 /* If the db already owns a lock, we can't add an extra lock record */
453 SVN_ERR(svn_wc__db_wclock_owns_lock(&owns_lock, db, path, FALSE,
456 /* If DB owns the lock, but when there is no access baton open for this
457 directory, old access baton based code is trying to access data that
458 was previously locked by new code. Just hand them the lock, or
459 important code paths like svn_wc_add3() will start failing */
461 || svn_wc__adm_retrieve_internal2(db, lock->abspath, scratch_pool))
463 SVN_ERR(svn_wc__db_wclock_obtain(db, lock->abspath, 0, FALSE,
468 err = add_to_shared(lock, scratch_pool);
471 return svn_error_compose_create(
473 svn_wc__db_wclock_release(db, lock->abspath, scratch_pool));
475 /* ### does this utf8 thing really/still apply?? */
476 /* It's important that the cleanup handler is registered *after* at least
477 one UTF8 conversion has been done, since such a conversion may create
478 the apr_xlate_t object in the pool, and that object must be around
479 when the cleanup handler runs. If the apr_xlate_t cleanup handler
480 were to run *before* the access baton cleanup handler, then the access
481 baton's handler won't work. */
483 /* Register an appropriate cleanup handler, based on the whether this
484 access baton is locked or not. */
485 apr_pool_cleanup_register(lock->pool, lock,
487 ? pool_cleanup_locked
488 : pool_cleanup_readonly,
497 probe(svn_wc__db_t *db,
502 svn_node_kind_t kind;
505 SVN_ERR(svn_io_check_path(path, &kind, pool));
506 if (kind == svn_node_dir)
508 const char *local_abspath;
510 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
511 SVN_ERR(svn_wc__internal_check_wc(&wc_format, db, local_abspath,
515 /* a "version" of 0 means a non-wc directory */
516 if (kind != svn_node_dir || wc_format == 0)
518 /* Passing a path ending in "." or ".." to svn_dirent_dirname() is
519 probably always a bad idea; certainly it is in this case.
520 Unfortunately, svn_dirent_dirname()'s current signature can't
521 return an error, so we have to insert the protection in this
522 caller, ideally the API needs a change. See issue #1617. */
523 const char *base_name = svn_dirent_basename(path, pool);
524 if ((strcmp(base_name, "..") == 0)
525 || (strcmp(base_name, ".") == 0))
527 return svn_error_createf
528 (SVN_ERR_WC_BAD_PATH, NULL,
529 _("Path '%s' ends in '%s', "
530 "which is unsupported for this operation"),
531 svn_dirent_local_style(path, pool), base_name);
534 *dir = svn_dirent_dirname(path, pool);
545 open_single(svn_wc_adm_access_t **adm_access,
547 svn_boolean_t write_lock,
549 svn_boolean_t db_provided,
550 apr_pool_t *result_pool,
551 apr_pool_t *scratch_pool)
553 const char *local_abspath;
556 svn_wc_adm_access_t *lock;
558 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
559 err = svn_wc__internal_check_wc(&wc_format, db, local_abspath, FALSE,
561 if (wc_format == 0 || (err && APR_STATUS_IS_ENOENT(err->apr_err)))
563 return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, err,
564 _("'%s' is not a working copy"),
565 svn_dirent_local_style(path, scratch_pool));
569 /* The format version must match exactly. Note that wc_db will perform
570 an auto-upgrade if allowed. If it does *not*, then it has decided a
571 manual upgrade is required and it should have raised an error. */
572 SVN_ERR_ASSERT(wc_format == SVN_WC__VERSION);
574 /* Need to create a new lock */
575 SVN_ERR(adm_access_alloc(&lock, path, db, db_provided, write_lock,
576 result_pool, scratch_pool));
578 /* ### recurse was here */
585 /* Retrieves the KIND of LOCAL_ABSPATH and whether its administrative data is
586 available in the working copy.
588 *AVAILABLE is set to TRUE when the node and its metadata are available,
589 otherwise to FALSE (due to obstruction, missing, absence, exclusion,
590 or a "not-present" child).
594 ### note: this function should go away when we move to a single
595 ### adminstrative area. */
597 adm_available(svn_boolean_t *available,
598 svn_node_kind_t *kind,
600 const char *local_abspath,
601 apr_pool_t *scratch_pool)
603 svn_wc__db_status_t status;
606 *kind = svn_node_unknown;
608 SVN_ERR(svn_wc__db_read_info(&status, kind, NULL, NULL, NULL, NULL, NULL,
609 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
610 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
611 NULL, NULL, NULL, NULL, NULL, NULL,
612 db, local_abspath, scratch_pool, scratch_pool));
614 *available = !(status == svn_wc__db_status_server_excluded
615 || status == svn_wc__db_status_excluded
616 || status == svn_wc__db_status_not_present);
620 /* This is essentially the guts of svn_wc_adm_open3.
622 * If the working copy is already locked, return SVN_ERR_WC_LOCKED; if
623 * it is not a versioned directory, return SVN_ERR_WC_NOT_WORKING_COPY.
626 do_open(svn_wc_adm_access_t **adm_access,
629 svn_boolean_t db_provided,
630 apr_array_header_t *rollback,
631 svn_boolean_t write_lock,
633 svn_cancel_func_t cancel_func,
635 apr_pool_t *result_pool,
636 apr_pool_t *scratch_pool)
638 svn_wc_adm_access_t *lock;
639 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
641 SVN_ERR(open_single(&lock, path, write_lock, db, db_provided,
642 result_pool, iterpool));
644 /* Add self to the rollback list in case of error. */
645 APR_ARRAY_PUSH(rollback, svn_wc_adm_access_t *) = lock;
647 if (levels_to_lock != 0)
649 const apr_array_header_t *children;
650 const char *local_abspath = svn_wc__adm_access_abspath(lock);
653 /* Reduce levels_to_lock since we are about to recurse */
654 if (levels_to_lock > 0)
657 SVN_ERR(svn_wc__db_read_children(&children, db, local_abspath,
658 scratch_pool, iterpool));
661 for (i = 0; i < children->nelts; i++)
663 const char *node_abspath;
664 svn_node_kind_t kind;
665 svn_boolean_t available;
666 const char *name = APR_ARRAY_IDX(children, i, const char *);
668 svn_pool_clear(iterpool);
670 /* See if someone wants to cancel this operation. */
672 SVN_ERR(cancel_func(cancel_baton));
674 node_abspath = svn_dirent_join(local_abspath, name, iterpool);
676 SVN_ERR(adm_available(&available,
682 if (kind != svn_node_dir)
687 const char *node_path = svn_dirent_join(path, name, iterpool);
688 svn_wc_adm_access_t *node_access;
690 SVN_ERR(do_open(&node_access, node_path, db, db_provided,
691 rollback, write_lock, levels_to_lock,
692 cancel_func, cancel_baton,
693 lock->pool, iterpool));
694 /* node_access has been registered in DB, so we don't need
695 to do anything with it. */
699 svn_pool_destroy(iterpool);
709 open_all(svn_wc_adm_access_t **adm_access,
712 svn_boolean_t db_provided,
713 svn_boolean_t write_lock,
715 svn_cancel_func_t cancel_func,
719 apr_array_header_t *rollback;
722 rollback = apr_array_make(pool, 10, sizeof(svn_wc_adm_access_t *));
724 err = do_open(adm_access, path, db, db_provided, rollback,
725 write_lock, levels_to_lock,
726 cancel_func, cancel_baton, pool, pool);
731 for (i = rollback->nelts; i--; )
733 svn_wc_adm_access_t *lock = APR_ARRAY_IDX(rollback, i,
734 svn_wc_adm_access_t *);
735 SVN_ERR_ASSERT(!IS_MISSING(lock));
737 svn_error_clear(close_single(lock, FALSE /* preserve_lock */, pool));
741 return svn_error_trace(err);
746 svn_wc_adm_open3(svn_wc_adm_access_t **adm_access,
747 svn_wc_adm_access_t *associated,
749 svn_boolean_t write_lock,
751 svn_cancel_func_t cancel_func,
756 svn_boolean_t db_provided;
758 /* Make sure that ASSOCIATED has a set of access batons, so that we can
759 glom a reference to self into it. */
763 svn_wc_adm_access_t *lock;
765 SVN_ERR(svn_dirent_get_absolute(&abspath, path, pool));
766 lock = get_from_shared(abspath, associated->db, pool);
767 if (lock && !IS_MISSING(lock))
768 /* Already locked. The reason we don't return the existing baton
769 here is that the user is supposed to know whether a directory is
770 locked: if it's not locked call svn_wc_adm_open, if it is locked
771 call svn_wc_adm_retrieve. */
772 return svn_error_createf(SVN_ERR_WC_LOCKED, NULL,
773 _("Working copy '%s' locked"),
774 svn_dirent_local_style(path, pool));
776 db_provided = associated->db_provided;
780 /* Any baton creation is going to need a shared structure for holding
781 data across the entire set. The caller isn't providing one, so we
783 /* ### we could optimize around levels_to_lock==0, but much of this
784 ### is going to be simplified soon anyways. */
785 SVN_ERR(svn_wc__db_open(&db, NULL /* ### config. need! */, FALSE, TRUE,
790 return svn_error_trace(open_all(adm_access, path, db, db_provided,
791 write_lock, levels_to_lock,
792 cancel_func, cancel_baton, pool));
797 svn_wc_adm_probe_open3(svn_wc_adm_access_t **adm_access,
798 svn_wc_adm_access_t *associated,
800 svn_boolean_t write_lock,
802 svn_cancel_func_t cancel_func,
809 if (associated == NULL)
813 /* Ugh. Too bad about having to open a DB. */
814 SVN_ERR(svn_wc__db_open(&db,
815 NULL /* ### config */, FALSE, TRUE, pool, pool));
816 err = probe(db, &dir, path, pool);
817 svn_error_clear(svn_wc__db_close(db));
822 SVN_ERR(probe(associated->db, &dir, path, pool));
825 /* If we moved up a directory, then the path is not a directory, or it
826 is not under version control. In either case, the notion of
827 levels_to_lock does not apply to the provided path. Disable it so
828 that we don't end up trying to lock more than we need. */
832 err = svn_wc_adm_open3(adm_access, associated, dir, write_lock,
833 levels_to_lock, cancel_func, cancel_baton, pool);
838 /* If we got an error on the parent dir, that means we failed to
839 get an access baton for the child in the first place. And if
840 the reason we couldn't get the child access baton is that the
841 child is not a versioned directory, then return an error
842 about the child, not the parent. */
843 svn_node_kind_t child_kind;
844 if ((err2 = svn_io_check_path(path, &child_kind, pool)))
846 svn_error_compose(err, err2);
851 && (child_kind == svn_node_dir)
852 && (err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY))
854 svn_error_clear(err);
855 return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
856 _("'%s' is not a working copy"),
857 svn_dirent_local_style(path, pool));
867 svn_wc_adm_access_t *
868 svn_wc__adm_retrieve_internal2(svn_wc__db_t *db,
870 apr_pool_t *scratch_pool)
872 svn_wc_adm_access_t *adm_access = get_from_shared(abspath, db, scratch_pool);
874 /* If the entry is marked as "missing", then return nothing. */
875 if (IS_MISSING(adm_access))
884 svn_wc_adm_retrieve(svn_wc_adm_access_t **adm_access,
885 svn_wc_adm_access_t *associated,
889 const char *local_abspath;
890 svn_node_kind_t kind = svn_node_unknown;
891 svn_node_kind_t wckind;
894 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
896 if (strcmp(associated->path, path) == 0)
897 *adm_access = associated;
899 *adm_access = svn_wc__adm_retrieve_internal2(associated->db, local_abspath,
902 /* We found what we're looking for, so bail. */
906 /* Most of the code expects access batons to exist, so returning an error
907 generally makes the calling code simpler as it doesn't need to check
909 /* We are going to send a SVN_ERR_WC_NOT_LOCKED, but let's provide
910 a bit more information to our caller */
912 err = svn_io_check_path(path, &wckind, pool);
914 /* If we can't check the path, we can't make a good error message. */
917 return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, err,
918 _("Unable to check path existence for '%s'"),
919 svn_dirent_local_style(path, pool));
924 err = svn_wc__db_read_kind(&kind, svn_wc__adm_get_db(associated),
926 TRUE /* allow_missing */,
927 TRUE /* show_deleted */,
928 FALSE /* show_hidden */, pool);
932 kind = svn_node_unknown;
933 svn_error_clear(err);
937 if (kind == svn_node_dir && wckind == svn_node_file)
939 err = svn_error_createf(
940 SVN_ERR_WC_NOT_WORKING_COPY, NULL,
941 _("Expected '%s' to be a directory but found a file"),
942 svn_dirent_local_style(path, pool));
944 return svn_error_create(SVN_ERR_WC_NOT_LOCKED, err, err->message);
947 if (kind != svn_node_dir && kind != svn_node_unknown)
949 err = svn_error_createf(
950 SVN_ERR_WC_NOT_WORKING_COPY, NULL,
951 _("Can't retrieve an access baton for non-directory '%s'"),
952 svn_dirent_local_style(path, pool));
954 return svn_error_create(SVN_ERR_WC_NOT_LOCKED, err, err->message);
957 if (kind == svn_node_unknown || wckind == svn_node_none)
959 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
960 _("Directory '%s' is missing"),
961 svn_dirent_local_style(path, pool));
963 return svn_error_create(SVN_ERR_WC_NOT_LOCKED, err, err->message);
966 /* If all else fails, return our useless generic error. */
967 return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
968 _("Working copy '%s' is not locked"),
969 svn_dirent_local_style(path, pool));
975 svn_wc_adm_probe_retrieve(svn_wc_adm_access_t **adm_access,
976 svn_wc_adm_access_t *associated,
981 const char *local_abspath;
982 svn_node_kind_t kind;
985 SVN_ERR_ASSERT(associated != NULL);
987 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
988 SVN_ERR(svn_wc__db_read_kind(&kind, associated->db, local_abspath,
989 TRUE /* allow_missing */,
990 TRUE /* show_deleted */,
991 FALSE /* show_hidden*/,
994 if (kind == svn_node_dir)
996 else if (kind != svn_node_unknown)
997 dir = svn_dirent_dirname(path, pool);
999 /* Not a versioned item, probe it */
1000 SVN_ERR(probe(associated->db, &dir, path, pool));
1002 err = svn_wc_adm_retrieve(adm_access, associated, dir, pool);
1003 if (err && err->apr_err == SVN_ERR_WC_NOT_LOCKED)
1005 /* We'll receive a NOT LOCKED error for various reasons,
1006 including the reason we'll actually want to test for:
1007 The path is a versioned directory, but missing, in which case
1008 we want its parent's adm_access (which holds minimal data
1010 svn_error_clear(err);
1011 SVN_ERR(probe(associated->db, &dir, path, pool));
1012 SVN_ERR(svn_wc_adm_retrieve(adm_access, associated, dir, pool));
1015 return svn_error_trace(err);
1017 return SVN_NO_ERROR;
1021 /* SVN_DEPRECATED */
1023 svn_wc_adm_probe_try3(svn_wc_adm_access_t **adm_access,
1024 svn_wc_adm_access_t *associated,
1026 svn_boolean_t write_lock,
1028 svn_cancel_func_t cancel_func,
1034 err = svn_wc_adm_probe_retrieve(adm_access, associated, path, pool);
1036 /* SVN_ERR_WC_NOT_LOCKED would mean there was no access baton for
1037 path in associated, in which case we want to open an access
1038 baton and add it to associated. */
1039 if (err && (err->apr_err == SVN_ERR_WC_NOT_LOCKED))
1041 svn_error_clear(err);
1042 err = svn_wc_adm_probe_open3(adm_access, associated,
1043 path, write_lock, levels_to_lock,
1044 cancel_func, cancel_baton,
1045 svn_wc_adm_access_pool(associated));
1047 /* If the path is not a versioned directory, we just return a
1048 null access baton with no error. Note that of the errors we
1049 do report, the most important (and probably most likely) is
1050 SVN_ERR_WC_LOCKED. That error would mean that someone else
1051 has this area locked, and we definitely want to bail in that
1053 if (err && (err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY))
1055 svn_error_clear(err);
1066 static svn_error_t *
1067 child_is_disjoint(svn_boolean_t *disjoint,
1069 const char *local_abspath,
1070 apr_pool_t *scratch_pool)
1072 svn_boolean_t is_switched;
1074 /* Check if the parent directory knows about this node */
1075 SVN_ERR(svn_wc__db_is_switched(disjoint, &is_switched, NULL,
1076 db, local_abspath, scratch_pool));
1079 return SVN_NO_ERROR;
1084 return SVN_NO_ERROR;
1088 static svn_error_t *
1089 open_anchor(svn_wc_adm_access_t **anchor_access,
1090 svn_wc_adm_access_t **target_access,
1091 const char **target,
1093 svn_boolean_t db_provided,
1095 svn_boolean_t write_lock,
1097 svn_cancel_func_t cancel_func,
1101 const char *base_name = svn_dirent_basename(path, pool);
1103 /* Any baton creation is going to need a shared structure for holding
1104 data across the entire set. The caller isn't providing one, so we
1106 /* ### we could maybe skip the shared struct for levels_to_lock==0, but
1107 ### given that we need DB for format detection, may as well keep this.
1108 ### in any case, much of this is going to be simplified soon anyways. */
1110 SVN_ERR(svn_wc__db_open(&db, NULL, /* ### config. need! */ FALSE, TRUE,
1113 if (svn_path_is_empty(path)
1114 || svn_dirent_is_root(path, strlen(path))
1115 || ! strcmp(base_name, ".."))
1117 SVN_ERR(open_all(anchor_access, path, db, db_provided,
1118 write_lock, levels_to_lock,
1119 cancel_func, cancel_baton, pool));
1120 *target_access = *anchor_access;
1126 svn_wc_adm_access_t *p_access = NULL;
1127 svn_wc_adm_access_t *t_access = NULL;
1128 const char *parent = svn_dirent_dirname(path, pool);
1129 const char *local_abspath;
1130 svn_error_t *p_access_err = SVN_NO_ERROR;
1132 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
1134 /* Try to open parent of PATH to setup P_ACCESS */
1135 err = open_single(&p_access, parent, write_lock, db, db_provided,
1139 const char *abspath = svn_dirent_dirname(local_abspath, pool);
1140 svn_wc_adm_access_t *existing_adm = svn_wc__db_temp_get_access(db, abspath, pool);
1142 if (IS_MISSING(existing_adm))
1143 svn_wc__db_temp_clear_access(db, abspath, pool);
1145 SVN_ERR_ASSERT(existing_adm == NULL);
1147 if (err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY)
1149 svn_error_clear(err);
1152 else if (write_lock && (err->apr_err == SVN_ERR_WC_LOCKED
1153 || APR_STATUS_IS_EACCES(err->apr_err)))
1155 /* If P_ACCESS isn't to be returned then a read-only baton
1156 will do for now, but keep the error in case we need it. */
1157 svn_error_t *err2 = open_single(&p_access, parent, FALSE,
1158 db, db_provided, pool, pool);
1161 svn_error_clear(err2);
1170 /* Try to open PATH to setup T_ACCESS */
1171 err = open_all(&t_access, path, db, db_provided, write_lock,
1172 levels_to_lock, cancel_func, cancel_baton, pool);
1175 if (p_access == NULL)
1177 /* Couldn't open the parent or the target. Bail out. */
1178 svn_error_clear(p_access_err);
1179 return svn_error_trace(err);
1182 if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
1185 svn_error_clear(svn_wc_adm_close2(p_access, pool));
1186 svn_error_clear(p_access_err);
1187 return svn_error_trace(err);
1190 /* This directory is not under version control. Ignore it. */
1191 svn_error_clear(err);
1195 /* At this stage might have P_ACCESS, T_ACCESS or both */
1197 /* Check for switched or disjoint P_ACCESS and T_ACCESS */
1198 if (p_access && t_access)
1200 svn_boolean_t disjoint;
1202 err = child_is_disjoint(&disjoint, db, local_abspath, pool);
1205 svn_error_clear(p_access_err);
1206 svn_error_clear(svn_wc_adm_close2(p_access, pool));
1207 svn_error_clear(svn_wc_adm_close2(t_access, pool));
1208 return svn_error_trace(err);
1213 /* Switched or disjoint, so drop P_ACCESS. Don't close any
1214 descendants, or we might blast the child. */
1215 err = close_single(p_access, FALSE /* preserve_lock */, pool);
1218 svn_error_clear(p_access_err);
1219 svn_error_clear(svn_wc_adm_close2(t_access, pool));
1220 return svn_error_trace(err);
1226 /* We have a parent baton *and* we have an error related to opening
1227 the baton. That means we have a readonly baton, but that isn't
1228 going to work for us. (p_access would have been set to NULL if
1229 a writable parent baton is not required) */
1230 if (p_access && p_access_err)
1233 svn_error_clear(svn_wc_adm_close2(t_access, pool));
1234 svn_error_clear(svn_wc_adm_close2(p_access, pool));
1235 return svn_error_trace(p_access_err);
1237 svn_error_clear(p_access_err);
1241 svn_boolean_t available;
1242 svn_node_kind_t kind;
1244 err = adm_available(&available, &kind, db, local_abspath, pool);
1246 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
1247 svn_error_clear(err);
1250 svn_error_clear(svn_wc_adm_close2(p_access, pool));
1251 return svn_error_trace(err);
1255 *anchor_access = p_access ? p_access : t_access;
1256 *target_access = t_access ? t_access : p_access;
1261 *target = base_name;
1264 return SVN_NO_ERROR;
1269 svn_wc_adm_open_anchor(svn_wc_adm_access_t **anchor_access,
1270 svn_wc_adm_access_t **target_access,
1271 const char **target,
1273 svn_boolean_t write_lock,
1275 svn_cancel_func_t cancel_func,
1279 return svn_error_trace(open_anchor(anchor_access, target_access, target,
1280 NULL, FALSE, path, write_lock,
1281 levels_to_lock, cancel_func,
1282 cancel_baton, pool));
1286 /* Does the work of closing the access baton ADM_ACCESS. Any physical
1287 locks are removed from the working copy if PRESERVE_LOCK is FALSE, or
1288 are left if PRESERVE_LOCK is TRUE. Any associated access batons that
1289 are direct descendants will also be closed.
1291 static svn_error_t *
1292 do_close(svn_wc_adm_access_t *adm_access,
1293 svn_boolean_t preserve_lock,
1294 apr_pool_t *scratch_pool)
1296 svn_wc_adm_access_t *look;
1298 if (adm_access->closed)
1299 return SVN_NO_ERROR;
1301 /* If we are part of the shared set, then close descendant batons. */
1302 look = get_from_shared(adm_access->abspath, adm_access->db, scratch_pool);
1306 apr_hash_index_t *hi;
1308 /* Gather all the opened access batons from the DB. */
1309 opened = svn_wc__db_temp_get_all_access(adm_access->db, scratch_pool);
1311 /* Close any that are descendants of this baton. */
1312 for (hi = apr_hash_first(scratch_pool, opened);
1314 hi = apr_hash_next(hi))
1316 const char *abspath = apr_hash_this_key(hi);
1317 svn_wc_adm_access_t *child = apr_hash_this_val(hi);
1318 const char *path = child->path;
1320 if (IS_MISSING(child))
1322 /* We don't close the missing entry, but get rid of it from
1324 svn_wc__db_temp_clear_access(adm_access->db, abspath,
1329 if (! svn_dirent_is_ancestor(adm_access->path, path)
1330 || strcmp(adm_access->path, path) == 0)
1333 SVN_ERR(close_single(child, preserve_lock, scratch_pool));
1337 return svn_error_trace(close_single(adm_access, preserve_lock,
1342 /* SVN_DEPRECATED */
1344 svn_wc_adm_close2(svn_wc_adm_access_t *adm_access, apr_pool_t *scratch_pool)
1346 return svn_error_trace(do_close(adm_access, FALSE, scratch_pool));
1350 /* SVN_DEPRECATED */
1352 svn_wc_adm_locked(const svn_wc_adm_access_t *adm_access)
1354 svn_boolean_t locked;
1355 apr_pool_t *subpool = svn_pool_create(adm_access->pool);
1356 svn_error_t *err = svn_wc__db_wclock_owns_lock(&locked, adm_access->db,
1357 adm_access->abspath, TRUE,
1359 svn_pool_destroy(subpool);
1363 svn_error_clear(err);
1364 /* ### is this right? */
1372 svn_wc__write_check(svn_wc__db_t *db,
1373 const char *local_abspath,
1374 apr_pool_t *scratch_pool)
1376 svn_boolean_t locked;
1378 SVN_ERR(svn_wc__db_wclock_owns_lock(&locked, db, local_abspath, FALSE,
1381 return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
1382 _("No write-lock in '%s'"),
1383 svn_dirent_local_style(local_abspath,
1386 return SVN_NO_ERROR;
1390 svn_wc_locked2(svn_boolean_t *locked_here,
1391 svn_boolean_t *locked,
1392 svn_wc_context_t *wc_ctx,
1393 const char *local_abspath,
1394 apr_pool_t *scratch_pool)
1396 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1398 if (locked_here != NULL)
1399 SVN_ERR(svn_wc__db_wclock_owns_lock(locked_here, wc_ctx->db, local_abspath,
1400 FALSE, scratch_pool));
1402 SVN_ERR(svn_wc__db_wclocked(locked, wc_ctx->db, local_abspath,
1405 return SVN_NO_ERROR;
1409 /* SVN_DEPRECATED */
1411 svn_wc_adm_access_path(const svn_wc_adm_access_t *adm_access)
1413 return adm_access->path;
1418 svn_wc__adm_access_abspath(const svn_wc_adm_access_t *adm_access)
1420 return adm_access->abspath;
1424 /* SVN_DEPRECATED */
1426 svn_wc_adm_access_pool(const svn_wc_adm_access_t *adm_access)
1428 return adm_access->pool;
1432 svn_wc__adm_access_pool_internal(const svn_wc_adm_access_t *adm_access)
1434 return adm_access->pool;
1438 svn_wc__adm_access_set_entries(svn_wc_adm_access_t *adm_access,
1439 apr_hash_t *entries)
1441 adm_access->entries_all = entries;
1446 svn_wc__adm_access_entries(svn_wc_adm_access_t *adm_access)
1448 /* Compile with -DSVN_DISABLE_ENTRY_CACHE to disable the in-memory
1449 entry caching. As of 2010-03-18 (r924708) merge_tests 34 and 134
1450 fail during "make check". */
1451 #ifdef SVN_DISABLE_ENTRY_CACHE
1454 return adm_access->entries_all;
1460 svn_wc__adm_get_db(const svn_wc_adm_access_t *adm_access)
1462 return adm_access->db;
1466 svn_wc__acquire_write_lock(const char **lock_root_abspath,
1467 svn_wc_context_t *wc_ctx,
1468 const char *local_abspath,
1469 svn_boolean_t lock_anchor,
1470 apr_pool_t *result_pool,
1471 apr_pool_t *scratch_pool)
1473 svn_wc__db_t *db = wc_ctx->db;
1474 svn_boolean_t is_wcroot;
1475 svn_boolean_t is_switched;
1476 svn_node_kind_t kind;
1479 err = svn_wc__db_is_switched(&is_wcroot, &is_switched, &kind,
1480 db, local_abspath, scratch_pool);
1484 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1485 return svn_error_trace(err);
1487 svn_error_clear(err);
1489 kind = svn_node_none;
1491 is_switched = FALSE;
1494 if (!lock_root_abspath && kind != svn_node_dir)
1495 return svn_error_createf(SVN_ERR_WC_NOT_DIRECTORY, NULL,
1496 _("Can't obtain lock on non-directory '%s'."),
1497 svn_dirent_local_style(local_abspath,
1500 if (lock_anchor && kind == svn_node_dir)
1503 lock_anchor = FALSE;
1508 const char *parent_abspath;
1509 SVN_ERR_ASSERT(lock_root_abspath != NULL);
1511 parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
1513 if (kind == svn_node_dir)
1516 local_abspath = parent_abspath;
1518 else if (kind != svn_node_none && kind != svn_node_unknown)
1520 /* In the single-DB world we know parent exists */
1521 local_abspath = parent_abspath;
1525 /* Can't lock parents that don't exist */
1526 svn_node_kind_t parent_kind;
1527 err = svn_wc__db_read_kind(&parent_kind, db, parent_abspath,
1528 TRUE /* allow_missing */,
1529 TRUE /* show_deleted */,
1530 FALSE /* show_hidden */,
1532 if (err && SVN_WC__ERR_IS_NOT_CURRENT_WC(err))
1534 svn_error_clear(err);
1535 parent_kind = svn_node_unknown;
1540 if (parent_kind != svn_node_dir)
1541 return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
1542 _("'%s' is not a working copy"),
1543 svn_dirent_local_style(local_abspath,
1546 local_abspath = parent_abspath;
1549 else if (kind != svn_node_dir)
1551 local_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
1554 if (lock_root_abspath)
1555 *lock_root_abspath = apr_pstrdup(result_pool, local_abspath);
1557 SVN_ERR(svn_wc__db_wclock_obtain(wc_ctx->db, local_abspath,
1558 -1 /* levels_to_lock (infinite) */,
1559 FALSE /* steal_lock */,
1562 return SVN_NO_ERROR;
1567 svn_wc__release_write_lock(svn_wc_context_t *wc_ctx,
1568 const char *local_abspath,
1569 apr_pool_t *scratch_pool)
1572 svn_skel_t *work_item;
1574 SVN_ERR(svn_wc__db_wq_fetch_next(&id, &work_item, wc_ctx->db, local_abspath,
1575 0, scratch_pool, scratch_pool));
1578 /* Do not release locks (here or below) if there is work to do. */
1579 return SVN_NO_ERROR;
1582 SVN_ERR(svn_wc__db_wclock_release(wc_ctx->db, local_abspath, scratch_pool));
1584 return SVN_NO_ERROR;
1588 svn_wc__call_with_write_lock(svn_wc__with_write_lock_func_t func,
1590 svn_wc_context_t *wc_ctx,
1591 const char *local_abspath,
1592 svn_boolean_t lock_anchor,
1593 apr_pool_t *result_pool,
1594 apr_pool_t *scratch_pool)
1596 svn_error_t *err1, *err2;
1597 const char *lock_root_abspath;
1599 SVN_ERR(svn_wc__acquire_write_lock(&lock_root_abspath, wc_ctx, local_abspath,
1600 lock_anchor, scratch_pool, scratch_pool));
1601 err1 = svn_error_trace(func(baton, result_pool, scratch_pool));
1602 err2 = svn_wc__release_write_lock(wc_ctx, lock_root_abspath, scratch_pool);
1603 return svn_error_compose_create(err1, err2);
1608 svn_wc__acquire_write_lock_for_resolve(const char **lock_root_abspath,
1609 svn_wc_context_t *wc_ctx,
1610 const char *local_abspath,
1611 apr_pool_t *result_pool,
1612 apr_pool_t *scratch_pool)
1614 svn_boolean_t locked = FALSE;
1615 const char *obtained_abspath;
1616 const char *requested_abspath = local_abspath;
1620 const char *required_abspath;
1623 SVN_ERR(svn_wc__acquire_write_lock(&obtained_abspath, wc_ctx,
1624 requested_abspath, FALSE,
1625 scratch_pool, scratch_pool));
1628 SVN_ERR(svn_wc__required_lock_for_resolve(&required_abspath,
1629 wc_ctx->db, local_abspath,
1630 scratch_pool, scratch_pool));
1632 /* It's possible for the required lock path to be an ancestor
1633 of, a descendant of, or equal to, the obtained lock path. If
1634 it's an ancestor we have to try again, otherwise the obtained
1636 child = svn_dirent_skip_ancestor(required_abspath, obtained_abspath);
1637 if (child && child[0])
1639 SVN_ERR(svn_wc__release_write_lock(wc_ctx, obtained_abspath,
1642 requested_abspath = required_abspath;
1646 /* required should be a descendant of, or equal to, obtained */
1647 SVN_ERR_ASSERT(!strcmp(required_abspath, obtained_abspath)
1648 || svn_dirent_skip_ancestor(obtained_abspath,
1653 *lock_root_abspath = apr_pstrdup(result_pool, obtained_abspath);
1655 return SVN_NO_ERROR;