1/*
2 * node.c:  routines for getting information about nodes in the working copy.
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/* A note about these functions:
25
26   We aren't really sure yet which bits of data libsvn_client needs about
27   nodes.  In wc-1, we just grab the entry, and then use whatever we want
28   from it.  Such a pattern is Bad.
29
30   This file is intended to hold functions which retrieve specific bits of
31   information about a node, and will hopefully give us a better idea about
32   what data libsvn_client needs, and how to best provide that data in 1.7
33   final.  As such, these functions should only be called from outside
34   libsvn_wc; any internal callers are encouraged to use the appropriate
35   information fetching function, such as svn_wc__db_read_info().
36*/
37
38#include <apr_pools.h>
39#include <apr_time.h>
40
41#include "svn_pools.h"
42#include "svn_dirent_uri.h"
43#include "svn_path.h"
44#include "svn_hash.h"
45#include "svn_types.h"
46
47#include "wc.h"
48#include "props.h"
49#include "entries.h"
50#include "wc_db.h"
51
52#include "svn_private_config.h"
53#include "private/svn_wc_private.h"
54
55
56/* Set *CHILDREN_ABSPATHS to a new array of the full paths formed by joining
57 * each name in REL_CHILDREN onto DIR_ABSPATH.
58 *
59 * Allocate the output array and its elements in RESULT_POOL. */
60static void
61make_absolute(const apr_array_header_t **children_abspaths,
62              const char *dir_abspath,
63              const apr_array_header_t *rel_children,
64              apr_pool_t *result_pool)
65{
66  apr_array_header_t *children;
67  int i;
68
69  children = apr_array_make(result_pool, rel_children->nelts,
70                            sizeof(const char *));
71  for (i = 0; i < rel_children->nelts; i++)
72    {
73      const char *name = APR_ARRAY_IDX(rel_children, i, const char *);
74      APR_ARRAY_PUSH(children, const char *) =
75                        svn_dirent_join(dir_abspath, name,
76                                        result_pool);
77    }
78
79  *children_abspaths = children;
80}
81
82
83svn_error_t *
84svn_wc__node_get_children_of_working_node(const apr_array_header_t **children,
85                                          svn_wc_context_t *wc_ctx,
86                                          const char *dir_abspath,
87                                          apr_pool_t *result_pool,
88                                          apr_pool_t *scratch_pool)
89{
90  const apr_array_header_t *child_names;
91
92  SVN_ERR(svn_wc__db_read_children_of_working_node(&child_names,
93                                                   wc_ctx->db, dir_abspath,
94                                                   scratch_pool, scratch_pool));
95  make_absolute(children, dir_abspath, child_names, result_pool);
96  return SVN_NO_ERROR;
97}
98
99svn_error_t *
100svn_wc__node_get_not_present_children(const apr_array_header_t **children,
101                                      svn_wc_context_t *wc_ctx,
102                                      const char *dir_abspath,
103                                      apr_pool_t *result_pool,
104                                      apr_pool_t *scratch_pool)
105{
106  const apr_array_header_t *child_names;
107
108  SVN_ERR(svn_wc__db_base_read_not_present_children(
109                                   &child_names,
110                                   wc_ctx->db, dir_abspath,
111                                   scratch_pool, scratch_pool));
112  make_absolute(children, dir_abspath, child_names, result_pool);
113  return SVN_NO_ERROR;
114}
115
116
117svn_error_t *
118svn_wc__node_get_repos_info(svn_revnum_t *revision,
119                            const char **repos_relpath,
120                            const char **repos_root_url,
121                            const char **repos_uuid,
122                            svn_wc_context_t *wc_ctx,
123                            const char *local_abspath,
124                            apr_pool_t *result_pool,
125                            apr_pool_t *scratch_pool)
126{
127  return svn_error_trace(
128            svn_wc__db_read_repos_info(revision,
129                                       repos_relpath,
130                                       repos_root_url,
131                                       repos_uuid,
132                                       wc_ctx->db, local_abspath,
133                                       result_pool, scratch_pool));
134}
135
136/* Convert DB_KIND into the appropriate NODE_KIND value.
137 * If SHOW_HIDDEN is TRUE, report the node kind as found in the DB
138 * even if DB_STATUS indicates that the node is hidden.
139 * Else, return svn_node_none for such nodes.
140 *
141 * ### This is a bit ugly. We should consider promoting svn_kind_t
142 * ### to the de-facto node kind type instead of converting between them
143 * ### in non-backwards compat code.
144 * ### See also comments at the definition of svn_kind_t.
145 *
146 * ### In reality, the previous comment is out of date, as there is
147 * ### now only one enumeration for node kinds, and that is
148 * ### svn_node_kind_t (svn_kind_t was merged with that). But it's
149 * ### still ugly.
150 */
151static svn_error_t *
152convert_db_kind_to_node_kind(svn_node_kind_t *node_kind,
153                             svn_node_kind_t db_kind,
154                             svn_wc__db_status_t db_status,
155                             svn_boolean_t show_hidden)
156{
157  *node_kind = db_kind;
158
159  /* Make sure hidden nodes return svn_node_none. */
160  if (! show_hidden)
161    switch (db_status)
162      {
163        case svn_wc__db_status_not_present:
164        case svn_wc__db_status_server_excluded:
165        case svn_wc__db_status_excluded:
166          *node_kind = svn_node_none;
167
168        default:
169          break;
170      }
171
172  return SVN_NO_ERROR;
173}
174
175svn_error_t *
176svn_wc_read_kind2(svn_node_kind_t *kind,
177                  svn_wc_context_t *wc_ctx,
178                  const char *local_abspath,
179                  svn_boolean_t show_deleted,
180                  svn_boolean_t show_hidden,
181                  apr_pool_t *scratch_pool)
182{
183  svn_node_kind_t db_kind;
184
185  SVN_ERR(svn_wc__db_read_kind(&db_kind,
186                               wc_ctx->db, local_abspath,
187                               TRUE,
188                               show_deleted,
189                               show_hidden,
190                               scratch_pool));
191
192  if (db_kind == svn_node_dir)
193    *kind = svn_node_dir;
194  else if (db_kind == svn_node_file || db_kind == svn_node_symlink)
195    *kind = svn_node_file;
196  else
197    *kind = svn_node_none;
198
199  return SVN_NO_ERROR;
200}
201
202svn_error_t *
203svn_wc__node_get_changed_info(svn_revnum_t *changed_rev,
204                              apr_time_t *changed_date,
205                              const char **changed_author,
206                              svn_wc_context_t *wc_ctx,
207                              const char *local_abspath,
208                              apr_pool_t *result_pool,
209                              apr_pool_t *scratch_pool)
210{
211  return svn_error_trace(
212    svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, changed_rev,
213                         changed_date, changed_author, NULL, NULL, NULL,
214                         NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
215                         NULL, NULL, NULL, NULL, NULL, NULL, NULL,
216                         wc_ctx->db, local_abspath, result_pool,
217                         scratch_pool));
218}
219
220svn_error_t *
221svn_wc__node_get_url(const char **url,
222                     svn_wc_context_t *wc_ctx,
223                     const char *local_abspath,
224                     apr_pool_t *result_pool,
225                     apr_pool_t *scratch_pool)
226{
227  const char *repos_root_url;
228  const char *repos_relpath;
229
230  SVN_ERR(svn_wc__db_read_repos_info(NULL, &repos_relpath, &repos_root_url,
231                                     NULL,
232                                     wc_ctx->db, local_abspath,
233                                     scratch_pool, scratch_pool));
234
235  *url = svn_path_url_add_component2(repos_root_url, repos_relpath,
236                                     result_pool);
237
238  return SVN_NO_ERROR;
239}
240
241/* A recursive node-walker, helper for svn_wc__internal_walk_children().
242 *
243 * Call WALK_CALLBACK with WALK_BATON on all children (recursively) of
244 * DIR_ABSPATH in DB, but not on DIR_ABSPATH itself. DIR_ABSPATH must be a
245 * versioned directory. If SHOW_HIDDEN is true, visit hidden nodes, else
246 * ignore them. Restrict the depth of the walk to DEPTH.
247 *
248 * ### Is it possible for a subdirectory to be hidden and known to be a
249 *     directory?  If so, and if show_hidden is true, this will try to
250 *     recurse into it.  */
251static svn_error_t *
252walker_helper(svn_wc__db_t *db,
253              const char *dir_abspath,
254              svn_boolean_t show_hidden,
255              const apr_hash_t *changelist_filter,
256              svn_wc__node_found_func_t walk_callback,
257              void *walk_baton,
258              svn_depth_t depth,
259              svn_cancel_func_t cancel_func,
260              void *cancel_baton,
261              apr_pool_t *scratch_pool)
262{
263  apr_pool_t *iterpool;
264  const apr_array_header_t *items;
265  int i;
266
267  if (depth == svn_depth_empty)
268    return SVN_NO_ERROR;
269
270  iterpool = svn_pool_create(scratch_pool);
271
272  SVN_ERR(svn_wc__db_read_children_walker_info(&items, db,
273                                               dir_abspath, scratch_pool,
274                                               iterpool));
275
276  for (i = 0; i < items->nelts; i++)
277    {
278      struct svn_wc__db_walker_info_t *wi =
279              APR_ARRAY_IDX(items, i, struct svn_wc__db_walker_info_t *);
280      const char *child_name = wi->name;
281      svn_node_kind_t child_kind = wi->kind;
282      svn_wc__db_status_t child_status = wi->status;
283      const char *child_abspath;
284
285      svn_pool_clear(iterpool);
286
287      /* See if someone wants to cancel this operation. */
288      if (cancel_func)
289        SVN_ERR(cancel_func(cancel_baton));
290
291      child_abspath = svn_dirent_join(dir_abspath, child_name, iterpool);
292
293      if (!show_hidden)
294        switch (child_status)
295          {
296            case svn_wc__db_status_not_present:
297            case svn_wc__db_status_server_excluded:
298            case svn_wc__db_status_excluded:
299              continue;
300            default:
301              break;
302          }
303
304      /* Return the child, if appropriate. */
305      if ( (child_kind == svn_node_file
306             || depth >= svn_depth_immediates)
307           && svn_wc__internal_changelist_match(db, child_abspath,
308                                                changelist_filter,
309                                                scratch_pool) )
310        {
311          svn_node_kind_t kind;
312
313          SVN_ERR(convert_db_kind_to_node_kind(&kind, child_kind,
314                                               child_status, show_hidden));
315          /* ### We might want to pass child_status as well because at least
316           * ### one callee is asking for it.
317           * ### But is it OK to use an svn_wc__db type in this API?
318           * ###    Not yet, we need to get the node walker
319           * ###    libsvn_wc-internal first. -hkw */
320          SVN_ERR(walk_callback(child_abspath, kind, walk_baton, iterpool));
321        }
322
323      /* Recurse into this directory, if appropriate. */
324      if (child_kind == svn_node_dir
325            && depth >= svn_depth_immediates)
326        {
327          svn_depth_t depth_below_here = depth;
328
329          if (depth == svn_depth_immediates)
330            depth_below_here = svn_depth_empty;
331
332          SVN_ERR(walker_helper(db, child_abspath, show_hidden,
333                                changelist_filter,
334                                walk_callback, walk_baton,
335                                depth_below_here,
336                                cancel_func, cancel_baton,
337                                iterpool));
338        }
339    }
340
341  svn_pool_destroy(iterpool);
342
343  return SVN_NO_ERROR;
344}
345
346
347svn_error_t *
348svn_wc__internal_walk_children(svn_wc__db_t *db,
349                               const char *local_abspath,
350                               svn_boolean_t show_hidden,
351                               const apr_array_header_t *changelist_filter,
352                               svn_wc__node_found_func_t walk_callback,
353                               void *walk_baton,
354                               svn_depth_t walk_depth,
355                               svn_cancel_func_t cancel_func,
356                               void *cancel_baton,
357                               apr_pool_t *scratch_pool)
358{
359  svn_node_kind_t db_kind;
360  svn_node_kind_t kind;
361  svn_wc__db_status_t status;
362  apr_hash_t *changelist_hash = NULL;
363  const char *changelist = NULL;
364
365  SVN_ERR_ASSERT(walk_depth >= svn_depth_empty
366                 && walk_depth <= svn_depth_infinity);
367
368  if (changelist_filter && changelist_filter->nelts)
369    SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter,
370                                       scratch_pool));
371
372  /* Check if the node exists before the first callback */
373  SVN_ERR(svn_wc__db_read_info(&status, &db_kind, NULL, NULL, NULL, NULL,
374                               NULL, NULL, NULL, NULL, NULL, NULL,
375                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
376                               changelist_hash ? &changelist : NULL,
377                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
378                               db, local_abspath, scratch_pool, scratch_pool));
379
380  SVN_ERR(convert_db_kind_to_node_kind(&kind, db_kind, status, show_hidden));
381
382  if (!changelist_hash
383      || (changelist && svn_hash_gets(changelist_hash, changelist)))
384    {
385      SVN_ERR(walk_callback(local_abspath, kind, walk_baton, scratch_pool));
386    }
387
388  if (db_kind == svn_node_file
389      || status == svn_wc__db_status_not_present
390      || status == svn_wc__db_status_excluded
391      || status == svn_wc__db_status_server_excluded)
392    return SVN_NO_ERROR;
393
394  if (db_kind == svn_node_dir)
395    {
396      return svn_error_trace(
397        walker_helper(db, local_abspath, show_hidden, changelist_hash,
398                      walk_callback, walk_baton,
399                      walk_depth, cancel_func, cancel_baton, scratch_pool));
400    }
401
402  return svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL,
403                           _("'%s' has an unrecognized node kind"),
404                           svn_dirent_local_style(local_abspath,
405                                                  scratch_pool));
406}
407
408svn_error_t *
409svn_wc__node_is_not_present(svn_boolean_t *is_not_present,
410                            svn_boolean_t *is_excluded,
411                            svn_boolean_t *is_server_excluded,
412                            svn_wc_context_t *wc_ctx,
413                            const char *local_abspath,
414                            svn_boolean_t base_only,
415                            apr_pool_t *scratch_pool)
416{
417  svn_wc__db_status_t status;
418
419  if (base_only)
420    {
421      SVN_ERR(svn_wc__db_base_get_info(&status,
422                                       NULL, NULL, NULL, NULL, NULL, NULL,
423                                       NULL, NULL, NULL, NULL, NULL, NULL,
424                                       NULL, NULL, NULL,
425                                       wc_ctx->db, local_abspath,
426                                       scratch_pool, scratch_pool));
427    }
428  else
429    {
430      SVN_ERR(svn_wc__db_read_info(&status,
431                                   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
432                                   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
433                                   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
434                                   NULL, NULL, NULL, NULL, NULL,
435                                   wc_ctx->db, local_abspath,
436                                   scratch_pool, scratch_pool));
437    }
438
439  if (is_not_present)
440    *is_not_present = (status == svn_wc__db_status_not_present);
441
442  if (is_excluded)
443    *is_excluded = (status == svn_wc__db_status_excluded);
444
445  if (is_server_excluded)
446    *is_server_excluded = (status == svn_wc__db_status_server_excluded);
447
448  return SVN_NO_ERROR;
449}
450
451svn_error_t *
452svn_wc__node_is_added(svn_boolean_t *is_added,
453                      svn_wc_context_t *wc_ctx,
454                      const char *local_abspath,
455                      apr_pool_t *scratch_pool)
456{
457  svn_wc__db_status_t status;
458
459  SVN_ERR(svn_wc__db_read_info(&status,
460                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
461                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
462                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
463                               NULL, NULL, NULL, NULL, NULL,
464                               wc_ctx->db, local_abspath,
465                               scratch_pool, scratch_pool));
466  *is_added = (status == svn_wc__db_status_added);
467
468  return SVN_NO_ERROR;
469}
470
471svn_error_t *
472svn_wc__node_has_working(svn_boolean_t *has_working,
473                         svn_wc_context_t *wc_ctx,
474                         const char *local_abspath,
475                         apr_pool_t *scratch_pool)
476{
477  svn_wc__db_status_t status;
478
479  SVN_ERR(svn_wc__db_read_info(&status,
480                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
481                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
482                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
483                               NULL, NULL, NULL, NULL, has_working,
484                               wc_ctx->db, local_abspath,
485                               scratch_pool, scratch_pool));
486
487  return SVN_NO_ERROR;
488}
489
490
491svn_error_t *
492svn_wc__node_get_base(svn_node_kind_t *kind,
493                      svn_revnum_t *revision,
494                      const char **repos_relpath,
495                      const char **repos_root_url,
496                      const char **repos_uuid,
497                      const char **lock_token,
498                      svn_wc_context_t *wc_ctx,
499                      const char *local_abspath,
500                      svn_boolean_t ignore_enoent,
501                      apr_pool_t *result_pool,
502                      apr_pool_t *scratch_pool)
503{
504  svn_error_t *err;
505  svn_wc__db_status_t status;
506  svn_wc__db_lock_t *lock;
507  svn_node_kind_t db_kind;
508
509  err = svn_wc__db_base_get_info(&status, &db_kind, revision, repos_relpath,
510                                 repos_root_url, repos_uuid, NULL,
511                                 NULL, NULL, NULL, NULL, NULL,
512                                 lock_token ? &lock : NULL,
513                                 NULL, NULL, NULL,
514                                 wc_ctx->db, local_abspath,
515                                 result_pool, scratch_pool);
516
517  if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
518    return svn_error_trace(err);
519  else if (err
520           || (status != svn_wc__db_status_normal
521               && status != svn_wc__db_status_incomplete))
522    {
523      if (!ignore_enoent)
524        {
525          if (err)
526            return svn_error_trace(err);
527          else
528            return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
529                                     _("The node '%s' was not found."),
530                                     svn_dirent_local_style(local_abspath,
531                                                            scratch_pool));
532        }
533      svn_error_clear(err);
534
535      if (kind)
536        *kind = svn_node_unknown;
537      if (revision)
538        *revision = SVN_INVALID_REVNUM;
539      if (repos_relpath)
540        *repos_relpath = NULL;
541      if (repos_root_url)
542        *repos_root_url = NULL;
543      if (repos_uuid)
544        *repos_uuid = NULL;
545      if (lock_token)
546        *lock_token = NULL;
547      return SVN_NO_ERROR;
548    }
549
550  if (kind)
551    *kind = db_kind;
552  if (lock_token)
553    *lock_token = lock ? lock->token : NULL;
554
555  SVN_ERR_ASSERT(!revision || SVN_IS_VALID_REVNUM(*revision));
556  SVN_ERR_ASSERT(!repos_relpath || *repos_relpath);
557  SVN_ERR_ASSERT(!repos_root_url || *repos_root_url);
558  SVN_ERR_ASSERT(!repos_uuid || *repos_uuid);
559  return SVN_NO_ERROR;
560}
561
562svn_error_t *
563svn_wc__node_get_pre_ng_status_data(svn_revnum_t *revision,
564                                   svn_revnum_t *changed_rev,
565                                   apr_time_t *changed_date,
566                                   const char **changed_author,
567                                   svn_wc_context_t *wc_ctx,
568                                   const char *local_abspath,
569                                   apr_pool_t *result_pool,
570                                   apr_pool_t *scratch_pool)
571{
572  svn_wc__db_status_t status;
573  svn_boolean_t have_base, have_more_work, have_work;
574
575  SVN_ERR(svn_wc__db_read_info(&status, NULL, revision, NULL, NULL, NULL,
576                               changed_rev, changed_date, changed_author,
577                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
578                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
579                               &have_base, &have_more_work, &have_work,
580                               wc_ctx->db, local_abspath,
581                               result_pool, scratch_pool));
582
583  if (!have_work
584      || ((!changed_rev || SVN_IS_VALID_REVNUM(*changed_rev))
585          && (!revision || SVN_IS_VALID_REVNUM(*revision)))
586      || ((status != svn_wc__db_status_added)
587          && (status != svn_wc__db_status_deleted)))
588    {
589      return SVN_NO_ERROR; /* We got everything we need */
590    }
591
592  if (have_base && !have_more_work)
593    SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, revision, NULL, NULL, NULL,
594                                     changed_rev, changed_date, changed_author,
595                                     NULL, NULL, NULL,
596                                     NULL, NULL, NULL, NULL,
597                                     wc_ctx->db, local_abspath,
598                                     result_pool, scratch_pool));
599  else if (status == svn_wc__db_status_deleted)
600    /* Check the information below a WORKING delete */
601    SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, changed_rev,
602                                          changed_date, changed_author, NULL,
603                                          NULL, NULL, NULL, NULL,
604                                          wc_ctx->db, local_abspath,
605                                          result_pool, scratch_pool));
606
607  return SVN_NO_ERROR;
608}
609
610svn_error_t *
611svn_wc__node_clear_dav_cache_recursive(svn_wc_context_t *wc_ctx,
612                                       const char *local_abspath,
613                                       apr_pool_t *scratch_pool)
614{
615  return svn_error_trace(svn_wc__db_base_clear_dav_cache_recursive(
616                              wc_ctx->db, local_abspath, scratch_pool));
617}
618
619
620svn_error_t *
621svn_wc__node_get_lock_tokens_recursive(apr_hash_t **lock_tokens,
622                                       svn_wc_context_t *wc_ctx,
623                                       const char *local_abspath,
624                                       apr_pool_t *result_pool,
625                                       apr_pool_t *scratch_pool)
626{
627  return svn_error_trace(svn_wc__db_base_get_lock_tokens_recursive(
628                              lock_tokens, wc_ctx->db, local_abspath,
629                              result_pool, scratch_pool));
630}
631
632svn_error_t *
633svn_wc__get_excluded_subtrees(apr_hash_t **server_excluded_subtrees,
634                              svn_wc_context_t *wc_ctx,
635                              const char *local_abspath,
636                              apr_pool_t *result_pool,
637                              apr_pool_t *scratch_pool)
638{
639  return svn_error_trace(
640           svn_wc__db_get_excluded_subtrees(server_excluded_subtrees,
641                                            wc_ctx->db,
642                                            local_abspath,
643                                            result_pool,
644                                            scratch_pool));
645}
646
647svn_error_t *
648svn_wc__internal_get_origin(svn_boolean_t *is_copy,
649                            svn_revnum_t *revision,
650                            const char **repos_relpath,
651                            const char **repos_root_url,
652                            const char **repos_uuid,
653                            svn_depth_t *depth,
654                            const char **copy_root_abspath,
655                            svn_wc__db_t *db,
656                            const char *local_abspath,
657                            svn_boolean_t scan_deleted,
658                            apr_pool_t *result_pool,
659                            apr_pool_t *scratch_pool)
660{
661  const char *original_repos_relpath;
662  const char *original_repos_root_url;
663  const char *original_repos_uuid;
664  svn_revnum_t original_revision;
665  svn_wc__db_status_t status;
666  svn_boolean_t have_more_work;
667  svn_boolean_t op_root;
668
669  const char *tmp_repos_relpath;
670
671  if (copy_root_abspath)
672    *copy_root_abspath = NULL;
673  if (!repos_relpath)
674    repos_relpath = &tmp_repos_relpath;
675
676  SVN_ERR(svn_wc__db_read_info(&status, NULL, revision, repos_relpath,
677                               repos_root_url, repos_uuid, NULL, NULL, NULL,
678                               depth, NULL, NULL,
679                               &original_repos_relpath,
680                               &original_repos_root_url,
681                               &original_repos_uuid, &original_revision,
682                               NULL, NULL, NULL, NULL, NULL, &op_root, NULL,
683                               NULL, NULL, &have_more_work, is_copy,
684                               db, local_abspath, result_pool, scratch_pool));
685
686  if (*repos_relpath)
687    {
688      return SVN_NO_ERROR; /* Returned BASE information */
689    }
690
691  if (status == svn_wc__db_status_deleted && !scan_deleted)
692    {
693      if (is_copy)
694        *is_copy = FALSE; /* Deletes are stored in working; default to FALSE */
695
696      return SVN_NO_ERROR; /* No info */
697    }
698
699  if (original_repos_relpath)
700    {
701      /* We an have a copy */
702      *repos_relpath = original_repos_relpath;
703      if (revision)
704        *revision = original_revision;
705      if (repos_root_url)
706        *repos_root_url = original_repos_root_url;
707      if (repos_uuid)
708        *repos_uuid = original_repos_uuid;
709
710      if (copy_root_abspath == NULL)
711        return SVN_NO_ERROR;
712      else if (op_root)
713        {
714          *copy_root_abspath = apr_pstrdup(result_pool, local_abspath);
715          return SVN_NO_ERROR;
716        }
717    }
718
719  {
720    svn_boolean_t scan_working = FALSE;
721
722    if (status == svn_wc__db_status_added
723        || (status == svn_wc__db_status_deleted && have_more_work))
724      scan_working = TRUE;
725
726    if (scan_working)
727      {
728        const char *op_root_abspath;
729
730        SVN_ERR(svn_wc__db_scan_addition(&status, &op_root_abspath, NULL,
731                                         NULL, NULL, &original_repos_relpath,
732                                         repos_root_url,
733                                         repos_uuid, revision,
734                                         db, local_abspath,
735                                         result_pool, scratch_pool));
736
737        if (status == svn_wc__db_status_added)
738          {
739            if (is_copy)
740              *is_copy = FALSE;
741            return SVN_NO_ERROR; /* Local addition */
742          }
743
744        /* We don't know how the following error condition can be fulfilled
745         * but we have seen that happening in the wild.  Better to create
746         * an error than a SEGFAULT. */
747        if (status == svn_wc__db_status_incomplete && !original_repos_relpath)
748          return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
749                               _("Incomplete copy information on path '%s'."),
750                                   svn_dirent_local_style(local_abspath,
751                                                          scratch_pool));
752
753        *repos_relpath = svn_relpath_join(
754                                original_repos_relpath,
755                                svn_dirent_skip_ancestor(op_root_abspath,
756                                                         local_abspath),
757                                result_pool);
758        if (copy_root_abspath)
759          *copy_root_abspath = op_root_abspath;
760      }
761    else /* Deleted, excluded, not-present, server-excluded, ... */
762      {
763        if (is_copy)
764          *is_copy = FALSE;
765
766        SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, revision, repos_relpath,
767                                         repos_root_url, repos_uuid, NULL,
768                                         NULL, NULL, NULL, NULL, NULL, NULL,
769                                         NULL, NULL, NULL,
770                                         db, local_abspath,
771                                         result_pool, scratch_pool));
772      }
773
774    return SVN_NO_ERROR;
775  }
776}
777
778svn_error_t *
779svn_wc__node_get_origin(svn_boolean_t *is_copy,
780                        svn_revnum_t *revision,
781                        const char **repos_relpath,
782                        const char **repos_root_url,
783                        const char **repos_uuid,
784                        svn_depth_t *depth,
785                        const char **copy_root_abspath,
786                        svn_wc_context_t *wc_ctx,
787                        const char *local_abspath,
788                        svn_boolean_t scan_deleted,
789                        apr_pool_t *result_pool,
790                        apr_pool_t *scratch_pool)
791{
792  return svn_error_trace(svn_wc__internal_get_origin(is_copy, revision,
793                           repos_relpath, repos_root_url, repos_uuid,
794                           depth, copy_root_abspath,
795                           wc_ctx->db, local_abspath, scan_deleted,
796                           result_pool, scratch_pool));
797}
798
799svn_error_t *
800svn_wc__node_get_commit_status(svn_boolean_t *added,
801                               svn_boolean_t *deleted,
802                               svn_boolean_t *is_replace_root,
803                               svn_boolean_t *is_op_root,
804                               svn_revnum_t *revision,
805                               svn_revnum_t *original_revision,
806                               const char **original_repos_relpath,
807                               svn_wc_context_t *wc_ctx,
808                               const char *local_abspath,
809                               apr_pool_t *result_pool,
810                               apr_pool_t *scratch_pool)
811{
812  svn_wc__db_status_t status;
813  svn_boolean_t have_base;
814  svn_boolean_t have_more_work;
815  svn_boolean_t op_root;
816
817  /* ### All of this should be handled inside a single read transaction */
818  SVN_ERR(svn_wc__db_read_info(&status, NULL, revision, NULL,
819                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
820                               original_repos_relpath, NULL, NULL,
821                               original_revision, NULL, NULL, NULL,
822                               NULL, NULL,
823                               &op_root, NULL, NULL,
824                               &have_base, &have_more_work, NULL,
825                               wc_ctx->db, local_abspath,
826                               result_pool, scratch_pool));
827
828  if (added)
829    *added = (status == svn_wc__db_status_added);
830  if (deleted)
831    *deleted = (status == svn_wc__db_status_deleted);
832  if (is_op_root)
833    *is_op_root = op_root;
834
835  if (is_replace_root)
836    {
837      if (status == svn_wc__db_status_added
838          && op_root
839          && (have_base || have_more_work))
840        SVN_ERR(svn_wc__db_node_check_replace(is_replace_root, NULL, NULL,
841                                              wc_ctx->db, local_abspath,
842                                              scratch_pool));
843      else
844        *is_replace_root = FALSE;
845    }
846
847  /* Retrieve some information from BASE which is needed for replacing
848     and/or deleting BASE nodes. */
849  if (have_base
850      && !have_more_work
851      && op_root
852      && (revision && !SVN_IS_VALID_REVNUM(*revision)))
853    {
854      SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, revision, NULL, NULL, NULL,
855                                       NULL, NULL, NULL, NULL, NULL, NULL,
856                                       NULL, NULL, NULL, NULL,
857                                       wc_ctx->db, local_abspath,
858                                       scratch_pool, scratch_pool));
859    }
860
861  return SVN_NO_ERROR;
862}
863
864svn_error_t *
865svn_wc__node_get_md5_from_sha1(const svn_checksum_t **md5_checksum,
866                               svn_wc_context_t *wc_ctx,
867                               const char *wri_abspath,
868                               const svn_checksum_t *sha1_checksum,
869                               apr_pool_t *result_pool,
870                               apr_pool_t *scratch_pool)
871{
872  return svn_error_trace(svn_wc__db_pristine_get_md5(md5_checksum,
873                                                     wc_ctx->db,
874                                                     wri_abspath,
875                                                     sha1_checksum,
876                                                     result_pool,
877                                                     scratch_pool));
878}
879
880svn_error_t *
881svn_wc__get_not_present_descendants(const apr_array_header_t **descendants,
882                                    svn_wc_context_t *wc_ctx,
883                                    const char *local_abspath,
884                                    apr_pool_t *result_pool,
885                                    apr_pool_t *scratch_pool)
886{
887  return svn_error_trace(
888                svn_wc__db_get_not_present_descendants(descendants,
889                                                       wc_ctx->db,
890                                                       local_abspath,
891                                                       result_pool,
892                                                       scratch_pool));
893}
894
895svn_error_t *
896svn_wc__rename_wc(svn_wc_context_t *wc_ctx,
897                  const char *from_abspath,
898                  const char *dst_abspath,
899                  apr_pool_t *scratch_pool)
900{
901  const char *wcroot_abspath;
902  SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, wc_ctx->db, from_abspath,
903                                scratch_pool, scratch_pool));
904
905  if (! strcmp(from_abspath, wcroot_abspath))
906    {
907      SVN_ERR(svn_wc__db_drop_root(wc_ctx->db, wcroot_abspath, scratch_pool));
908
909      SVN_ERR(svn_io_file_rename2(from_abspath, dst_abspath, FALSE,
910                                  scratch_pool));
911    }
912  else
913    return svn_error_createf(
914                    SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
915                    _("'%s' is not the root of the working copy '%s'"),
916                    svn_dirent_local_style(from_abspath, scratch_pool),
917                    svn_dirent_local_style(wcroot_abspath, scratch_pool));
918
919  return SVN_NO_ERROR;
920}
921
922svn_error_t *
923svn_wc__check_for_obstructions(svn_wc_notify_state_t *obstruction_state,
924                               svn_node_kind_t *kind,
925                               svn_boolean_t *deleted,
926                               svn_boolean_t *excluded,
927                               svn_depth_t *parent_depth,
928                               svn_wc_context_t *wc_ctx,
929                               const char *local_abspath,
930                               svn_boolean_t no_wcroot_check,
931                               apr_pool_t *scratch_pool)
932{
933  svn_wc__db_status_t status;
934  svn_node_kind_t db_kind;
935  svn_node_kind_t disk_kind;
936  svn_error_t *err;
937
938  *obstruction_state = svn_wc_notify_state_inapplicable;
939  if (kind)
940    *kind = svn_node_none;
941  if (deleted)
942    *deleted = FALSE;
943  if (excluded)
944    *excluded = FALSE;
945  if (parent_depth)
946    *parent_depth = svn_depth_unknown;
947
948  SVN_ERR(svn_io_check_path(local_abspath, &disk_kind, scratch_pool));
949
950  err = svn_wc__db_read_info(&status, &db_kind, NULL, NULL, NULL, NULL, NULL,
951                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
952                             NULL, NULL, NULL, NULL, NULL, NULL, NULL,
953                             NULL, NULL, NULL, NULL, NULL,
954                             wc_ctx->db, local_abspath,
955                             scratch_pool, scratch_pool);
956
957  if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
958    {
959      svn_error_clear(err);
960
961      if (disk_kind != svn_node_none)
962        {
963          /* Nothing in the DB, but something on disk */
964          *obstruction_state = svn_wc_notify_state_obstructed;
965          return SVN_NO_ERROR;
966        }
967
968      err = svn_wc__db_read_info(&status, &db_kind, NULL, NULL, NULL, NULL,
969                                 NULL, NULL, NULL, parent_depth, NULL, NULL,
970                                 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
971                                 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
972                                 NULL,
973                                 wc_ctx->db, svn_dirent_dirname(local_abspath,
974                                                                scratch_pool),
975                                 scratch_pool, scratch_pool);
976
977      if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
978        {
979          svn_error_clear(err);
980          /* No versioned parent; we can't add a node here */
981          *obstruction_state = svn_wc_notify_state_obstructed;
982          return SVN_NO_ERROR;
983        }
984      else
985        SVN_ERR(err);
986
987      if (db_kind != svn_node_dir
988          || (status != svn_wc__db_status_normal
989              && status != svn_wc__db_status_added))
990        {
991          /* The parent doesn't allow nodes to be added below it */
992          *obstruction_state = svn_wc_notify_state_obstructed;
993        }
994
995      return SVN_NO_ERROR;
996    }
997  else
998    SVN_ERR(err);
999
1000  /* Check for obstructing working copies */
1001  if (!no_wcroot_check
1002      && db_kind == svn_node_dir
1003      && status == svn_wc__db_status_normal)
1004    {
1005      svn_boolean_t is_root;
1006      SVN_ERR(svn_wc__db_is_wcroot(&is_root, wc_ctx->db, local_abspath,
1007                                   scratch_pool));
1008
1009      if (is_root)
1010        {
1011          /* Callers should handle this as unversioned */
1012          *obstruction_state = svn_wc_notify_state_obstructed;
1013          return SVN_NO_ERROR;
1014        }
1015    }
1016
1017  if (kind)
1018    SVN_ERR(convert_db_kind_to_node_kind(kind, db_kind, status, FALSE));
1019
1020  switch (status)
1021    {
1022      case svn_wc__db_status_deleted:
1023        if (deleted)
1024          *deleted = TRUE;
1025        /* Fall through to svn_wc__db_status_not_present */
1026      case svn_wc__db_status_not_present:
1027        if (disk_kind != svn_node_none)
1028          *obstruction_state = svn_wc_notify_state_obstructed;
1029        break;
1030
1031      case svn_wc__db_status_excluded:
1032      case svn_wc__db_status_server_excluded:
1033        if (excluded)
1034          *excluded = TRUE;
1035        /* fall through */
1036      case svn_wc__db_status_incomplete:
1037        *obstruction_state = svn_wc_notify_state_missing;
1038        break;
1039
1040      case svn_wc__db_status_added:
1041      case svn_wc__db_status_normal:
1042        if (disk_kind == svn_node_none)
1043          *obstruction_state = svn_wc_notify_state_missing;
1044        else
1045          {
1046            svn_node_kind_t expected_kind;
1047
1048            SVN_ERR(convert_db_kind_to_node_kind(&expected_kind, db_kind,
1049                                                 status, FALSE));
1050
1051            if (disk_kind != expected_kind)
1052              *obstruction_state = svn_wc_notify_state_obstructed;
1053          }
1054        break;
1055      default:
1056        SVN_ERR_MALFUNCTION();
1057    }
1058
1059  return SVN_NO_ERROR;
1060}
1061
1062
1063svn_error_t *
1064svn_wc__node_was_moved_away(const char **moved_to_abspath,
1065                            const char **op_root_abspath,
1066                            svn_wc_context_t *wc_ctx,
1067                            const char *local_abspath,
1068                            apr_pool_t *result_pool,
1069                            apr_pool_t *scratch_pool)
1070{
1071  svn_wc__db_status_t status;
1072
1073  if (moved_to_abspath)
1074    *moved_to_abspath = NULL;
1075  if (op_root_abspath)
1076    *op_root_abspath = NULL;
1077
1078  SVN_ERR(svn_wc__db_read_info(&status,
1079                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1080                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1081                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1082                               NULL, NULL, NULL, NULL, NULL,
1083                               wc_ctx->db, local_abspath,
1084                               scratch_pool, scratch_pool));
1085
1086  if (status == svn_wc__db_status_deleted)
1087    SVN_ERR(svn_wc__db_scan_deletion(NULL, moved_to_abspath, NULL,
1088                                     op_root_abspath, wc_ctx->db,
1089                                     local_abspath,
1090                                     result_pool, scratch_pool));
1091
1092  return SVN_NO_ERROR;
1093}
1094
1095
1096svn_error_t *
1097svn_wc__node_was_moved_here(const char **moved_from_abspath,
1098                            const char **delete_op_root_abspath,
1099                            svn_wc_context_t *wc_ctx,
1100                            const char *local_abspath,
1101                            apr_pool_t *result_pool,
1102                            apr_pool_t *scratch_pool)
1103{
1104  svn_error_t *err;
1105
1106  if (moved_from_abspath)
1107    *moved_from_abspath = NULL;
1108  if (delete_op_root_abspath)
1109    *delete_op_root_abspath = NULL;
1110
1111  err = svn_wc__db_scan_moved(moved_from_abspath, NULL, NULL,
1112                              delete_op_root_abspath,
1113                              wc_ctx->db, local_abspath,
1114                              result_pool, scratch_pool);
1115
1116  if (err)
1117    {
1118      /* Return error for not added nodes */
1119      if (err->apr_err != SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
1120        return svn_error_trace(err);
1121
1122      /* Path not moved here */
1123      svn_error_clear(err);
1124      return SVN_NO_ERROR;
1125    }
1126
1127  return SVN_NO_ERROR;
1128}
1129
1130svn_error_t *
1131svn_wc__find_working_nodes_with_basename(apr_array_header_t **abspaths,
1132                                         const char *wri_abspath,
1133                                         const char *basename,
1134                                         svn_node_kind_t kind,
1135                                         svn_wc_context_t *wc_ctx,
1136                                         apr_pool_t *result_pool,
1137                                         apr_pool_t *scratch_pool)
1138{
1139  return svn_error_trace(svn_wc__db_find_working_nodes_with_basename(
1140                           abspaths, wc_ctx->db, wri_abspath, basename, kind,
1141                           result_pool, scratch_pool));
1142}
1143
1144svn_error_t *
1145svn_wc__find_copies_of_repos_path(apr_array_header_t **abspaths,
1146                                  const char *wri_abspath,
1147                                  const char *repos_relpath,
1148                                  svn_node_kind_t kind,
1149                                  svn_wc_context_t *wc_ctx,
1150                                  apr_pool_t *result_pool,
1151                                  apr_pool_t *scratch_pool)
1152{
1153  return svn_error_trace(svn_wc__db_find_copies_of_repos_path(
1154                           abspaths, wc_ctx->db, wri_abspath, repos_relpath,
1155                           kind, result_pool, scratch_pool));
1156}
1157