]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_wc/lock.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / libsvn_wc / lock.c
1 /*
2  * lock.c:  routines for locking working copy subdirectories.
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23
24 #define SVN_DEPRECATED
25
26 #include <apr_pools.h>
27 #include <apr_time.h>
28
29 #include "svn_pools.h"
30 #include "svn_dirent_uri.h"
31 #include "svn_path.h"
32 #include "svn_sorts.h"
33 #include "svn_hash.h"
34 #include "svn_types.h"
35
36 #include "wc.h"
37 #include "adm_files.h"
38 #include "lock.h"
39 #include "props.h"
40 #include "wc_db.h"
41
42 #include "svn_private_config.h"
43 #include "private/svn_wc_private.h"
44
45 \f
46
47
48 struct svn_wc_adm_access_t
49 {
50   /* PATH to directory which contains the administrative area */
51   const char *path;
52
53   /* And the absolute form of the path.  */
54   const char *abspath;
55
56   /* Indicates that the baton has been closed. */
57   svn_boolean_t closed;
58
59   /* Handle to the administrative database. */
60   svn_wc__db_t *db;
61
62   /* Was the DB provided to us? If so, then we'll never close it.  */
63   svn_boolean_t db_provided;
64
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;
68
69   /* POOL is used to allocate cached items, they need to persist for the
70      lifetime of this access baton */
71   apr_pool_t *pool;
72
73 };
74
75
76 /* This is a placeholder used in the set hash to represent missing
77    directories.  Only its address is important, it contains no useful
78    data. */
79 static const svn_wc_adm_access_t missing = { 0 };
80 #define IS_MISSING(lock) ((lock) == &missing)
81
82 /* ### hack for now. future functionality coming in a future revision.  */
83 #define svn_wc__db_is_closed(db) FALSE
84
85
86 svn_error_t *
87 svn_wc__internal_check_wc(int *wc_format,
88                           svn_wc__db_t *db,
89                           const char *local_abspath,
90                           svn_boolean_t check_path,
91                           apr_pool_t *scratch_pool)
92 {
93   svn_error_t *err;
94
95   err = svn_wc__db_temp_get_format(wc_format, db, local_abspath, scratch_pool);
96   if (err)
97     {
98       svn_node_kind_t kind;
99
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);
105
106       /* ### the stuff below seems to be redundant. get_format() probably
107          ### does all this.
108          ###
109          ### investigate all callers. DEFINITELY keep in mind the
110          ### svn_wc_check_wc() entrypoint.
111       */
112
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. */
115       *wc_format = 0;
116
117       /* Check path itself exists. */
118       SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
119       if (kind == svn_node_none)
120         {
121           return svn_error_createf(APR_ENOENT, NULL, _("'%s' does not exist"),
122                                    svn_dirent_local_style(local_abspath,
123                                                           scratch_pool));
124         }
125     }
126
127   if (*wc_format >= SVN_WC__WC_NG_VERSION)
128     {
129       svn_wc__db_status_t db_status;
130       svn_node_kind_t db_kind;
131
132       if (check_path)
133         {
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;
138
139           SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool));
140           if (wc_kind != svn_node_dir)
141             {
142               *wc_format = 0; /* Not a directory, so not a wc-directory */
143               return SVN_NO_ERROR;
144             }
145         }
146
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,
151                                  NULL, NULL, NULL,
152                                  db, local_abspath,
153                                  scratch_pool, scratch_pool);
154
155       if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
156         {
157           svn_error_clear(err);
158           *wc_format = 0;
159           return SVN_NO_ERROR;
160         }
161       else
162         SVN_ERR(err);
163
164       if (db_kind != svn_node_dir)
165         {
166           /* The WC thinks there must be a file, so this is not
167              a wc-directory */
168           *wc_format = 0;
169           return SVN_NO_ERROR;
170         }
171
172       switch (db_status)
173         {
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 */
179             *wc_format = 0;
180             return SVN_NO_ERROR;
181           default:
182             break;
183         }
184     }
185
186   return SVN_NO_ERROR;
187 }
188
189
190 svn_error_t *
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)
195 {
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,
200                               scratch_pool));
201 }
202
203
204 /* */
205 static svn_error_t *
206 add_to_shared(svn_wc_adm_access_t *lock, apr_pool_t *scratch_pool)
207 {
208   /* ### sometimes we replace &missing with a now-valid lock.  */
209   {
210     svn_wc_adm_access_t *prior = svn_wc__db_temp_get_access(lock->db,
211                                                             lock->abspath,
212                                                             scratch_pool);
213     if (IS_MISSING(prior))
214       SVN_ERR(svn_wc__db_temp_close_access(lock->db, lock->abspath,
215                                            prior, scratch_pool));
216   }
217
218   svn_wc__db_temp_set_access(lock->db, lock->abspath, lock,
219                              scratch_pool);
220
221   return SVN_NO_ERROR;
222 }
223
224
225 /* */
226 static svn_wc_adm_access_t *
227 get_from_shared(const char *abspath,
228                 svn_wc__db_t *db,
229                 apr_pool_t *scratch_pool)
230 {
231   /* We closed the DB when it became empty. ABSPATH is not present.  */
232   if (db == NULL)
233     return NULL;
234   return svn_wc__db_temp_get_access(db, abspath, scratch_pool);
235 }
236
237
238 /* */
239 static svn_error_t *
240 close_single(svn_wc_adm_access_t *adm_access,
241              svn_boolean_t preserve_lock,
242              apr_pool_t *scratch_pool)
243 {
244   svn_boolean_t locked;
245
246   if (adm_access->closed)
247     return SVN_NO_ERROR;
248
249   /* Physically unlock if required */
250   SVN_ERR(svn_wc__db_wclock_owns_lock(&locked, adm_access->db,
251                                       adm_access->abspath, TRUE,
252                                       scratch_pool));
253   if (locked)
254     {
255       if (!preserve_lock)
256         {
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. */
262
263           svn_error_t *err = svn_wc__db_wclock_release(adm_access->db,
264                                                        adm_access->abspath,
265                                                        scratch_pool);
266           if (err)
267             {
268               if (svn_wc__adm_area_exists(adm_access->abspath, scratch_pool))
269                 return err;
270               svn_error_clear(err);
271             }
272         }
273     }
274
275   /* Reset to prevent further use of the lock. */
276   adm_access->closed = TRUE;
277
278   /* Detach from set */
279   SVN_ERR(svn_wc__db_temp_close_access(adm_access->db, adm_access->abspath,
280                                        adm_access, scratch_pool));
281
282   /* Possibly close the underlying wc_db. */
283   if (!adm_access->db_provided)
284     {
285       apr_hash_t *opened = svn_wc__db_temp_get_all_access(adm_access->db,
286                                                           scratch_pool);
287       if (apr_hash_count(opened) == 0)
288         {
289           SVN_ERR(svn_wc__db_close(adm_access->db));
290           adm_access->db = NULL;
291         }
292     }
293
294   return SVN_NO_ERROR;
295 }
296
297
298 /* Cleanup for a locked access baton.
299
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.  */
303 static apr_status_t
304 pool_cleanup_locked(void *p)
305 {
306   svn_wc_adm_access_t *lock = p;
307   apr_uint64_t id;
308   svn_skel_t *work_item;
309   svn_error_t *err;
310
311   if (lock->closed)
312     return APR_SUCCESS;
313
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))
316     {
317       apr_pool_t *scratch_pool;
318       svn_wc__db_t *db;
319
320       lock->closed = TRUE;
321
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))
325         return APR_SUCCESS;
326
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.
329
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);
334
335       err = svn_wc__db_open(&db, NULL /* ### config. need! */, FALSE, TRUE,
336                             scratch_pool, scratch_pool);
337       if (!err)
338         {
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)
342             {
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);
346             }
347         }
348       svn_error_clear(err);
349
350       /* Closes the DB, too.  */
351       svn_pool_destroy(scratch_pool);
352
353       return APR_SUCCESS;
354     }
355
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);
359
360   /* Close just this access baton. The pool cleanup will close the rest.  */
361   if (!err)
362     err = close_single(lock,
363                        work_item != NULL /* preserve_lock */,
364                        lock->pool);
365
366   if (err)
367     {
368       apr_status_t apr_err = err->apr_err;
369       svn_error_clear(err);
370       return apr_err;
371     }
372
373   return APR_SUCCESS;
374 }
375
376
377 /* Cleanup for a readonly access baton.  */
378 static apr_status_t
379 pool_cleanup_readonly(void *data)
380 {
381   svn_wc_adm_access_t *lock = data;
382   svn_error_t *err;
383
384   if (lock->closed)
385     return APR_SUCCESS;
386
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))
391     return APR_SUCCESS;
392
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);
397   if (err)
398     {
399       apr_status_t result = err->apr_err;
400       svn_error_clear(err);
401       return result;
402     }
403
404   return APR_SUCCESS;
405 }
406
407
408 /* An APR pool cleanup handler.  This is a child handler, it removes the
409    main pool handler. */
410 static apr_status_t
411 pool_cleanup_child(void *p)
412 {
413   svn_wc_adm_access_t *lock = p;
414
415   apr_pool_cleanup_kill(lock->pool, lock, pool_cleanup_locked);
416   apr_pool_cleanup_kill(lock->pool, lock, pool_cleanup_readonly);
417
418   return APR_SUCCESS;
419 }
420
421
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
424    is already locked */
425 static svn_error_t *
426 adm_access_alloc(svn_wc_adm_access_t **adm_access,
427                  const char *path,
428                  svn_wc__db_t *db,
429                  svn_boolean_t db_provided,
430                  svn_boolean_t write_lock,
431                  apr_pool_t *result_pool,
432                  apr_pool_t *scratch_pool)
433 {
434   svn_error_t *err;
435   svn_wc_adm_access_t *lock = apr_palloc(result_pool, sizeof(*lock));
436
437   lock->closed = FALSE;
438   lock->entries_all = NULL;
439   lock->db = db;
440   lock->db_provided = db_provided;
441   lock->path = apr_pstrdup(result_pool, path);
442   lock->pool = result_pool;
443
444   SVN_ERR(svn_dirent_get_absolute(&lock->abspath, path, result_pool));
445
446   *adm_access = lock;
447
448   if (write_lock)
449     {
450       svn_boolean_t owns_lock;
451
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,
454                                           scratch_pool));
455
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 */
460       if (!owns_lock
461           || svn_wc__adm_retrieve_internal2(db, lock->abspath, scratch_pool))
462         {
463           SVN_ERR(svn_wc__db_wclock_obtain(db, lock->abspath, 0, FALSE,
464                                            scratch_pool));
465         }
466     }
467
468   err = add_to_shared(lock, scratch_pool);
469
470   if (err)
471     return svn_error_compose_create(
472                 err,
473                 svn_wc__db_wclock_release(db, lock->abspath, scratch_pool));
474
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. */
482
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,
486                             write_lock
487                               ? pool_cleanup_locked
488                               : pool_cleanup_readonly,
489                             pool_cleanup_child);
490
491   return SVN_NO_ERROR;
492 }
493
494
495 /* */
496 static svn_error_t *
497 probe(svn_wc__db_t *db,
498       const char **dir,
499       const char *path,
500       apr_pool_t *pool)
501 {
502   svn_node_kind_t kind;
503   int wc_format = 0;
504
505   SVN_ERR(svn_io_check_path(path, &kind, pool));
506   if (kind == svn_node_dir)
507     {
508       const char *local_abspath;
509
510       SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
511       SVN_ERR(svn_wc__internal_check_wc(&wc_format, db, local_abspath,
512                                         FALSE, pool));
513     }
514
515   /* a "version" of 0 means a non-wc directory */
516   if (kind != svn_node_dir || wc_format == 0)
517     {
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))
526         {
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);
532         }
533
534       *dir = svn_dirent_dirname(path, pool);
535     }
536   else
537     *dir = path;
538
539   return SVN_NO_ERROR;
540 }
541
542
543 /* */
544 static svn_error_t *
545 open_single(svn_wc_adm_access_t **adm_access,
546             const char *path,
547             svn_boolean_t write_lock,
548             svn_wc__db_t *db,
549             svn_boolean_t db_provided,
550             apr_pool_t *result_pool,
551             apr_pool_t *scratch_pool)
552 {
553   const char *local_abspath;
554   int wc_format = 0;
555   svn_error_t *err;
556   svn_wc_adm_access_t *lock;
557
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,
560                                   scratch_pool);
561   if (wc_format == 0 || (err && APR_STATUS_IS_ENOENT(err->apr_err)))
562     {
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));
566     }
567   SVN_ERR(err);
568
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);
573
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));
577
578   /* ### recurse was here */
579   *adm_access = lock;
580
581   return SVN_NO_ERROR;
582 }
583
584
585 /* Retrieves the KIND of LOCAL_ABSPATH and whether its administrative data is
586    available in the working copy.
587
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).
591
592    KIND can be NULL.
593
594    ### note: this function should go away when we move to a single
595    ### adminstrative area.  */
596 static svn_error_t *
597 adm_available(svn_boolean_t *available,
598               svn_node_kind_t *kind,
599               svn_wc__db_t *db,
600               const char *local_abspath,
601               apr_pool_t *scratch_pool)
602 {
603   svn_wc__db_status_t status;
604
605   if (kind)
606     *kind = svn_node_unknown;
607
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));
613
614   *available = !(status == svn_wc__db_status_server_excluded
615                  || status == svn_wc__db_status_excluded
616                  || status == svn_wc__db_status_not_present);
617
618   return SVN_NO_ERROR;
619 }
620 /* This is essentially the guts of svn_wc_adm_open3.
621  *
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.
624  */
625 static svn_error_t *
626 do_open(svn_wc_adm_access_t **adm_access,
627         const char *path,
628         svn_wc__db_t *db,
629         svn_boolean_t db_provided,
630         apr_array_header_t *rollback,
631         svn_boolean_t write_lock,
632         int levels_to_lock,
633         svn_cancel_func_t cancel_func,
634         void *cancel_baton,
635         apr_pool_t *result_pool,
636         apr_pool_t *scratch_pool)
637 {
638   svn_wc_adm_access_t *lock;
639   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
640
641   SVN_ERR(open_single(&lock, path, write_lock, db, db_provided,
642                       result_pool, iterpool));
643
644   /* Add self to the rollback list in case of error.  */
645   APR_ARRAY_PUSH(rollback, svn_wc_adm_access_t *) = lock;
646
647   if (levels_to_lock != 0)
648     {
649       const apr_array_header_t *children;
650       const char *local_abspath = svn_wc__adm_access_abspath(lock);
651       int i;
652
653       /* Reduce levels_to_lock since we are about to recurse */
654       if (levels_to_lock > 0)
655         levels_to_lock--;
656
657       SVN_ERR(svn_wc__db_read_children(&children, db, local_abspath,
658                                        scratch_pool, iterpool));
659
660       /* Open the tree */
661       for (i = 0; i < children->nelts; i++)
662         {
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 *);
667
668           svn_pool_clear(iterpool);
669
670           /* See if someone wants to cancel this operation. */
671           if (cancel_func)
672             SVN_ERR(cancel_func(cancel_baton));
673
674           node_abspath = svn_dirent_join(local_abspath, name, iterpool);
675
676           SVN_ERR(adm_available(&available,
677                                 &kind,
678                                 db,
679                                 node_abspath,
680                                 scratch_pool));
681
682           if (kind != svn_node_dir)
683             continue;
684
685           if (available)
686             {
687               const char *node_path = svn_dirent_join(path, name, iterpool);
688               svn_wc_adm_access_t *node_access;
689
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.  */
696             }
697         }
698     }
699   svn_pool_destroy(iterpool);
700
701   *adm_access = lock;
702
703   return SVN_NO_ERROR;
704 }
705
706
707 /* */
708 static svn_error_t *
709 open_all(svn_wc_adm_access_t **adm_access,
710          const char *path,
711          svn_wc__db_t *db,
712          svn_boolean_t db_provided,
713          svn_boolean_t write_lock,
714          int levels_to_lock,
715          svn_cancel_func_t cancel_func,
716          void *cancel_baton,
717          apr_pool_t *pool)
718 {
719   apr_array_header_t *rollback;
720   svn_error_t *err;
721
722   rollback = apr_array_make(pool, 10, sizeof(svn_wc_adm_access_t *));
723
724   err = do_open(adm_access, path, db, db_provided, rollback,
725                 write_lock, levels_to_lock,
726                 cancel_func, cancel_baton, pool, pool);
727   if (err)
728     {
729       int i;
730
731       for (i = rollback->nelts; i--; )
732         {
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));
736
737           svn_error_clear(close_single(lock, FALSE /* preserve_lock */, pool));
738         }
739     }
740
741   return svn_error_trace(err);
742 }
743
744
745 svn_error_t *
746 svn_wc_adm_open3(svn_wc_adm_access_t **adm_access,
747                  svn_wc_adm_access_t *associated,
748                  const char *path,
749                  svn_boolean_t write_lock,
750                  int levels_to_lock,
751                  svn_cancel_func_t cancel_func,
752                  void *cancel_baton,
753                  apr_pool_t *pool)
754 {
755   svn_wc__db_t *db;
756   svn_boolean_t db_provided;
757
758   /* Make sure that ASSOCIATED has a set of access batons, so that we can
759      glom a reference to self into it. */
760   if (associated)
761     {
762       const char *abspath;
763       svn_wc_adm_access_t *lock;
764
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));
775       db = associated->db;
776       db_provided = associated->db_provided;
777     }
778   else
779     {
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
782          do it here.  */
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,
786                               pool, pool));
787       db_provided = FALSE;
788     }
789
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));
793 }
794
795
796 svn_error_t *
797 svn_wc_adm_probe_open3(svn_wc_adm_access_t **adm_access,
798                        svn_wc_adm_access_t *associated,
799                        const char *path,
800                        svn_boolean_t write_lock,
801                        int levels_to_lock,
802                        svn_cancel_func_t cancel_func,
803                        void *cancel_baton,
804                        apr_pool_t *pool)
805 {
806   svn_error_t *err;
807   const char *dir;
808
809   if (associated == NULL)
810     {
811       svn_wc__db_t *db;
812
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));
818       SVN_ERR(err);
819     }
820   else
821     {
822       SVN_ERR(probe(associated->db, &dir, path, pool));
823     }
824
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.  */
829   if (dir != path)
830     levels_to_lock = 0;
831
832   err = svn_wc_adm_open3(adm_access, associated, dir, write_lock,
833                          levels_to_lock, cancel_func, cancel_baton, pool);
834   if (err)
835     {
836       svn_error_t *err2;
837
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)))
845         {
846           svn_error_compose(err, err2);
847           return err;
848         }
849
850       if ((dir != path)
851           && (child_kind == svn_node_dir)
852           && (err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY))
853         {
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));
858         }
859
860       return err;
861     }
862
863   return SVN_NO_ERROR;
864 }
865
866
867 svn_wc_adm_access_t *
868 svn_wc__adm_retrieve_internal2(svn_wc__db_t *db,
869                                const char *abspath,
870                                apr_pool_t *scratch_pool)
871 {
872   svn_wc_adm_access_t *adm_access = get_from_shared(abspath, db, scratch_pool);
873
874   /* If the entry is marked as "missing", then return nothing.  */
875   if (IS_MISSING(adm_access))
876     adm_access = NULL;
877
878   return adm_access;
879 }
880
881
882 /* SVN_DEPRECATED */
883 svn_error_t *
884 svn_wc_adm_retrieve(svn_wc_adm_access_t **adm_access,
885                     svn_wc_adm_access_t *associated,
886                     const char *path,
887                     apr_pool_t *pool)
888 {
889   const char *local_abspath;
890   svn_node_kind_t kind = svn_node_unknown;
891   svn_node_kind_t wckind;
892   svn_error_t *err;
893
894   SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
895
896   if (strcmp(associated->path, path) == 0)
897     *adm_access = associated;
898   else
899     *adm_access = svn_wc__adm_retrieve_internal2(associated->db, local_abspath,
900                                                  pool);
901
902   /* We found what we're looking for, so bail. */
903   if (*adm_access)
904     return SVN_NO_ERROR;
905
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
908      for NULL batons. */
909   /* We are going to send a SVN_ERR_WC_NOT_LOCKED, but let's provide
910      a bit more information to our caller */
911
912   err = svn_io_check_path(path, &wckind, pool);
913
914   /* If we can't check the path, we can't make a good error message.  */
915   if (err)
916     {
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));
920     }
921
922   if (associated)
923     {
924       err = svn_wc__db_read_kind(&kind, svn_wc__adm_get_db(associated),
925                                  local_abspath,
926                                  TRUE /* allow_missing */,
927                                  TRUE /* show_deleted */,
928                                  FALSE /* show_hidden */, pool);
929
930       if (err)
931         {
932           kind = svn_node_unknown;
933           svn_error_clear(err);
934         }
935     }
936
937   if (kind == svn_node_dir && wckind == svn_node_file)
938     {
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));
943
944       return svn_error_create(SVN_ERR_WC_NOT_LOCKED, err, err->message);
945     }
946
947   if (kind != svn_node_dir && kind != svn_node_unknown)
948     {
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));
953
954       return svn_error_create(SVN_ERR_WC_NOT_LOCKED, err, err->message);
955     }
956
957   if (kind == svn_node_unknown || wckind == svn_node_none)
958     {
959       err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
960                               _("Directory '%s' is missing"),
961                               svn_dirent_local_style(path, pool));
962
963       return svn_error_create(SVN_ERR_WC_NOT_LOCKED, err, err->message);
964     }
965
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));
970 }
971
972
973 /* SVN_DEPRECATED */
974 svn_error_t *
975 svn_wc_adm_probe_retrieve(svn_wc_adm_access_t **adm_access,
976                           svn_wc_adm_access_t *associated,
977                           const char *path,
978                           apr_pool_t *pool)
979 {
980   const char *dir;
981   const char *local_abspath;
982   svn_node_kind_t kind;
983   svn_error_t *err;
984
985   SVN_ERR_ASSERT(associated != NULL);
986
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*/,
992                                pool));
993
994   if (kind == svn_node_dir)
995     dir = path;
996   else if (kind != svn_node_unknown)
997     dir = svn_dirent_dirname(path, pool);
998   else
999     /* Not a versioned item, probe it */
1000     SVN_ERR(probe(associated->db, &dir, path, pool));
1001
1002   err = svn_wc_adm_retrieve(adm_access, associated, dir, pool);
1003   if (err && err->apr_err == SVN_ERR_WC_NOT_LOCKED)
1004     {
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
1009          on the child) */
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));
1013     }
1014   else
1015     return svn_error_trace(err);
1016
1017   return SVN_NO_ERROR;
1018 }
1019
1020
1021 /* SVN_DEPRECATED */
1022 svn_error_t *
1023 svn_wc_adm_probe_try3(svn_wc_adm_access_t **adm_access,
1024                       svn_wc_adm_access_t *associated,
1025                       const char *path,
1026                       svn_boolean_t write_lock,
1027                       int levels_to_lock,
1028                       svn_cancel_func_t cancel_func,
1029                       void *cancel_baton,
1030                       apr_pool_t *pool)
1031 {
1032   svn_error_t *err;
1033
1034   err = svn_wc_adm_probe_retrieve(adm_access, associated, path, pool);
1035
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))
1040     {
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));
1046
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
1052          case. */
1053       if (err && (err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY))
1054         {
1055           svn_error_clear(err);
1056           *adm_access = NULL;
1057           err = NULL;
1058         }
1059     }
1060
1061   return err;
1062 }
1063
1064
1065 /* */
1066 static svn_error_t *
1067 child_is_disjoint(svn_boolean_t *disjoint,
1068                   svn_wc__db_t *db,
1069                   const char *local_abspath,
1070                   apr_pool_t *scratch_pool)
1071 {
1072   svn_boolean_t is_switched;
1073
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));
1077
1078   if (*disjoint)
1079     return SVN_NO_ERROR;
1080
1081   if (is_switched)
1082     *disjoint = TRUE;
1083
1084   return SVN_NO_ERROR;
1085 }
1086
1087 /* */
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,
1092             svn_wc__db_t *db,
1093             svn_boolean_t db_provided,
1094             const char *path,
1095             svn_boolean_t write_lock,
1096             int levels_to_lock,
1097             svn_cancel_func_t cancel_func,
1098             void *cancel_baton,
1099             apr_pool_t *pool)
1100 {
1101   const char *base_name = svn_dirent_basename(path, pool);
1102
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
1105      do it here.  */
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.  */
1109   if (!db_provided)
1110     SVN_ERR(svn_wc__db_open(&db, NULL, /* ### config. need! */ FALSE, TRUE,
1111                             pool, pool));
1112
1113   if (svn_path_is_empty(path)
1114       || svn_dirent_is_root(path, strlen(path))
1115       || ! strcmp(base_name, ".."))
1116     {
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;
1121       *target = "";
1122     }
1123   else
1124     {
1125       svn_error_t *err;
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;
1131
1132       SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
1133
1134       /* Try to open parent of PATH to setup P_ACCESS */
1135       err = open_single(&p_access, parent, write_lock, db, db_provided,
1136                         pool, pool);
1137       if (err)
1138         {
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);
1141
1142           if (IS_MISSING(existing_adm))
1143             svn_wc__db_temp_clear_access(db, abspath, pool);
1144           else
1145             SVN_ERR_ASSERT(existing_adm == NULL);
1146
1147           if (err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY)
1148             {
1149               svn_error_clear(err);
1150               p_access = NULL;
1151             }
1152           else if (write_lock && (err->apr_err == SVN_ERR_WC_LOCKED
1153                                   || APR_STATUS_IS_EACCES(err->apr_err)))
1154             {
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);
1159               if (err2)
1160                 {
1161                   svn_error_clear(err2);
1162                   return err;
1163                 }
1164               p_access_err = err;
1165             }
1166           else
1167             return err;
1168         }
1169
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);
1173       if (err)
1174         {
1175           if (p_access == NULL)
1176             {
1177               /* Couldn't open the parent or the target. Bail out.  */
1178               svn_error_clear(p_access_err);
1179               return svn_error_trace(err);
1180             }
1181
1182           if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
1183             {
1184               if (p_access)
1185                 svn_error_clear(svn_wc_adm_close2(p_access, pool));
1186               svn_error_clear(p_access_err);
1187               return svn_error_trace(err);
1188             }
1189
1190           /* This directory is not under version control. Ignore it.  */
1191           svn_error_clear(err);
1192           t_access = NULL;
1193         }
1194
1195       /* At this stage might have P_ACCESS, T_ACCESS or both */
1196
1197       /* Check for switched or disjoint P_ACCESS and T_ACCESS */
1198       if (p_access && t_access)
1199         {
1200           svn_boolean_t disjoint;
1201
1202           err = child_is_disjoint(&disjoint, db, local_abspath, pool);
1203           if (err)
1204             {
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);
1209             }
1210
1211           if (disjoint)
1212             {
1213               /* Switched or disjoint, so drop P_ACCESS. Don't close any
1214                  descendents, or we might blast the child.  */
1215               err = close_single(p_access, FALSE /* preserve_lock */, pool);
1216               if (err)
1217                 {
1218                   svn_error_clear(p_access_err);
1219                   svn_error_clear(svn_wc_adm_close2(t_access, pool));
1220                   return svn_error_trace(err);
1221                 }
1222               p_access = NULL;
1223             }
1224         }
1225
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)
1231         {
1232           if (t_access)
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);
1236         }
1237       svn_error_clear(p_access_err);
1238
1239       if (! t_access)
1240         {
1241           svn_boolean_t available;
1242           svn_node_kind_t kind;
1243
1244           err = adm_available(&available, &kind, db, local_abspath, pool);
1245
1246           if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
1247             svn_error_clear(err);
1248           else if (err)
1249             {
1250               svn_error_clear(svn_wc_adm_close2(p_access, pool));
1251               return svn_error_trace(err);
1252             }
1253         }
1254
1255       *anchor_access = p_access ? p_access : t_access;
1256       *target_access = t_access ? t_access : p_access;
1257
1258       if (! p_access)
1259         *target = "";
1260       else
1261         *target = base_name;
1262     }
1263
1264   return SVN_NO_ERROR;
1265 }
1266
1267
1268 svn_error_t *
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,
1272                        const char *path,
1273                        svn_boolean_t write_lock,
1274                        int levels_to_lock,
1275                        svn_cancel_func_t cancel_func,
1276                        void *cancel_baton,
1277                        apr_pool_t *pool)
1278 {
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));
1283 }
1284
1285
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.
1290  */
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)
1295 {
1296   svn_wc_adm_access_t *look;
1297
1298   if (adm_access->closed)
1299     return SVN_NO_ERROR;
1300
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);
1303   if (look != NULL)
1304     {
1305       apr_hash_t *opened;
1306       apr_hash_index_t *hi;
1307
1308       /* Gather all the opened access batons from the DB.  */
1309       opened = svn_wc__db_temp_get_all_access(adm_access->db, scratch_pool);
1310
1311       /* Close any that are descendents of this baton.  */
1312       for (hi = apr_hash_first(scratch_pool, opened);
1313            hi;
1314            hi = apr_hash_next(hi))
1315         {
1316           const char *abspath = svn__apr_hash_index_key(hi);
1317           svn_wc_adm_access_t *child = svn__apr_hash_index_val(hi);
1318           const char *path = child->path;
1319
1320           if (IS_MISSING(child))
1321             {
1322               /* We don't close the missing entry, but get rid of it from
1323                  the set. */
1324               svn_wc__db_temp_clear_access(adm_access->db, abspath,
1325                                            scratch_pool);
1326               continue;
1327             }
1328
1329           if (! svn_dirent_is_ancestor(adm_access->path, path)
1330               || strcmp(adm_access->path, path) == 0)
1331             continue;
1332
1333           SVN_ERR(close_single(child, preserve_lock, scratch_pool));
1334         }
1335     }
1336
1337   return svn_error_trace(close_single(adm_access, preserve_lock,
1338                                       scratch_pool));
1339 }
1340
1341
1342 /* SVN_DEPRECATED */
1343 svn_error_t *
1344 svn_wc_adm_close2(svn_wc_adm_access_t *adm_access, apr_pool_t *scratch_pool)
1345 {
1346   return svn_error_trace(do_close(adm_access, FALSE, scratch_pool));
1347 }
1348
1349
1350 /* SVN_DEPRECATED */
1351 svn_boolean_t
1352 svn_wc_adm_locked(const svn_wc_adm_access_t *adm_access)
1353 {
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,
1358                                                  subpool);
1359   svn_pool_destroy(subpool);
1360
1361   if (err)
1362     {
1363       svn_error_clear(err);
1364       /* ### is this right? */
1365       return FALSE;
1366     }
1367
1368   return locked;
1369 }
1370
1371 svn_error_t *
1372 svn_wc__write_check(svn_wc__db_t *db,
1373                     const char *local_abspath,
1374                     apr_pool_t *scratch_pool)
1375 {
1376   svn_boolean_t locked;
1377
1378   SVN_ERR(svn_wc__db_wclock_owns_lock(&locked, db, local_abspath, FALSE,
1379                                       scratch_pool));
1380   if (!locked)
1381     return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
1382                              _("No write-lock in '%s'"),
1383                              svn_dirent_local_style(local_abspath,
1384                                                     scratch_pool));
1385
1386   return SVN_NO_ERROR;
1387 }
1388
1389 svn_error_t *
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)
1395 {
1396   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1397
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));
1401   if (locked != NULL)
1402     SVN_ERR(svn_wc__db_wclocked(locked, wc_ctx->db, local_abspath,
1403                                 scratch_pool));
1404
1405   return SVN_NO_ERROR;
1406 }
1407
1408
1409 /* SVN_DEPRECATED */
1410 const char *
1411 svn_wc_adm_access_path(const svn_wc_adm_access_t *adm_access)
1412 {
1413   return adm_access->path;
1414 }
1415
1416
1417 const char *
1418 svn_wc__adm_access_abspath(const svn_wc_adm_access_t *adm_access)
1419 {
1420   return adm_access->abspath;
1421 }
1422
1423
1424 /* SVN_DEPRECATED */
1425 apr_pool_t *
1426 svn_wc_adm_access_pool(const svn_wc_adm_access_t *adm_access)
1427 {
1428   return adm_access->pool;
1429 }
1430
1431 apr_pool_t *
1432 svn_wc__adm_access_pool_internal(const svn_wc_adm_access_t *adm_access)
1433 {
1434   return adm_access->pool;
1435 }
1436
1437 void
1438 svn_wc__adm_access_set_entries(svn_wc_adm_access_t *adm_access,
1439                                apr_hash_t *entries)
1440 {
1441   adm_access->entries_all = entries;
1442 }
1443
1444
1445 apr_hash_t *
1446 svn_wc__adm_access_entries(svn_wc_adm_access_t *adm_access)
1447 {
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
1452   return NULL;
1453 #else
1454   return adm_access->entries_all;
1455 #endif
1456 }
1457
1458
1459 svn_wc__db_t *
1460 svn_wc__adm_get_db(const svn_wc_adm_access_t *adm_access)
1461 {
1462   return adm_access->db;
1463 }
1464
1465 svn_error_t *
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)
1472 {
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;
1477   svn_error_t *err;
1478
1479   err = svn_wc__db_is_switched(&is_wcroot, &is_switched, &kind,
1480                                db, local_abspath, scratch_pool);
1481
1482   if (err)
1483     {
1484       if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1485         return svn_error_trace(err);
1486
1487       svn_error_clear(err);
1488
1489       kind = svn_node_none;
1490       is_wcroot = FALSE;
1491       is_switched = FALSE;
1492     }
1493
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,
1498                                                     scratch_pool));
1499
1500   if (lock_anchor && kind == svn_node_dir)
1501     {
1502       if (is_wcroot)
1503         lock_anchor = FALSE;
1504     }
1505
1506   if (lock_anchor)
1507     {
1508       const char *parent_abspath;
1509       SVN_ERR_ASSERT(lock_root_abspath != NULL);
1510
1511       parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
1512
1513       if (kind == svn_node_dir)
1514         {
1515           if (! is_switched)
1516             local_abspath = parent_abspath;
1517         }
1518       else if (kind != svn_node_none && kind != svn_node_unknown)
1519         {
1520           /* In the single-DB world we know parent exists */
1521           local_abspath = parent_abspath;
1522         }
1523       else
1524         {
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 */,
1531                                      scratch_pool);
1532           if (err && SVN_WC__ERR_IS_NOT_CURRENT_WC(err))
1533             {
1534               svn_error_clear(err);
1535               parent_kind = svn_node_unknown;
1536             }
1537           else
1538             SVN_ERR(err);
1539
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,
1544                                                             scratch_pool));
1545
1546           local_abspath = parent_abspath;
1547         }
1548     }
1549   else if (kind != svn_node_dir)
1550     {
1551       local_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
1552     }
1553
1554   if (lock_root_abspath)
1555     *lock_root_abspath = apr_pstrdup(result_pool, local_abspath);
1556
1557   SVN_ERR(svn_wc__db_wclock_obtain(wc_ctx->db, local_abspath,
1558                                    -1 /* levels_to_lock (infinite) */,
1559                                    FALSE /* steal_lock */,
1560                                    scratch_pool));
1561
1562   return SVN_NO_ERROR;
1563 }
1564
1565
1566 svn_error_t *
1567 svn_wc__release_write_lock(svn_wc_context_t *wc_ctx,
1568                            const char *local_abspath,
1569                            apr_pool_t *scratch_pool)
1570 {
1571   apr_uint64_t id;
1572   svn_skel_t *work_item;
1573
1574   SVN_ERR(svn_wc__db_wq_fetch_next(&id, &work_item, wc_ctx->db, local_abspath,
1575                                    0, scratch_pool, scratch_pool));
1576   if (work_item)
1577     {
1578       /* Do not release locks (here or below) if there is work to do.  */
1579       return SVN_NO_ERROR;
1580     }
1581
1582   SVN_ERR(svn_wc__db_wclock_release(wc_ctx->db, local_abspath, scratch_pool));
1583
1584   return SVN_NO_ERROR;
1585 }
1586
1587 svn_error_t *
1588 svn_wc__call_with_write_lock(svn_wc__with_write_lock_func_t func,
1589                              void *baton,
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)
1595 {
1596   svn_error_t *err1, *err2;
1597   const char *lock_root_abspath;
1598
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);
1604 }
1605
1606
1607 svn_error_t *
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)
1613 {
1614   svn_boolean_t locked = FALSE;
1615   const char *obtained_abspath;
1616   const char *requested_abspath = local_abspath;
1617
1618   while (!locked)
1619     {
1620       const char *required_abspath;
1621       const char *child;
1622
1623       SVN_ERR(svn_wc__acquire_write_lock(&obtained_abspath, wc_ctx,
1624                                          requested_abspath, FALSE,
1625                                          scratch_pool, scratch_pool));
1626       locked = TRUE;
1627
1628       SVN_ERR(svn_wc__required_lock_for_resolve(&required_abspath,
1629                                                 wc_ctx->db, local_abspath,
1630                                                 scratch_pool, scratch_pool));
1631
1632       /* It's possible for the required lock path to be an ancestor
1633          of, a descendent of, or equal to, the obtained lock path. If
1634          it's an ancestor we have to try again, otherwise the obtained
1635          lock will do. */
1636       child = svn_dirent_skip_ancestor(required_abspath, obtained_abspath);
1637       if (child && child[0])
1638         {
1639           SVN_ERR(svn_wc__release_write_lock(wc_ctx, obtained_abspath,
1640                                              scratch_pool));
1641           locked = FALSE;
1642           requested_abspath = required_abspath;
1643         }
1644       else
1645         {
1646           /* required should be a descendent of, or equal to, obtained */
1647           SVN_ERR_ASSERT(!strcmp(required_abspath, obtained_abspath)
1648                          || svn_dirent_skip_ancestor(obtained_abspath,
1649                                                      required_abspath));
1650         }
1651     }
1652
1653   *lock_root_abspath = apr_pstrdup(result_pool, obtained_abspath);
1654
1655   return SVN_NO_ERROR;
1656 }