node.c revision 299742
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_rename(from_abspath, dst_abspath, scratch_pool));
910    }
911  else
912    return svn_error_createf(
913                    SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
914                    _("'%s' is not the root of the working copy '%s'"),
915                    svn_dirent_local_style(from_abspath, scratch_pool),
916                    svn_dirent_local_style(wcroot_abspath, scratch_pool));
917
918  return SVN_NO_ERROR;
919}
920
921svn_error_t *
922svn_wc__check_for_obstructions(svn_wc_notify_state_t *obstruction_state,
923                               svn_node_kind_t *kind,
924                               svn_boolean_t *deleted,
925                               svn_boolean_t *excluded,
926                               svn_depth_t *parent_depth,
927                               svn_wc_context_t *wc_ctx,
928                               const char *local_abspath,
929                               svn_boolean_t no_wcroot_check,
930                               apr_pool_t *scratch_pool)
931{
932  svn_wc__db_status_t status;
933  svn_node_kind_t db_kind;
934  svn_node_kind_t disk_kind;
935  svn_error_t *err;
936
937  *obstruction_state = svn_wc_notify_state_inapplicable;
938  if (kind)
939    *kind = svn_node_none;
940  if (deleted)
941    *deleted = FALSE;
942  if (excluded)
943    *excluded = FALSE;
944  if (parent_depth)
945    *parent_depth = svn_depth_unknown;
946
947  SVN_ERR(svn_io_check_path(local_abspath, &disk_kind, scratch_pool));
948
949  err = svn_wc__db_read_info(&status, &db_kind, NULL, NULL, NULL, NULL, NULL,
950                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
951                             NULL, NULL, NULL, NULL, NULL, NULL, NULL,
952                             NULL, NULL, NULL, NULL, NULL,
953                             wc_ctx->db, local_abspath,
954                             scratch_pool, scratch_pool);
955
956  if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
957    {
958      svn_error_clear(err);
959
960      if (disk_kind != svn_node_none)
961        {
962          /* Nothing in the DB, but something on disk */
963          *obstruction_state = svn_wc_notify_state_obstructed;
964          return SVN_NO_ERROR;
965        }
966
967      err = svn_wc__db_read_info(&status, &db_kind, NULL, NULL, NULL, NULL,
968                                 NULL, NULL, NULL, parent_depth, NULL, NULL,
969                                 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
970                                 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
971                                 NULL,
972                                 wc_ctx->db, svn_dirent_dirname(local_abspath,
973                                                                scratch_pool),
974                                 scratch_pool, scratch_pool);
975
976      if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
977        {
978          svn_error_clear(err);
979          /* No versioned parent; we can't add a node here */
980          *obstruction_state = svn_wc_notify_state_obstructed;
981          return SVN_NO_ERROR;
982        }
983      else
984        SVN_ERR(err);
985
986      if (db_kind != svn_node_dir
987          || (status != svn_wc__db_status_normal
988              && status != svn_wc__db_status_added))
989        {
990          /* The parent doesn't allow nodes to be added below it */
991          *obstruction_state = svn_wc_notify_state_obstructed;
992        }
993
994      return SVN_NO_ERROR;
995    }
996  else
997    SVN_ERR(err);
998
999  /* Check for obstructing working copies */
1000  if (!no_wcroot_check
1001      && db_kind == svn_node_dir
1002      && status == svn_wc__db_status_normal)
1003    {
1004      svn_boolean_t is_root;
1005      SVN_ERR(svn_wc__db_is_wcroot(&is_root, wc_ctx->db, local_abspath,
1006                                   scratch_pool));
1007
1008      if (is_root)
1009        {
1010          /* Callers should handle this as unversioned */
1011          *obstruction_state = svn_wc_notify_state_obstructed;
1012          return SVN_NO_ERROR;
1013        }
1014    }
1015
1016  if (kind)
1017    SVN_ERR(convert_db_kind_to_node_kind(kind, db_kind, status, FALSE));
1018
1019  switch (status)
1020    {
1021      case svn_wc__db_status_deleted:
1022        if (deleted)
1023          *deleted = TRUE;
1024        /* Fall through to svn_wc__db_status_not_present */
1025      case svn_wc__db_status_not_present:
1026        if (disk_kind != svn_node_none)
1027          *obstruction_state = svn_wc_notify_state_obstructed;
1028        break;
1029
1030      case svn_wc__db_status_excluded:
1031      case svn_wc__db_status_server_excluded:
1032        if (excluded)
1033          *excluded = TRUE;
1034        /* fall through */
1035      case svn_wc__db_status_incomplete:
1036        *obstruction_state = svn_wc_notify_state_missing;
1037        break;
1038
1039      case svn_wc__db_status_added:
1040      case svn_wc__db_status_normal:
1041        if (disk_kind == svn_node_none)
1042          *obstruction_state = svn_wc_notify_state_missing;
1043        else
1044          {
1045            svn_node_kind_t expected_kind;
1046
1047            SVN_ERR(convert_db_kind_to_node_kind(&expected_kind, db_kind,
1048                                                 status, FALSE));
1049
1050            if (disk_kind != expected_kind)
1051              *obstruction_state = svn_wc_notify_state_obstructed;
1052          }
1053        break;
1054      default:
1055        SVN_ERR_MALFUNCTION();
1056    }
1057
1058  return SVN_NO_ERROR;
1059}
1060
1061
1062svn_error_t *
1063svn_wc__node_was_moved_away(const char **moved_to_abspath,
1064                            const char **op_root_abspath,
1065                            svn_wc_context_t *wc_ctx,
1066                            const char *local_abspath,
1067                            apr_pool_t *result_pool,
1068                            apr_pool_t *scratch_pool)
1069{
1070  svn_wc__db_status_t status;
1071
1072  if (moved_to_abspath)
1073    *moved_to_abspath = NULL;
1074  if (op_root_abspath)
1075    *op_root_abspath = NULL;
1076
1077  SVN_ERR(svn_wc__db_read_info(&status,
1078                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1079                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1080                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1081                               NULL, NULL, NULL, NULL, NULL,
1082                               wc_ctx->db, local_abspath,
1083                               scratch_pool, scratch_pool));
1084
1085  if (status == svn_wc__db_status_deleted)
1086    SVN_ERR(svn_wc__db_scan_deletion(NULL, moved_to_abspath, NULL,
1087                                     op_root_abspath, wc_ctx->db,
1088                                     local_abspath,
1089                                     result_pool, scratch_pool));
1090
1091  return SVN_NO_ERROR;
1092}
1093
1094
1095svn_error_t *
1096svn_wc__node_was_moved_here(const char **moved_from_abspath,
1097                            const char **delete_op_root_abspath,
1098                            svn_wc_context_t *wc_ctx,
1099                            const char *local_abspath,
1100                            apr_pool_t *result_pool,
1101                            apr_pool_t *scratch_pool)
1102{
1103  svn_error_t *err;
1104
1105  if (moved_from_abspath)
1106    *moved_from_abspath = NULL;
1107  if (delete_op_root_abspath)
1108    *delete_op_root_abspath = NULL;
1109
1110  err = svn_wc__db_scan_moved(moved_from_abspath, NULL, NULL,
1111                              delete_op_root_abspath,
1112                              wc_ctx->db, local_abspath,
1113                              result_pool, scratch_pool);
1114
1115  if (err)
1116    {
1117      /* Return error for not added nodes */
1118      if (err->apr_err != SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
1119        return svn_error_trace(err);
1120
1121      /* Path not moved here */
1122      svn_error_clear(err);
1123      return SVN_NO_ERROR;
1124    }
1125
1126  return SVN_NO_ERROR;
1127}
1128