entries.c revision 289166
1/*
2 * entries.c :  manipulating the administrative `entries' file.
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#include <string.h>
25#include <assert.h>
26
27#include <apr_strings.h>
28
29#include "svn_error.h"
30#include "svn_types.h"
31#include "svn_time.h"
32#include "svn_pools.h"
33#include "svn_dirent_uri.h"
34#include "svn_path.h"
35#include "svn_ctype.h"
36#include "svn_string.h"
37#include "svn_hash.h"
38
39#include "wc.h"
40#include "adm_files.h"
41#include "conflicts.h"
42#include "entries.h"
43#include "lock.h"
44#include "tree_conflicts.h"
45#include "wc_db.h"
46#include "wc-queries.h"  /* for STMT_*  */
47
48#include "svn_private_config.h"
49#include "private/svn_wc_private.h"
50#include "private/svn_sqlite.h"
51
52#define MAYBE_ALLOC(x,p) ((x) ? (x) : apr_pcalloc((p), sizeof(*(x))))
53
54
55/* Temporary structures which mirror the tables in wc-metadata.sql.
56   For detailed descriptions of each field, see that file. */
57typedef struct db_node_t {
58  apr_int64_t wc_id;
59  const char *local_relpath;
60  int op_depth;
61  apr_int64_t repos_id;
62  const char *repos_relpath;
63  const char *parent_relpath;
64  svn_wc__db_status_t presence;
65  svn_revnum_t revision;
66  svn_node_kind_t kind;
67  svn_checksum_t *checksum;
68  svn_filesize_t recorded_size;
69  svn_revnum_t changed_rev;
70  apr_time_t changed_date;
71  const char *changed_author;
72  svn_depth_t depth;
73  apr_time_t recorded_time;
74  apr_hash_t *properties;
75  svn_boolean_t file_external;
76  apr_array_header_t *inherited_props;
77} db_node_t;
78
79typedef struct db_actual_node_t {
80  apr_int64_t wc_id;
81  const char *local_relpath;
82  const char *parent_relpath;
83  apr_hash_t *properties;
84  const char *conflict_old;
85  const char *conflict_new;
86  const char *conflict_working;
87  const char *prop_reject;
88  const char *changelist;
89  /* ### enum for text_mod */
90  const char *tree_conflict_data;
91} db_actual_node_t;
92
93
94
95/*** reading and writing the entries file ***/
96
97
98/* */
99static svn_wc_entry_t *
100alloc_entry(apr_pool_t *pool)
101{
102  svn_wc_entry_t *entry = apr_pcalloc(pool, sizeof(*entry));
103  entry->revision = SVN_INVALID_REVNUM;
104  entry->copyfrom_rev = SVN_INVALID_REVNUM;
105  entry->cmt_rev = SVN_INVALID_REVNUM;
106  entry->kind = svn_node_none;
107  entry->working_size = SVN_WC_ENTRY_WORKING_SIZE_UNKNOWN;
108  entry->depth = svn_depth_infinity;
109  entry->file_external_peg_rev.kind = svn_opt_revision_unspecified;
110  entry->file_external_rev.kind = svn_opt_revision_unspecified;
111  return entry;
112}
113
114
115/* Is the entry in a 'hidden' state in the sense of the 'show_hidden'
116 * switches on svn_wc_entries_read(), svn_wc_walk_entries*(), etc.? */
117svn_error_t *
118svn_wc__entry_is_hidden(svn_boolean_t *hidden, const svn_wc_entry_t *entry)
119{
120  /* In English, the condition is: "the entry is not present, and I haven't
121     scheduled something over the top of it."  */
122  if (entry->deleted
123      || entry->absent
124      || entry->depth == svn_depth_exclude)
125    {
126      /* These kinds of nodes cannot be marked for deletion (which also
127         means no "replace" either).  */
128      SVN_ERR_ASSERT(entry->schedule == svn_wc_schedule_add
129                     || entry->schedule == svn_wc_schedule_normal);
130
131      /* Hidden if something hasn't been added over it.
132
133         ### is this even possible with absent or excluded nodes?  */
134      *hidden = entry->schedule != svn_wc_schedule_add;
135    }
136  else
137    *hidden = FALSE;
138
139  return SVN_NO_ERROR;
140}
141
142
143/* Hit the database to check the file external information for the given
144   entry.  The entry will be modified in place. */
145static svn_error_t *
146check_file_external(svn_wc_entry_t *entry,
147                    svn_wc__db_t *db,
148                    const char *local_abspath,
149                    const char *wri_abspath,
150                    apr_pool_t *result_pool,
151                    apr_pool_t *scratch_pool)
152{
153  svn_wc__db_status_t status;
154  svn_node_kind_t kind;
155  const char *repos_relpath;
156  svn_revnum_t peg_revision;
157  svn_revnum_t revision;
158  svn_error_t *err;
159
160  err = svn_wc__db_external_read(&status, &kind, NULL, NULL, NULL,
161                                 &repos_relpath, &peg_revision, &revision,
162                                 db, local_abspath, wri_abspath,
163                                 result_pool, scratch_pool);
164
165  if (err)
166    {
167      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
168        return svn_error_trace(err);
169
170      svn_error_clear(err);
171      return SVN_NO_ERROR;
172    }
173
174  if (status == svn_wc__db_status_normal
175      && kind == svn_node_file)
176    {
177      entry->file_external_path = repos_relpath;
178      if (SVN_IS_VALID_REVNUM(peg_revision))
179        {
180          entry->file_external_peg_rev.kind = svn_opt_revision_number;
181          entry->file_external_peg_rev.value.number = peg_revision;
182          entry->file_external_rev = entry->file_external_peg_rev;
183        }
184      if (SVN_IS_VALID_REVNUM(revision))
185        {
186          entry->file_external_rev.kind = svn_opt_revision_number;
187          entry->file_external_rev.value.number = revision;
188        }
189    }
190
191  return SVN_NO_ERROR;
192}
193
194
195/* Fill in the following fields of ENTRY:
196
197     REVISION
198     REPOS
199     UUID
200     CMT_REV
201     CMT_DATE
202     CMT_AUTHOR
203     DEPTH
204     DELETED
205
206   Return: KIND, REPOS_RELPATH, CHECKSUM
207*/
208static svn_error_t *
209get_info_for_deleted(svn_wc_entry_t *entry,
210                     svn_node_kind_t *kind,
211                     const char **repos_relpath,
212                     const svn_checksum_t **checksum,
213                     svn_wc__db_lock_t **lock,
214                     svn_wc__db_t *db,
215                     const char *entry_abspath,
216                     const svn_wc_entry_t *parent_entry,
217                     svn_boolean_t have_base,
218                     svn_boolean_t have_more_work,
219                     apr_pool_t *result_pool,
220                     apr_pool_t *scratch_pool)
221{
222  if (have_base && !have_more_work)
223    {
224      /* This is the delete of a BASE node */
225      SVN_ERR(svn_wc__db_base_get_info(NULL, kind,
226                                       &entry->revision,
227                                       repos_relpath,
228                                       &entry->repos,
229                                       &entry->uuid,
230                                       &entry->cmt_rev,
231                                       &entry->cmt_date,
232                                       &entry->cmt_author,
233                                       &entry->depth,
234                                       checksum,
235                                       NULL,
236                                       lock,
237                                       &entry->has_props, NULL,
238                                       NULL,
239                                       db,
240                                       entry_abspath,
241                                       result_pool,
242                                       scratch_pool));
243    }
244  else
245    {
246      const char *work_del_abspath;
247      const char *parent_repos_relpath;
248      const char *parent_abspath;
249
250      /* This is a deleted child of a copy/move-here,
251         so we need to scan up the WORKING tree to find the root of
252         the deletion. Then examine its parent to discover its
253         future location in the repository.  */
254      SVN_ERR(svn_wc__db_read_pristine_info(NULL, kind,
255                                            &entry->cmt_rev,
256                                            &entry->cmt_date,
257                                            &entry->cmt_author,
258                                            &entry->depth,
259                                            checksum,
260                                            NULL,
261                                            &entry->has_props, NULL,
262                                            db,
263                                            entry_abspath,
264                                            result_pool,
265                                            scratch_pool));
266      /* working_size and text_time unavailable */
267
268     SVN_ERR(svn_wc__db_scan_deletion(NULL,
269                                      NULL,
270                                      &work_del_abspath, NULL,
271                                      db, entry_abspath,
272                                      scratch_pool, scratch_pool));
273
274      SVN_ERR_ASSERT(work_del_abspath != NULL);
275      parent_abspath = svn_dirent_dirname(work_del_abspath, scratch_pool);
276
277      /* The parent directory of the delete root must be added, so we
278         can find the required information there */
279      SVN_ERR(svn_wc__db_scan_addition(NULL, NULL,
280                                       &parent_repos_relpath,
281                                       &entry->repos,
282                                       &entry->uuid,
283                                       NULL, NULL, NULL, NULL,
284                                       db, parent_abspath,
285                                       result_pool, scratch_pool));
286
287      /* Now glue it all together */
288      *repos_relpath = svn_relpath_join(parent_repos_relpath,
289                                        svn_dirent_is_child(parent_abspath,
290                                                            entry_abspath,
291                                                            NULL),
292                                        result_pool);
293
294
295      /* Even though this is the delete of a WORKING node, there might still
296         be a BASE node somewhere below with an interesting revision */
297      if (have_base)
298        {
299          svn_wc__db_status_t status;
300          SVN_ERR(svn_wc__db_base_get_info(&status, NULL, &entry->revision,
301                                           NULL, NULL, NULL, NULL, NULL, NULL,
302                                           NULL, NULL, NULL, lock, NULL, NULL,
303                                           NULL,
304                                           db, entry_abspath,
305                                           result_pool, scratch_pool));
306
307          if (status == svn_wc__db_status_not_present)
308            entry->deleted = TRUE;
309        }
310    }
311
312  /* Do some extra work for the child nodes.  */
313  if (!SVN_IS_VALID_REVNUM(entry->revision) && parent_entry != NULL)
314    {
315      /* For child nodes without a revision, pick up the parent's
316         revision.  */
317      entry->revision = parent_entry->revision;
318    }
319
320  return SVN_NO_ERROR;
321}
322
323
324/*
325 * Encode tree conflict descriptions into a single string.
326 *
327 * Set *CONFLICT_DATA to a string, allocated in POOL, that encodes the tree
328 * conflicts in CONFLICTS in a form suitable for storage in a single string
329 * field in a WC entry. CONFLICTS is a hash of zero or more pointers to
330 * svn_wc_conflict_description2_t objects, index by their basenames. All of the
331 * conflict victim paths must be siblings.
332 *
333 * Do all allocations in POOL.
334 *
335 * @see svn_wc__read_tree_conflicts()
336 */
337static svn_error_t *
338write_tree_conflicts(const char **conflict_data,
339                     apr_hash_t *conflicts,
340                     apr_pool_t *pool)
341{
342  svn_skel_t *skel = svn_skel__make_empty_list(pool);
343  apr_hash_index_t *hi;
344
345  for (hi = apr_hash_first(pool, conflicts); hi; hi = apr_hash_next(hi))
346    {
347      svn_skel_t *c_skel;
348
349      SVN_ERR(svn_wc__serialize_conflict(&c_skel, svn__apr_hash_index_val(hi),
350                                         pool, pool));
351      svn_skel__prepend(c_skel, skel);
352    }
353
354  *conflict_data = svn_skel__unparse(skel, pool)->data;
355
356  return SVN_NO_ERROR;
357}
358
359
360/* Read one entry from wc_db. It will be allocated in RESULT_POOL and
361   returned in *NEW_ENTRY.
362
363   DIR_ABSPATH is the name of the directory to read this entry from, and
364   it will be named NAME (use "" for "this dir").
365
366   DB specifies the wc_db database, and WC_ID specifies which working copy
367   this information is being read from.
368
369   If this node is "this dir", then PARENT_ENTRY should be NULL. Otherwise,
370   it should refer to the entry for the child's parent directory.
371
372   Temporary allocations are made in SCRATCH_POOL.  */
373static svn_error_t *
374read_one_entry(const svn_wc_entry_t **new_entry,
375               svn_wc__db_t *db,
376               apr_int64_t wc_id,
377               const char *dir_abspath,
378               const char *name,
379               const svn_wc_entry_t *parent_entry,
380               apr_pool_t *result_pool,
381               apr_pool_t *scratch_pool)
382{
383  svn_node_kind_t kind;
384  svn_wc__db_status_t status;
385  svn_wc__db_lock_t *lock;
386  const char *repos_relpath;
387  const svn_checksum_t *checksum;
388  svn_filesize_t translated_size;
389  svn_wc_entry_t *entry = alloc_entry(result_pool);
390  const char *entry_abspath;
391  const char *original_repos_relpath;
392  const char *original_root_url;
393  svn_boolean_t conflicted;
394  svn_boolean_t have_base;
395  svn_boolean_t have_more_work;
396
397  entry->name = name;
398
399  entry_abspath = svn_dirent_join(dir_abspath, entry->name, scratch_pool);
400
401  SVN_ERR(svn_wc__db_read_info(
402            &status,
403            &kind,
404            &entry->revision,
405            &repos_relpath,
406            &entry->repos,
407            &entry->uuid,
408            &entry->cmt_rev,
409            &entry->cmt_date,
410            &entry->cmt_author,
411            &entry->depth,
412            &checksum,
413            NULL,
414            &original_repos_relpath,
415            &original_root_url,
416            NULL,
417            &entry->copyfrom_rev,
418            &lock,
419            &translated_size,
420            &entry->text_time,
421            &entry->changelist,
422            &conflicted,
423            NULL /* op_root */,
424            &entry->has_props /* have_props */,
425            &entry->has_prop_mods /* props_mod */,
426            &have_base,
427            &have_more_work,
428            NULL /* have_work */,
429            db,
430            entry_abspath,
431            result_pool,
432            scratch_pool));
433
434  if (entry->has_prop_mods)
435    entry->has_props = TRUE;
436
437  if (strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR) == 0)
438    {
439      /* get the tree conflict data. */
440      apr_hash_t *tree_conflicts = NULL;
441      const apr_array_header_t *conflict_victims;
442      int k;
443
444      SVN_ERR(svn_wc__db_read_conflict_victims(&conflict_victims, db,
445                                               dir_abspath,
446                                               scratch_pool,
447                                               scratch_pool));
448
449      for (k = 0; k < conflict_victims->nelts; k++)
450        {
451          int j;
452          const apr_array_header_t *child_conflicts;
453          const char *child_name;
454          const char *child_abspath;
455
456          child_name = APR_ARRAY_IDX(conflict_victims, k, const char *);
457          child_abspath = svn_dirent_join(dir_abspath, child_name,
458                                          scratch_pool);
459
460          SVN_ERR(svn_wc__read_conflicts(&child_conflicts,
461                                         db, child_abspath,
462                                         FALSE /* create tempfiles */,
463                                         scratch_pool, scratch_pool));
464
465          for (j = 0; j < child_conflicts->nelts; j++)
466            {
467              const svn_wc_conflict_description2_t *conflict =
468                APR_ARRAY_IDX(child_conflicts, j,
469                              svn_wc_conflict_description2_t *);
470
471              if (conflict->kind == svn_wc_conflict_kind_tree)
472                {
473                  if (!tree_conflicts)
474                    tree_conflicts = apr_hash_make(scratch_pool);
475                  svn_hash_sets(tree_conflicts, child_name, conflict);
476                }
477            }
478        }
479
480      if (tree_conflicts)
481        {
482          SVN_ERR(write_tree_conflicts(&entry->tree_conflict_data,
483                                       tree_conflicts, result_pool));
484        }
485    }
486
487  if (status == svn_wc__db_status_normal
488      || status == svn_wc__db_status_incomplete)
489    {
490      /* Plain old BASE node.  */
491      entry->schedule = svn_wc_schedule_normal;
492
493      /* Grab inherited repository information, if necessary. */
494      if (repos_relpath == NULL)
495        {
496          SVN_ERR(svn_wc__db_scan_base_repos(&repos_relpath,
497                                             &entry->repos,
498                                             &entry->uuid,
499                                             db,
500                                             entry_abspath,
501                                             result_pool,
502                                             scratch_pool));
503        }
504
505      entry->incomplete = (status == svn_wc__db_status_incomplete);
506    }
507  else if (status == svn_wc__db_status_deleted)
508    {
509      svn_node_kind_t path_kind;
510
511      /* ### we don't have to worry about moves, so this is a delete. */
512      entry->schedule = svn_wc_schedule_delete;
513
514      /* If there are multiple working layers or no BASE layer, then
515         this is a WORKING delete or WORKING not-present. */
516      if (have_more_work || !have_base)
517        entry->copied = TRUE;
518      else if (have_base && !have_more_work)
519        entry->copied = FALSE;
520      else
521        {
522          const char *work_del_abspath;
523          SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL,
524                                           &work_del_abspath, NULL,
525                                           db, entry_abspath,
526                                           scratch_pool, scratch_pool));
527
528          if (work_del_abspath)
529            entry->copied = TRUE;
530        }
531
532      /* If there is still a directory on-disk we keep it, if not it is
533         already deleted. Simple, isn't it?
534
535         Before single-db we had to keep the administative area alive until
536         after the commit really deletes it. Setting keep alive stopped the
537         commit processing from deleting the directory. We don't delete it
538         any more, so all we have to do is provide some 'sane' value.
539       */
540      SVN_ERR(svn_io_check_path(entry_abspath, &path_kind, scratch_pool));
541      entry->keep_local = (path_kind == svn_node_dir);
542    }
543  else if (status == svn_wc__db_status_added)
544    {
545      svn_wc__db_status_t work_status;
546      const char *op_root_abspath;
547      const char *scanned_original_relpath;
548      svn_revnum_t original_revision;
549
550      /* For child nodes, pick up the parent's revision.  */
551      if (*entry->name != '\0')
552        {
553          assert(parent_entry != NULL);
554          assert(entry->revision == SVN_INVALID_REVNUM);
555
556          entry->revision = parent_entry->revision;
557        }
558
559      if (have_base)
560        {
561          svn_wc__db_status_t base_status;
562
563          /* ENTRY->REVISION is overloaded. When a node is schedule-add
564             or -replace, then REVISION refers to the BASE node's revision
565             that is being overwritten. We need to fetch it now.  */
566          SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL,
567                                           &entry->revision,
568                                           NULL, NULL, NULL,
569                                           NULL, NULL, NULL,
570                                           NULL, NULL, NULL,
571                                           NULL, NULL, NULL, NULL,
572                                           db, entry_abspath,
573                                           scratch_pool,
574                                           scratch_pool));
575
576          if (base_status == svn_wc__db_status_not_present)
577            {
578              /* The underlying node is DELETED in this revision.  */
579              entry->deleted = TRUE;
580
581              /* This is an add since there isn't a node to replace.  */
582              entry->schedule = svn_wc_schedule_add;
583            }
584          else
585            entry->schedule = svn_wc_schedule_replace;
586        }
587      else
588        {
589          /* There is NO 'not-present' BASE_NODE for this node.
590             Therefore, we are looking at some kind of add/copy
591             rather than a replace.  */
592
593          /* ### if this looks like a plain old add, then rev=0.  */
594          if (!SVN_IS_VALID_REVNUM(entry->copyfrom_rev)
595              && !SVN_IS_VALID_REVNUM(entry->cmt_rev))
596            entry->revision = 0;
597
598          entry->schedule = svn_wc_schedule_add;
599        }
600
601      /* If we don't have "real" data from the entry (obstruction),
602         then we cannot begin a scan for data. The original node may
603         have important data. Set up stuff to kill that idea off,
604         and finish up this entry.  */
605        {
606          SVN_ERR(svn_wc__db_scan_addition(&work_status,
607                                           &op_root_abspath,
608                                           &repos_relpath,
609                                           &entry->repos,
610                                           &entry->uuid,
611                                           &scanned_original_relpath,
612                                           NULL, NULL, /* original_root|uuid */
613                                           &original_revision,
614                                           db,
615                                           entry_abspath,
616                                           result_pool, scratch_pool));
617
618          /* In wc.db we want to keep the valid revision of the not-present
619             BASE_REV, but when we used entries we set the revision to 0
620             when adding a new node over a not present base node. */
621          if (work_status == svn_wc__db_status_added
622              && entry->deleted)
623            entry->revision = 0;
624        }
625
626      if (!SVN_IS_VALID_REVNUM(entry->cmt_rev)
627          && scanned_original_relpath == NULL)
628        {
629          /* There is NOT a last-changed revision (last-changed date and
630             author may be unknown, but we can always check the rev).
631             The absence of a revision implies this node was added WITHOUT
632             any history. Avoid the COPIED checks in the else block.  */
633          /* ### scan_addition may need to be updated to avoid returning
634             ### status_copied in this case.  */
635        }
636      /* For backwards-compatiblity purposes we treat moves just like
637       * regular copies. */
638      else if (work_status == svn_wc__db_status_copied ||
639               work_status == svn_wc__db_status_moved_here)
640        {
641          entry->copied = TRUE;
642
643          /* If this is a child of a copied subtree, then it should be
644             schedule_normal.  */
645          if (original_repos_relpath == NULL)
646            {
647              /* ### what if there is a BASE node under there? */
648              entry->schedule = svn_wc_schedule_normal;
649            }
650
651          /* Copied nodes need to mirror their copyfrom_rev, if they
652             don't have a revision of their own already. */
653          if (!SVN_IS_VALID_REVNUM(entry->revision)
654              || entry->revision == 0 /* added */)
655            entry->revision = original_revision;
656        }
657
658      /* Does this node have copyfrom_* information?  */
659      if (scanned_original_relpath != NULL)
660        {
661          svn_boolean_t is_copied_child;
662          svn_boolean_t is_mixed_rev = FALSE;
663
664          SVN_ERR_ASSERT(work_status == svn_wc__db_status_copied ||
665                         work_status == svn_wc__db_status_moved_here);
666
667          /* If this node inherits copyfrom information from an
668             ancestor node, then it must be a copied child.  */
669          is_copied_child = (original_repos_relpath == NULL);
670
671          /* If this node has copyfrom information on it, then it may
672             be an actual copy-root, or it could be participating in
673             a mixed-revision copied tree. So if we don't already know
674             this is a copied child, then we need to look for this
675             mixed-revision situation.  */
676          if (!is_copied_child)
677            {
678              const char *parent_abspath;
679              svn_error_t *err;
680              const char *parent_repos_relpath;
681              const char *parent_root_url;
682
683              /* When we insert entries into the database, we will
684                 construct additional copyfrom records for mixed-revision
685                 copies. The old entries would simply record the different
686                 revision in the entry->revision field. That is not
687                 available within wc-ng, so additional copies are made
688                 (see the logic inside write_entry()). However, when
689                 reading these back *out* of the database, the additional
690                 copies look like new "Added" nodes rather than a simple
691                 mixed-rev working copy.
692
693                 That would be a behavior change if we did not compensate.
694                 If there is copyfrom information for this node, then the
695                 code below looks at the parent to detect if it *also* has
696                 copyfrom information, and if the copyfrom_url would align
697                 properly. If it *does*, then we omit storing copyfrom_url
698                 and copyfrom_rev (ie. inherit the copyfrom info like a
699                 normal child), and update entry->revision with the
700                 copyfrom_rev in order to (re)create the mixed-rev copied
701                 subtree that was originally presented for storage.  */
702
703              /* Get the copyfrom information from our parent.
704
705                 Note that the parent could be added/copied/moved-here.
706                 There is no way for it to be deleted/moved-away and
707                 have *this* node appear as copied.  */
708              parent_abspath = svn_dirent_dirname(entry_abspath,
709                                                  scratch_pool);
710              err = svn_wc__db_scan_addition(NULL,
711                                             &op_root_abspath,
712                                             NULL, NULL, NULL,
713                                             &parent_repos_relpath,
714                                             &parent_root_url,
715                                             NULL, NULL,
716                                             db, parent_abspath,
717                                             scratch_pool,
718                                             scratch_pool);
719              if (err)
720                {
721                  if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
722                    return svn_error_trace(err);
723                  svn_error_clear(err);
724                }
725              else if (parent_root_url != NULL
726                       && strcmp(original_root_url, parent_root_url) == 0)
727                {
728                  const char *relpath_to_entry = svn_dirent_is_child(
729                    op_root_abspath, entry_abspath, NULL);
730                  const char *entry_repos_relpath = svn_relpath_join(
731                    parent_repos_relpath, relpath_to_entry, scratch_pool);
732
733                  /* The copyfrom repos roots matched.
734
735                     Now we look to see if the copyfrom path of the parent
736                     would align with our own path. If so, then it means
737                     this copyfrom was spontaneously created and inserted
738                     for mixed-rev purposes and can be eliminated without
739                     changing the semantics of a mixed-rev copied subtree.
740
741                     See notes/api-errata/wc003.txt for some additional
742                     detail, and potential issues.  */
743                  if (strcmp(entry_repos_relpath,
744                             original_repos_relpath) == 0)
745                    {
746                      is_copied_child = TRUE;
747                      is_mixed_rev = TRUE;
748                    }
749                }
750            }
751
752          if (is_copied_child)
753            {
754              /* We won't be settig the  copyfrom_url, yet need to
755                 clear out the copyfrom_rev. Thus, this node becomes a
756                 child of a copied subtree (rather than its own root).  */
757              entry->copyfrom_rev = SVN_INVALID_REVNUM;
758
759              /* Children in a copied subtree are schedule normal
760                 since we don't plan to actually *do* anything with
761                 them. Their operation is implied by ancestors.  */
762              entry->schedule = svn_wc_schedule_normal;
763
764              /* And *finally* we turn this entry into the mixed
765                 revision node that it was intended to be. This
766                 node's revision is taken from the copyfrom record
767                 that we spontaneously constructed.  */
768              if (is_mixed_rev)
769                entry->revision = original_revision;
770            }
771          else if (original_repos_relpath != NULL)
772            {
773              entry->copyfrom_url =
774                svn_path_url_add_component2(original_root_url,
775                                            original_repos_relpath,
776                                            result_pool);
777            }
778          else
779            {
780              /* NOTE: if original_repos_relpath == NULL, then the
781                 second call to scan_addition() will not have occurred.
782                 Thus, this use of OP_ROOT_ABSPATH still contains the
783                 original value where we fetched a value for
784                 SCANNED_REPOS_RELPATH.  */
785              const char *relpath_to_entry = svn_dirent_is_child(
786                op_root_abspath, entry_abspath, NULL);
787              const char *entry_repos_relpath = svn_relpath_join(
788                scanned_original_relpath, relpath_to_entry, scratch_pool);
789
790              entry->copyfrom_url =
791                svn_path_url_add_component2(original_root_url,
792                                            entry_repos_relpath,
793                                            result_pool);
794            }
795        }
796    }
797  else if (status == svn_wc__db_status_not_present)
798    {
799      /* ### buh. 'deleted' nodes are actually supposed to be
800         ### schedule "normal" since we aren't going to actually *do*
801         ### anything to this node at commit time.  */
802      entry->schedule = svn_wc_schedule_normal;
803      entry->deleted = TRUE;
804    }
805  else if (status == svn_wc__db_status_server_excluded)
806    {
807      entry->absent = TRUE;
808    }
809  else if (status == svn_wc__db_status_excluded)
810    {
811      entry->schedule = svn_wc_schedule_normal;
812      entry->depth = svn_depth_exclude;
813    }
814  else
815    {
816      /* ### we should have handled all possible status values.  */
817      SVN_ERR_MALFUNCTION();
818    }
819
820  /* ### higher levels want repos information about deleted nodes, even
821     ### tho they are not "part of" a repository any more.  */
822  if (entry->schedule == svn_wc_schedule_delete)
823    {
824      SVN_ERR(get_info_for_deleted(entry,
825                                   &kind,
826                                   &repos_relpath,
827                                   &checksum,
828                                   &lock,
829                                   db, entry_abspath,
830                                   parent_entry,
831                                   have_base, have_more_work,
832                                   result_pool, scratch_pool));
833    }
834
835  /* ### default to the infinite depth if we don't know it. */
836  if (entry->depth == svn_depth_unknown)
837    entry->depth = svn_depth_infinity;
838
839  if (kind == svn_node_dir)
840    entry->kind = svn_node_dir;
841  else if (kind == svn_node_file)
842    entry->kind = svn_node_file;
843  else if (kind == svn_node_symlink)
844    entry->kind = svn_node_file;  /* ### no symlink kind */
845  else
846    entry->kind = svn_node_unknown;
847
848  /* We should always have a REPOS_RELPATH, except for:
849     - deleted nodes
850     - certain obstructed nodes
851     - not-present nodes
852     - absent nodes
853     - excluded nodes
854
855     ### the last three should probably have an "implied" REPOS_RELPATH
856  */
857  SVN_ERR_ASSERT(repos_relpath != NULL
858                 || entry->schedule == svn_wc_schedule_delete
859                 || status == svn_wc__db_status_not_present
860                 || status == svn_wc__db_status_server_excluded
861                 || status == svn_wc__db_status_excluded);
862  if (repos_relpath)
863    entry->url = svn_path_url_add_component2(entry->repos,
864                                             repos_relpath,
865                                             result_pool);
866
867  if (checksum)
868    {
869      /* We got a SHA-1, get the corresponding MD-5. */
870      if (checksum->kind != svn_checksum_md5)
871        SVN_ERR(svn_wc__db_pristine_get_md5(&checksum, db,
872                                            entry_abspath, checksum,
873                                            scratch_pool, scratch_pool));
874
875      SVN_ERR_ASSERT(checksum->kind == svn_checksum_md5);
876      entry->checksum = svn_checksum_to_cstring(checksum, result_pool);
877    }
878
879  if (conflicted)
880    {
881      svn_skel_t *conflict;
882      svn_boolean_t text_conflicted;
883      svn_boolean_t prop_conflicted;
884      SVN_ERR(svn_wc__db_read_conflict(&conflict, db, entry_abspath,
885                                       scratch_pool, scratch_pool));
886
887      SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, &text_conflicted,
888                                         &prop_conflicted, NULL,
889                                         db, dir_abspath, conflict,
890                                         scratch_pool, scratch_pool));
891
892      if (text_conflicted)
893        {
894          const char *my_abspath;
895          const char *their_old_abspath;
896          const char *their_abspath;
897          SVN_ERR(svn_wc__conflict_read_text_conflict(&my_abspath,
898                                                      &their_old_abspath,
899                                                      &their_abspath,
900                                                      db, dir_abspath,
901                                                      conflict, scratch_pool,
902                                                      scratch_pool));
903
904          if (my_abspath)
905            entry->conflict_wrk = svn_dirent_basename(my_abspath, result_pool);
906
907          if (their_old_abspath)
908            entry->conflict_old = svn_dirent_basename(their_old_abspath,
909                                                      result_pool);
910
911          if (their_abspath)
912            entry->conflict_new = svn_dirent_basename(their_abspath,
913                                                      result_pool);
914        }
915
916      if (prop_conflicted)
917        {
918          const char *prej_abspath;
919
920          SVN_ERR(svn_wc__conflict_read_prop_conflict(&prej_abspath, NULL,
921                                                      NULL, NULL, NULL,
922                                                      db, dir_abspath,
923                                                      conflict, scratch_pool,
924                                                      scratch_pool));
925
926          if (prej_abspath)
927            entry->prejfile = svn_dirent_basename(prej_abspath, result_pool);
928        }
929    }
930
931  if (lock)
932    {
933      entry->lock_token = lock->token;
934      entry->lock_owner = lock->owner;
935      entry->lock_comment = lock->comment;
936      entry->lock_creation_date = lock->date;
937    }
938
939  /* Let's check for a file external.  ugh.  */
940  if (status == svn_wc__db_status_normal
941      && kind == svn_node_file)
942    SVN_ERR(check_file_external(entry, db, entry_abspath, dir_abspath,
943                                result_pool, scratch_pool));
944
945  entry->working_size = translated_size;
946
947  *new_entry = entry;
948
949  return SVN_NO_ERROR;
950}
951
952/* Read entries for PATH/LOCAL_ABSPATH from DB. The entries
953   will be allocated in RESULT_POOL, with temporary allocations in
954   SCRATCH_POOL. The entries are returned in RESULT_ENTRIES.  */
955static svn_error_t *
956read_entries_new(apr_hash_t **result_entries,
957                 svn_wc__db_t *db,
958                 const char *local_abspath,
959                 apr_pool_t *result_pool,
960                 apr_pool_t *scratch_pool)
961{
962  apr_hash_t *entries;
963  const apr_array_header_t *children;
964  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
965  int i;
966  const svn_wc_entry_t *parent_entry;
967  apr_int64_t wc_id = 1;  /* ### hacky. should remove.  */
968
969  entries = apr_hash_make(result_pool);
970
971  SVN_ERR(read_one_entry(&parent_entry, db, wc_id, local_abspath,
972                         "" /* name */,
973                         NULL /* parent_entry */,
974                         result_pool, iterpool));
975  svn_hash_sets(entries, "", parent_entry);
976
977  /* Use result_pool so that the child names (used by reference, rather
978     than copied) appear in result_pool.  */
979  SVN_ERR(svn_wc__db_read_children(&children, db,
980                                   local_abspath,
981                                   result_pool, iterpool));
982  for (i = children->nelts; i--; )
983    {
984      const char *name = APR_ARRAY_IDX(children, i, const char *);
985      const svn_wc_entry_t *entry;
986
987      svn_pool_clear(iterpool);
988
989      SVN_ERR(read_one_entry(&entry,
990                             db, wc_id, local_abspath, name, parent_entry,
991                             result_pool, iterpool));
992      svn_hash_sets(entries, entry->name, entry);
993    }
994
995  svn_pool_destroy(iterpool);
996
997  *result_entries = entries;
998
999  return SVN_NO_ERROR;
1000}
1001
1002
1003/* Read a pair of entries from wc_db in the directory DIR_ABSPATH. Return
1004   the directory's entry in *PARENT_ENTRY and NAME's entry in *ENTRY. The
1005   two returned pointers will be the same if NAME=="" ("this dir").
1006
1007   The parent entry must exist.
1008
1009   The requested entry MAY exist. If it does not, then NULL will be returned.
1010
1011   The resulting entries are allocated in RESULT_POOL, and all temporary
1012   allocations are made in SCRATCH_POOL.  */
1013static svn_error_t *
1014read_entry_pair(const svn_wc_entry_t **parent_entry,
1015                const svn_wc_entry_t **entry,
1016                svn_wc__db_t *db,
1017                const char *dir_abspath,
1018                const char *name,
1019                apr_pool_t *result_pool,
1020                apr_pool_t *scratch_pool)
1021{
1022  apr_int64_t wc_id = 1;  /* ### hacky. should remove.  */
1023
1024  SVN_ERR(read_one_entry(parent_entry, db, wc_id, dir_abspath,
1025                         "" /* name */,
1026                         NULL /* parent_entry */,
1027                         result_pool, scratch_pool));
1028
1029  /* If we need the entry for "this dir", then return the parent_entry
1030     in both outputs. Otherwise, read the child node.  */
1031  if (*name == '\0')
1032    {
1033      /* If the retrieved node is a FILE, then we have a problem. We asked
1034         for a directory. This implies there is an obstructing, unversioned
1035         directory where a FILE should be. We navigated from the obstructing
1036         subdir up to the parent dir, then returned the FILE found there.
1037
1038         Let's return WC_MISSING cuz the caller thought we had a dir, but
1039         that (versioned subdir) isn't there.  */
1040      if ((*parent_entry)->kind == svn_node_file)
1041        {
1042          *parent_entry = NULL;
1043          return svn_error_createf(SVN_ERR_WC_MISSING, NULL,
1044                                 _("'%s' is not a versioned working copy"),
1045                                 svn_dirent_local_style(dir_abspath,
1046                                                        scratch_pool));
1047        }
1048
1049      *entry = *parent_entry;
1050    }
1051  else
1052    {
1053      const apr_array_header_t *children;
1054      int i;
1055
1056      /* Default to not finding the child.  */
1057      *entry = NULL;
1058
1059      /* Determine whether the parent KNOWS about this child. If it does
1060         not, then we should not attempt to look for it.
1061
1062         For example: the parent doesn't "know" about the child, but the
1063         versioned directory *does* exist on disk. We don't want to look
1064         into that subdir.  */
1065      SVN_ERR(svn_wc__db_read_children(&children, db, dir_abspath,
1066                                       scratch_pool, scratch_pool));
1067      for (i = children->nelts; i--; )
1068        {
1069          const char *child = APR_ARRAY_IDX(children, i, const char *);
1070
1071          if (strcmp(child, name) == 0)
1072            {
1073              svn_error_t *err;
1074
1075              err = read_one_entry(entry,
1076                                   db, wc_id, dir_abspath, name, *parent_entry,
1077                                   result_pool, scratch_pool);
1078              if (err)
1079                {
1080                  if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1081                    return svn_error_trace(err);
1082
1083                  /* No problem. Clear the error and leave the default value
1084                     of "missing".  */
1085                  svn_error_clear(err);
1086                }
1087
1088              /* Found it. No need to keep searching.  */
1089              break;
1090            }
1091        }
1092      /* if the loop ends without finding a child, then we have the default
1093         ENTRY value of NULL.  */
1094    }
1095
1096  return SVN_NO_ERROR;
1097}
1098
1099
1100/* */
1101static svn_error_t *
1102read_entries(apr_hash_t **entries,
1103             svn_wc__db_t *db,
1104             const char *wcroot_abspath,
1105             apr_pool_t *result_pool,
1106             apr_pool_t *scratch_pool)
1107{
1108  int wc_format;
1109
1110  SVN_ERR(svn_wc__db_temp_get_format(&wc_format, db, wcroot_abspath,
1111                                     scratch_pool));
1112
1113  if (wc_format < SVN_WC__WC_NG_VERSION)
1114    return svn_error_trace(svn_wc__read_entries_old(entries,
1115                                                    wcroot_abspath,
1116                                                    result_pool,
1117                                                    scratch_pool));
1118
1119  return svn_error_trace(read_entries_new(entries, db, wcroot_abspath,
1120                                          result_pool, scratch_pool));
1121}
1122
1123
1124/* For a given LOCAL_ABSPATH, using DB, set *ADM_ABSPATH to the directory in
1125   which the entry information is located, and *ENTRY_NAME to the entry name
1126   to access that entry.
1127
1128   KIND is as in svn_wc__get_entry().
1129
1130   Return the results in RESULT_POOL and use SCRATCH_POOL for temporary
1131   allocations. */
1132static svn_error_t *
1133get_entry_access_info(const char **adm_abspath,
1134                      const char **entry_name,
1135                      svn_wc__db_t *db,
1136                      const char *local_abspath,
1137                      svn_node_kind_t kind,
1138                      apr_pool_t *result_pool,
1139                      apr_pool_t *scratch_pool)
1140{
1141  svn_wc_adm_access_t *adm_access;
1142  svn_boolean_t read_from_subdir = FALSE;
1143
1144  /* If the caller didn't know the node kind, then stat the path. Maybe
1145     it is really there, and we can speed up the steps below.  */
1146  if (kind == svn_node_unknown)
1147    {
1148      svn_node_kind_t on_disk;
1149
1150      /* Do we already have an access baton for LOCAL_ABSPATH?  */
1151      adm_access = svn_wc__adm_retrieve_internal2(db, local_abspath,
1152                                                  scratch_pool);
1153      if (adm_access)
1154        {
1155          /* Sweet. The node is a directory.  */
1156          on_disk = svn_node_dir;
1157        }
1158      else
1159        {
1160          svn_boolean_t special;
1161
1162          /* What's on disk?  */
1163          SVN_ERR(svn_io_check_special_path(local_abspath, &on_disk, &special,
1164                                            scratch_pool));
1165        }
1166
1167      if (on_disk != svn_node_dir)
1168        {
1169          /* If this is *anything* besides a directory (FILE, NONE, or
1170             UNKNOWN), then we cannot treat it as a versioned directory
1171             containing entries to read. Leave READ_FROM_SUBDIR as FALSE,
1172             so that the parent will be examined.
1173
1174             For NONE and UNKNOWN, it may be that metadata exists for the
1175             node, even though on-disk is unhelpful.
1176
1177             If NEED_PARENT_STUB is TRUE, and the entry is not a DIRECTORY,
1178             then we'll error.
1179
1180             If NEED_PARENT_STUB if FALSE, and we successfully read a stub,
1181             then this on-disk node is obstructing the read.  */
1182        }
1183      else
1184        {
1185          /* We found a directory for this UNKNOWN node. Determine whether
1186             we need to read inside it.  */
1187          read_from_subdir = TRUE;
1188        }
1189    }
1190  else if (kind == svn_node_dir)
1191    {
1192      read_from_subdir = TRUE;
1193    }
1194
1195  if (read_from_subdir)
1196    {
1197      /* KIND must be a DIR or UNKNOWN (and we found a subdir). We want
1198         the "real" data, so treat LOCAL_ABSPATH as a versioned directory.  */
1199      *adm_abspath = apr_pstrdup(result_pool, local_abspath);
1200      *entry_name = "";
1201    }
1202  else
1203    {
1204      /* FILE node needs to read the parent directory. Or a DIR node
1205         needs to read from the parent to get at the stub entry. Or this
1206         is an UNKNOWN node, and we need to examine the parent.  */
1207      svn_dirent_split(adm_abspath, entry_name, local_abspath, result_pool);
1208    }
1209
1210  return SVN_NO_ERROR;
1211}
1212
1213
1214svn_error_t *
1215svn_wc__get_entry(const svn_wc_entry_t **entry,
1216                  svn_wc__db_t *db,
1217                  const char *local_abspath,
1218                  svn_boolean_t allow_unversioned,
1219                  svn_node_kind_t kind,
1220                  apr_pool_t *result_pool,
1221                  apr_pool_t *scratch_pool)
1222{
1223  const char *dir_abspath;
1224  const char *entry_name;
1225
1226  SVN_ERR(get_entry_access_info(&dir_abspath, &entry_name, db, local_abspath,
1227                                kind, scratch_pool, scratch_pool));
1228
1229    {
1230      const svn_wc_entry_t *parent_entry;
1231      svn_error_t *err;
1232
1233      /* NOTE: if KIND is UNKNOWN and we decided to examine the *parent*
1234         directory, then it is possible we moved out of the working copy.
1235         If the on-disk node is a DIR, and we asked for a stub, then we
1236         obviously can't provide that (parent has no info). If the on-disk
1237         node is a FILE/NONE/UNKNOWN, then it is obstructing the real
1238         LOCAL_ABSPATH (or it was never a versioned item). In all these
1239         cases, the read_entries() will (properly) throw an error.
1240
1241         NOTE: if KIND is a DIR and we asked for the real data, but it is
1242         obstructed on-disk by some other node kind (NONE, FILE, UNKNOWN),
1243         then this will throw an error.  */
1244
1245      err = read_entry_pair(&parent_entry, entry,
1246                            db, dir_abspath, entry_name,
1247                            result_pool, scratch_pool);
1248      if (err)
1249        {
1250          if (err->apr_err != SVN_ERR_WC_MISSING || kind != svn_node_unknown
1251              || *entry_name != '\0')
1252            return svn_error_trace(err);
1253          svn_error_clear(err);
1254
1255          /* The caller didn't know the node type, we saw a directory there,
1256             we attempted to read IN that directory, and then wc_db reports
1257             that it is NOT a working copy directory. It is possible that
1258             one of two things has happened:
1259
1260             1) a directory is obstructing a file in the parent
1261             2) the (versioned) directory's contents have been removed
1262
1263             Let's assume situation (1); if that is true, then we can just
1264             return the newly-found data.
1265
1266             If we assumed (2), then a valid result still won't help us
1267             since the caller asked for the actual contents, not the stub
1268             (which is why we read *into* the directory). However, if we
1269             assume (1) and get back a stub, then we have verified a
1270             missing, versioned directory, and can return an error
1271             describing that.
1272
1273             Redo the fetch, but "insist" we are trying to find a file.
1274             This will read from the parent directory of the "file".  */
1275          err = svn_wc__get_entry(entry, db, local_abspath, allow_unversioned,
1276                                  svn_node_file, result_pool, scratch_pool);
1277          if (err == SVN_NO_ERROR)
1278            return SVN_NO_ERROR;
1279          if (err->apr_err != SVN_ERR_NODE_UNEXPECTED_KIND)
1280            return svn_error_trace(err);
1281          svn_error_clear(err);
1282
1283          /* We asked for a FILE, but the node found is a DIR. Thus, we
1284             are looking at a stub. Originally, we tried to read into the
1285             subdir because NEED_PARENT_STUB is FALSE. The stub we just
1286             read is not going to work for the caller, so inform them of
1287             the missing subdirectory.  */
1288          SVN_ERR_ASSERT(*entry != NULL && (*entry)->kind == svn_node_dir);
1289          return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
1290                                 _("Admin area of '%s' is missing"),
1291                                 svn_dirent_local_style(local_abspath,
1292                                                        scratch_pool));
1293        }
1294    }
1295
1296  if (*entry == NULL)
1297    {
1298      if (allow_unversioned)
1299        return SVN_NO_ERROR;
1300      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
1301                               _("'%s' is not under version control"),
1302                               svn_dirent_local_style(local_abspath,
1303                                                      scratch_pool));
1304    }
1305
1306  /* The caller had the wrong information.  */
1307  if ((kind == svn_node_file && (*entry)->kind != svn_node_file)
1308      || (kind == svn_node_dir && (*entry)->kind != svn_node_dir))
1309    return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
1310                             _("'%s' is not of the right kind"),
1311                             svn_dirent_local_style(local_abspath,
1312                                                    scratch_pool));
1313
1314  return SVN_NO_ERROR;
1315}
1316
1317/* TODO ### Rewrite doc string to mention ENTRIES_ALL; not ADM_ACCESS.
1318
1319   Prune the deleted entries from the cached entries in ADM_ACCESS, and
1320   return that collection in *ENTRIES_PRUNED.  SCRATCH_POOL is used for local,
1321   short term, memory allocation, RESULT_POOL for permanent stuff.  */
1322static svn_error_t *
1323prune_deleted(apr_hash_t **entries_pruned,
1324              apr_hash_t *entries_all,
1325              apr_pool_t *result_pool,
1326              apr_pool_t *scratch_pool)
1327{
1328  apr_hash_index_t *hi;
1329
1330  if (!entries_all)
1331    {
1332      *entries_pruned = NULL;
1333      return SVN_NO_ERROR;
1334    }
1335
1336  /* I think it will be common for there to be no deleted entries, so
1337     it is worth checking for that case as we can optimise it. */
1338  for (hi = apr_hash_first(scratch_pool, entries_all);
1339       hi;
1340       hi = apr_hash_next(hi))
1341    {
1342      svn_boolean_t hidden;
1343
1344      SVN_ERR(svn_wc__entry_is_hidden(&hidden,
1345                                      svn__apr_hash_index_val(hi)));
1346      if (hidden)
1347        break;
1348    }
1349
1350  if (! hi)
1351    {
1352      /* There are no deleted entries, so we can use the full hash */
1353      *entries_pruned = entries_all;
1354      return SVN_NO_ERROR;
1355    }
1356
1357  /* Construct pruned hash without deleted entries */
1358  *entries_pruned = apr_hash_make(result_pool);
1359  for (hi = apr_hash_first(scratch_pool, entries_all);
1360       hi;
1361       hi = apr_hash_next(hi))
1362    {
1363      const void *key = svn__apr_hash_index_key(hi);
1364      const svn_wc_entry_t *entry = svn__apr_hash_index_val(hi);
1365      svn_boolean_t hidden;
1366
1367      SVN_ERR(svn_wc__entry_is_hidden(&hidden, entry));
1368      if (!hidden)
1369        svn_hash_sets(*entries_pruned, key, entry);
1370    }
1371
1372  return SVN_NO_ERROR;
1373}
1374
1375struct entries_read_baton_t
1376{
1377  apr_hash_t **new_entries;
1378  svn_wc__db_t *db;
1379  const char *local_abspath;
1380  apr_pool_t *result_pool;
1381};
1382
1383static svn_error_t *
1384entries_read_txn(void *baton, svn_sqlite__db_t *db, apr_pool_t *scratch_pool)
1385{
1386  struct entries_read_baton_t *erb = baton;
1387
1388  SVN_ERR(read_entries(erb->new_entries, erb->db, erb->local_abspath,
1389                       erb->result_pool, scratch_pool));
1390
1391  return NULL;
1392}
1393
1394svn_error_t *
1395svn_wc__entries_read_internal(apr_hash_t **entries,
1396                              svn_wc_adm_access_t *adm_access,
1397                              svn_boolean_t show_hidden,
1398                              apr_pool_t *pool)
1399{
1400  apr_hash_t *new_entries;
1401
1402  new_entries = svn_wc__adm_access_entries(adm_access);
1403  if (! new_entries)
1404    {
1405      svn_wc__db_t *db = svn_wc__adm_get_db(adm_access);
1406      const char *local_abspath = svn_wc__adm_access_abspath(adm_access);
1407      apr_pool_t *result_pool = svn_wc__adm_access_pool_internal(adm_access);
1408      svn_sqlite__db_t *sdb;
1409      struct entries_read_baton_t erb;
1410
1411      /* ### Use the borrow DB api to handle all calls in a single read
1412         ### transaction. This api is used extensively in our test suite
1413         ### via the entries-read application. */
1414
1415      SVN_ERR(svn_wc__db_temp_borrow_sdb(&sdb, db, local_abspath, pool));
1416
1417      erb.db = db;
1418      erb.local_abspath = local_abspath;
1419      erb.new_entries = &new_entries;
1420      erb.result_pool = result_pool;
1421
1422      SVN_ERR(svn_sqlite__with_lock(sdb, entries_read_txn, &erb, pool));
1423
1424      svn_wc__adm_access_set_entries(adm_access, new_entries);
1425    }
1426
1427  if (show_hidden)
1428    *entries = new_entries;
1429  else
1430    SVN_ERR(prune_deleted(entries, new_entries,
1431                          svn_wc__adm_access_pool_internal(adm_access),
1432                          pool));
1433
1434  return SVN_NO_ERROR;
1435}
1436
1437svn_error_t *
1438svn_wc_entries_read(apr_hash_t **entries,
1439                    svn_wc_adm_access_t *adm_access,
1440                    svn_boolean_t show_hidden,
1441                    apr_pool_t *pool)
1442{
1443  return svn_error_trace(svn_wc__entries_read_internal(entries, adm_access,
1444                                                       show_hidden, pool));
1445}
1446
1447/* No transaction required: called from write_entry which is itself
1448   transaction-wrapped. */
1449static svn_error_t *
1450insert_node(svn_sqlite__db_t *sdb,
1451            const db_node_t *node,
1452            apr_pool_t *scratch_pool)
1453{
1454  svn_sqlite__stmt_t *stmt;
1455
1456  SVN_ERR_ASSERT(node->op_depth > 0 || node->repos_relpath);
1457
1458  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE));
1459  SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnnnsnrisnnni",
1460                            node->wc_id,
1461                            node->local_relpath,
1462                            node->op_depth,
1463                            node->parent_relpath,
1464                            /* Setting depth for files? */
1465                            svn_depth_to_word(node->depth),
1466                            node->changed_rev,
1467                            node->changed_date,
1468                            node->changed_author,
1469                            node->recorded_time));
1470
1471  if (node->repos_relpath)
1472    {
1473      SVN_ERR(svn_sqlite__bind_int64(stmt, 5,
1474                                     node->repos_id));
1475      SVN_ERR(svn_sqlite__bind_text(stmt, 6,
1476                                    node->repos_relpath));
1477      SVN_ERR(svn_sqlite__bind_revnum(stmt, 7, node->revision));
1478    }
1479
1480  if (node->presence == svn_wc__db_status_normal)
1481    SVN_ERR(svn_sqlite__bind_text(stmt, 8, "normal"));
1482  else if (node->presence == svn_wc__db_status_not_present)
1483    SVN_ERR(svn_sqlite__bind_text(stmt, 8, "not-present"));
1484  else if (node->presence == svn_wc__db_status_base_deleted)
1485    SVN_ERR(svn_sqlite__bind_text(stmt, 8, "base-deleted"));
1486  else if (node->presence == svn_wc__db_status_incomplete)
1487    SVN_ERR(svn_sqlite__bind_text(stmt, 8, "incomplete"));
1488  else if (node->presence == svn_wc__db_status_excluded)
1489    SVN_ERR(svn_sqlite__bind_text(stmt, 8, "excluded"));
1490  else if (node->presence == svn_wc__db_status_server_excluded)
1491    SVN_ERR(svn_sqlite__bind_text(stmt, 8, "server-excluded"));
1492
1493  if (node->kind == svn_node_none)
1494    SVN_ERR(svn_sqlite__bind_text(stmt, 10, "unknown"));
1495  else
1496    SVN_ERR(svn_sqlite__bind_text(stmt, 10,
1497                                  svn_node_kind_to_word(node->kind)));
1498
1499  if (node->kind == svn_node_file)
1500    {
1501      if (!node->checksum
1502          && node->op_depth == 0
1503          && node->presence != svn_wc__db_status_not_present
1504          && node->presence != svn_wc__db_status_excluded
1505          && node->presence != svn_wc__db_status_server_excluded)
1506        return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
1507                                 _("The file '%s' has no checksum"),
1508                                 svn_dirent_local_style(node->local_relpath,
1509                                                        scratch_pool));
1510
1511      SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, node->checksum,
1512                                        scratch_pool));
1513    }
1514
1515  if (node->properties) /* ### Never set, props done later */
1516    SVN_ERR(svn_sqlite__bind_properties(stmt, 15, node->properties,
1517                                        scratch_pool));
1518
1519  if (node->recorded_size != SVN_INVALID_FILESIZE)
1520    SVN_ERR(svn_sqlite__bind_int64(stmt, 16, node->recorded_size));
1521
1522  if (node->file_external)
1523    SVN_ERR(svn_sqlite__bind_int(stmt, 20, 1));
1524
1525  if (node->inherited_props)
1526    SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, node->inherited_props,
1527                                    scratch_pool));
1528
1529  SVN_ERR(svn_sqlite__insert(NULL, stmt));
1530
1531  return SVN_NO_ERROR;
1532}
1533
1534
1535/* */
1536static svn_error_t *
1537insert_actual_node(svn_sqlite__db_t *sdb,
1538                   svn_wc__db_t *db,
1539                   const char *wri_abspath,
1540                   const db_actual_node_t *actual_node,
1541                   apr_pool_t *scratch_pool)
1542{
1543  svn_sqlite__stmt_t *stmt;
1544  svn_skel_t *conflict_data = NULL;
1545
1546  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_ACTUAL_NODE));
1547
1548  SVN_ERR(svn_sqlite__bind_int64(stmt, 1, actual_node->wc_id));
1549  SVN_ERR(svn_sqlite__bind_text(stmt, 2, actual_node->local_relpath));
1550  SVN_ERR(svn_sqlite__bind_text(stmt, 3, actual_node->parent_relpath));
1551
1552  if (actual_node->properties)
1553    SVN_ERR(svn_sqlite__bind_properties(stmt, 4, actual_node->properties,
1554                                        scratch_pool));
1555
1556  if (actual_node->changelist)
1557    SVN_ERR(svn_sqlite__bind_text(stmt, 5, actual_node->changelist));
1558
1559  SVN_ERR(svn_wc__upgrade_conflict_skel_from_raw(
1560                                &conflict_data,
1561                                db, wri_abspath,
1562                                actual_node->local_relpath,
1563                                actual_node->conflict_old,
1564                                actual_node->conflict_working,
1565                                actual_node->conflict_new,
1566                                actual_node->prop_reject,
1567                                actual_node->tree_conflict_data,
1568                                actual_node->tree_conflict_data
1569                                    ? strlen(actual_node->tree_conflict_data)
1570                                    : 0,
1571                                scratch_pool, scratch_pool));
1572
1573  if (conflict_data)
1574    {
1575      svn_stringbuf_t *data = svn_skel__unparse(conflict_data, scratch_pool);
1576
1577      SVN_ERR(svn_sqlite__bind_blob(stmt, 6, data->data, data->len));
1578    }
1579
1580  /* Execute and reset the insert clause. */
1581  return svn_error_trace(svn_sqlite__insert(NULL, stmt));
1582}
1583
1584static svn_boolean_t
1585is_switched(db_node_t *parent,
1586            db_node_t *child,
1587            apr_pool_t *scratch_pool)
1588{
1589  if (parent && child)
1590    {
1591      if (parent->repos_id != child->repos_id)
1592        return TRUE;
1593
1594      if (parent->repos_relpath && child->repos_relpath)
1595        {
1596          const char *unswitched
1597            = svn_relpath_join(parent->repos_relpath,
1598                               svn_relpath_basename(child->local_relpath,
1599                                                    scratch_pool),
1600                               scratch_pool);
1601          if (strcmp(unswitched, child->repos_relpath))
1602            return TRUE;
1603        }
1604    }
1605
1606  return FALSE;
1607}
1608
1609struct write_baton {
1610  db_node_t *base;
1611  db_node_t *work;
1612  db_node_t *below_work;
1613  apr_hash_t *tree_conflicts;
1614};
1615
1616#define WRITE_ENTRY_ASSERT(expr) \
1617  if (!(expr)) \
1618    return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,  \
1619                             _("Unable to upgrade '%s' at line %d"),    \
1620                             svn_dirent_local_style( \
1621                               svn_dirent_join(root_abspath, \
1622                                               local_relpath,           \
1623                                               scratch_pool),           \
1624                               scratch_pool), __LINE__)
1625
1626/* Write the information for ENTRY to WC_DB.  The WC_ID, REPOS_ID and
1627   REPOS_ROOT will all be used for writing ENTRY.
1628   ### transitioning from straight sql to using the wc_db APIs.  For the
1629   ### time being, we'll need both parameters. */
1630static svn_error_t *
1631write_entry(struct write_baton **entry_node,
1632            const struct write_baton *parent_node,
1633            svn_wc__db_t *db,
1634            svn_sqlite__db_t *sdb,
1635            apr_int64_t wc_id,
1636            apr_int64_t repos_id,
1637            const svn_wc_entry_t *entry,
1638            const svn_wc__text_base_info_t *text_base_info,
1639            const char *local_relpath,
1640            const char *tmp_entry_abspath,
1641            const char *root_abspath,
1642            const svn_wc_entry_t *this_dir,
1643            svn_boolean_t create_locks,
1644            apr_pool_t *result_pool,
1645            apr_pool_t *scratch_pool)
1646{
1647  db_node_t *base_node = NULL;
1648  db_node_t *working_node = NULL, *below_working_node = NULL;
1649  db_actual_node_t *actual_node = NULL;
1650  const char *parent_relpath;
1651  apr_hash_t *tree_conflicts;
1652
1653  if (*local_relpath == '\0')
1654    parent_relpath = NULL;
1655  else
1656    parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
1657
1658  /* This is how it should work, it doesn't work like this yet because
1659     we need proper op_depth to layer the working nodes.
1660
1661     Using "svn add", "svn rm", "svn cp" only files can be replaced
1662     pre-wcng; directories can only be normal, deleted or added.
1663     Files cannot be replaced within a deleted directory, so replaced
1664     files can only exist in a normal directory, or a directory that
1665     is added+copied.  In a normal directory a replaced file needs a
1666     base node and a working node, in an added+copied directory a
1667     replaced file needs two working nodes at different op-depths.
1668
1669     With just the above operations the conversion for files and
1670     directories is straightforward:
1671
1672           pre-wcng                             wcng
1673     parent         child                 parent     child
1674
1675     normal         normal                base       base
1676     add+copied     normal+copied         work       work
1677     normal+copied  normal+copied         work       work
1678     normal         delete                base       base+work
1679     delete         delete                base+work  base+work
1680     add+copied     delete                work       work
1681     normal         add                   base       work
1682     add            add                   work       work
1683     add+copied     add                   work       work
1684     normal         add+copied            base       work
1685     add            add+copied            work       work
1686     add+copied     add+copied            work       work
1687     normal         replace               base       base+work
1688     add+copied     replace               work       work+work
1689     normal         replace+copied        base       base+work
1690     add+copied     replace+copied        work       work+work
1691
1692     However "svn merge" make this more complicated.  The pre-wcng
1693     "svn merge" is capable of replacing a directory, that is it can
1694     mark the whole tree deleted, and then copy another tree on top.
1695     The entries then represent the replacing tree overlayed on the
1696     deleted tree.
1697
1698       original       replace          schedule in
1699       tree           tree             combined tree
1700
1701       A              A                replace+copied
1702       A/f                             delete+copied
1703       A/g            A/g              replace+copied
1704                      A/h              add+copied
1705       A/B            A/B              replace+copied
1706       A/B/f                           delete+copied
1707       A/B/g          A/B/g            replace+copied
1708                      A/B/h            add+copied
1709       A/C                             delete+copied
1710       A/C/f                           delete+copied
1711                      A/D              add+copied
1712                      A/D/f            add+copied
1713
1714     The original tree could be normal tree, or an add+copied tree.
1715     Committing such a merge generally worked, but making further tree
1716     modifications before commit sometimes failed.
1717
1718     The root of the replace is handled like the file replace:
1719
1720           pre-wcng                             wcng
1721     parent         child                 parent     child
1722
1723     normal         replace+copied        base       base+work
1724     add+copied     replace+copied        work       work+work
1725
1726     although obviously the node is a directory rather then a file.
1727     There are then more conversion states where the parent is
1728     replaced.
1729
1730           pre-wcng                                wcng
1731     parent           child              parent            child
1732
1733     replace+copied   add                [base|work]+work  work
1734     replace+copied   add+copied         [base|work]+work  work
1735     replace+copied   delete+copied      [base|work]+work  [base|work]+work
1736     delete+copied    delete+copied      [base|work]+work  [base|work]+work
1737     replace+copied   replace+copied     [base|work]+work  [base|work]+work
1738  */
1739
1740  WRITE_ENTRY_ASSERT(parent_node || entry->schedule == svn_wc_schedule_normal);
1741
1742  WRITE_ENTRY_ASSERT(!parent_node || parent_node->base
1743                     || parent_node->below_work || parent_node->work);
1744
1745  switch (entry->schedule)
1746    {
1747      case svn_wc_schedule_normal:
1748        if (entry->copied ||
1749            (entry->depth == svn_depth_exclude
1750             && parent_node && !parent_node->base && parent_node->work))
1751          working_node = MAYBE_ALLOC(working_node, result_pool);
1752        else
1753          base_node = MAYBE_ALLOC(base_node, result_pool);
1754        break;
1755
1756      case svn_wc_schedule_add:
1757        working_node = MAYBE_ALLOC(working_node, result_pool);
1758        if (entry->deleted)
1759          {
1760            if (parent_node->base)
1761              base_node = MAYBE_ALLOC(base_node, result_pool);
1762            else
1763              below_working_node = MAYBE_ALLOC(below_working_node, result_pool);
1764          }
1765        break;
1766
1767      case svn_wc_schedule_delete:
1768        working_node = MAYBE_ALLOC(working_node, result_pool);
1769        if (parent_node->base)
1770          base_node = MAYBE_ALLOC(base_node, result_pool);
1771        if (parent_node->work)
1772          below_working_node = MAYBE_ALLOC(below_working_node, result_pool);
1773        break;
1774
1775      case svn_wc_schedule_replace:
1776        working_node = MAYBE_ALLOC(working_node, result_pool);
1777        if (parent_node->base)
1778          base_node = MAYBE_ALLOC(base_node, result_pool);
1779        else
1780          below_working_node = MAYBE_ALLOC(below_working_node, result_pool);
1781        break;
1782    }
1783
1784  /* Something deleted in this revision means there should always be a
1785     BASE node to indicate the not-present node.  */
1786  if (entry->deleted)
1787    {
1788      WRITE_ENTRY_ASSERT(base_node || below_working_node);
1789      WRITE_ENTRY_ASSERT(!entry->incomplete);
1790      if (base_node)
1791        base_node->presence = svn_wc__db_status_not_present;
1792      else
1793        below_working_node->presence = svn_wc__db_status_not_present;
1794    }
1795  else if (entry->absent)
1796    {
1797      WRITE_ENTRY_ASSERT(base_node && !working_node && !below_working_node);
1798      WRITE_ENTRY_ASSERT(!entry->incomplete);
1799      base_node->presence = svn_wc__db_status_server_excluded;
1800    }
1801
1802  if (entry->copied)
1803    {
1804      if (entry->copyfrom_url)
1805        {
1806          working_node->repos_id = repos_id;
1807          working_node->repos_relpath = svn_uri_skip_ancestor(
1808                                          this_dir->repos, entry->copyfrom_url,
1809                                          result_pool);
1810          working_node->revision = entry->copyfrom_rev;
1811          working_node->op_depth
1812            = svn_wc__db_op_depth_for_upgrade(local_relpath);
1813        }
1814      else if (parent_node->work && parent_node->work->repos_relpath)
1815        {
1816          working_node->repos_id = repos_id;
1817          working_node->repos_relpath
1818            = svn_relpath_join(parent_node->work->repos_relpath,
1819                               svn_relpath_basename(local_relpath, NULL),
1820                               result_pool);
1821          working_node->revision = parent_node->work->revision;
1822          working_node->op_depth = parent_node->work->op_depth;
1823        }
1824      else if (parent_node->below_work
1825                && parent_node->below_work->repos_relpath)
1826        {
1827          working_node->repos_id = repos_id;
1828          working_node->repos_relpath
1829            = svn_relpath_join(parent_node->below_work->repos_relpath,
1830                               svn_relpath_basename(local_relpath, NULL),
1831                               result_pool);
1832          working_node->revision = parent_node->below_work->revision;
1833          working_node->op_depth = parent_node->below_work->op_depth;
1834        }
1835      else
1836        return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
1837                                 _("No copyfrom URL for '%s'"),
1838                                 svn_dirent_local_style(local_relpath,
1839                                                        scratch_pool));
1840    }
1841
1842  if (entry->conflict_old)
1843    {
1844      actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
1845      if (parent_relpath && entry->conflict_old)
1846        actual_node->conflict_old = svn_relpath_join(parent_relpath,
1847                                                     entry->conflict_old,
1848                                                     scratch_pool);
1849      else
1850        actual_node->conflict_old = entry->conflict_old;
1851      if (parent_relpath && entry->conflict_new)
1852        actual_node->conflict_new = svn_relpath_join(parent_relpath,
1853                                                     entry->conflict_new,
1854                                                     scratch_pool);
1855      else
1856        actual_node->conflict_new = entry->conflict_new;
1857      if (parent_relpath && entry->conflict_wrk)
1858        actual_node->conflict_working = svn_relpath_join(parent_relpath,
1859                                                         entry->conflict_wrk,
1860                                                         scratch_pool);
1861      else
1862        actual_node->conflict_working = entry->conflict_wrk;
1863    }
1864
1865  if (entry->prejfile)
1866    {
1867      actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
1868      actual_node->prop_reject = svn_relpath_join((entry->kind == svn_node_dir
1869                                                   ? local_relpath
1870                                                   : parent_relpath),
1871                                                  entry->prejfile,
1872                                                  scratch_pool);
1873    }
1874
1875  if (entry->changelist)
1876    {
1877      actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
1878      actual_node->changelist = entry->changelist;
1879    }
1880
1881  /* ### set the text_mod value? */
1882
1883  if (entry_node && entry->tree_conflict_data)
1884    {
1885      /* Issues #3840/#3916: 1.6 stores multiple tree conflicts on the
1886         parent node, 1.7 stores them directly on the conflited nodes.
1887         So "((skel1) (skel2))" becomes "(skel1)" and "(skel2)" */
1888      svn_skel_t *skel;
1889
1890      skel = svn_skel__parse(entry->tree_conflict_data,
1891                             strlen(entry->tree_conflict_data),
1892                             scratch_pool);
1893      tree_conflicts = apr_hash_make(result_pool);
1894      skel = skel->children;
1895      while(skel)
1896        {
1897          svn_wc_conflict_description2_t *conflict;
1898          svn_skel_t *new_skel;
1899          const char *key;
1900
1901          /* *CONFLICT is allocated so it is safe to use a non-const pointer */
1902          SVN_ERR(svn_wc__deserialize_conflict(
1903                             (const svn_wc_conflict_description2_t**)&conflict,
1904                                               skel,
1905                                               svn_dirent_join(root_abspath,
1906                                                               local_relpath,
1907                                                               scratch_pool),
1908                                               scratch_pool, scratch_pool));
1909
1910          WRITE_ENTRY_ASSERT(conflict->kind == svn_wc_conflict_kind_tree);
1911
1912          /* Fix dubious data stored by old clients, local adds don't have
1913             a repository URL. */
1914          if (conflict->reason == svn_wc_conflict_reason_added)
1915            conflict->src_left_version = NULL;
1916
1917          SVN_ERR(svn_wc__serialize_conflict(&new_skel, conflict,
1918                                             scratch_pool, scratch_pool));
1919
1920          /* Store in hash to be retrieved when writing the child
1921             row. */
1922          key = svn_dirent_skip_ancestor(root_abspath, conflict->local_abspath);
1923          svn_hash_sets(tree_conflicts, apr_pstrdup(result_pool, key),
1924                        svn_skel__unparse(new_skel, result_pool)->data);
1925          skel = skel->next;
1926        }
1927    }
1928  else
1929    tree_conflicts = NULL;
1930
1931  if (parent_node && parent_node->tree_conflicts)
1932    {
1933      const char *tree_conflict_data =
1934          svn_hash_gets(parent_node->tree_conflicts, local_relpath);
1935      if (tree_conflict_data)
1936        {
1937          actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
1938          actual_node->tree_conflict_data = tree_conflict_data;
1939        }
1940
1941      /* Reset hash so that we don't write the row again when writing
1942         actual-only nodes */
1943      svn_hash_sets(parent_node->tree_conflicts, local_relpath, NULL);
1944    }
1945
1946  if (entry->file_external_path != NULL)
1947    {
1948      base_node = MAYBE_ALLOC(base_node, result_pool);
1949    }
1950
1951
1952  /* Insert the base node. */
1953  if (base_node)
1954    {
1955      base_node->wc_id = wc_id;
1956      base_node->local_relpath = local_relpath;
1957      base_node->op_depth = 0;
1958      base_node->parent_relpath = parent_relpath;
1959      base_node->revision = entry->revision;
1960      base_node->recorded_time = entry->text_time;
1961      base_node->recorded_size = entry->working_size;
1962
1963      if (entry->depth != svn_depth_exclude)
1964        base_node->depth = entry->depth;
1965      else
1966        {
1967          base_node->presence = svn_wc__db_status_excluded;
1968          base_node->depth = svn_depth_infinity;
1969        }
1970
1971      if (entry->deleted)
1972        {
1973          WRITE_ENTRY_ASSERT(base_node->presence
1974                             == svn_wc__db_status_not_present);
1975          /* ### should be svn_node_unknown, but let's store what we have. */
1976          base_node->kind = entry->kind;
1977        }
1978      else if (entry->absent)
1979        {
1980          WRITE_ENTRY_ASSERT(base_node->presence
1981                             == svn_wc__db_status_server_excluded);
1982          /* ### should be svn_node_unknown, but let's store what we have. */
1983          base_node->kind = entry->kind;
1984
1985          /* Store the most likely revision in the node to avoid
1986             base nodes without a valid revision. Of course
1987             we remember that the data is still incomplete. */
1988          if (!SVN_IS_VALID_REVNUM(base_node->revision) && parent_node->base)
1989            base_node->revision = parent_node->base->revision;
1990        }
1991      else
1992        {
1993          base_node->kind = entry->kind;
1994
1995          if (base_node->presence != svn_wc__db_status_excluded)
1996            {
1997              /* All subdirs are initially incomplete, they stop being
1998                 incomplete when the entries file in the subdir is
1999                 upgraded and remain incomplete if that doesn't happen. */
2000              if (entry->kind == svn_node_dir
2001                  && strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR))
2002                {
2003                  base_node->presence = svn_wc__db_status_incomplete;
2004
2005                  /* Store the most likely revision in the node to avoid
2006                     base nodes without a valid revision. Of course
2007                     we remember that the data is still incomplete. */
2008                  if (parent_node->base)
2009                    base_node->revision = parent_node->base->revision;
2010                }
2011              else if (entry->incomplete)
2012                {
2013                  /* ### nobody should have set the presence.  */
2014                  WRITE_ENTRY_ASSERT(base_node->presence
2015                                     == svn_wc__db_status_normal);
2016                  base_node->presence = svn_wc__db_status_incomplete;
2017                }
2018            }
2019        }
2020
2021      if (entry->kind == svn_node_dir)
2022        base_node->checksum = NULL;
2023      else
2024        {
2025          if (text_base_info && text_base_info->revert_base.sha1_checksum)
2026            base_node->checksum = text_base_info->revert_base.sha1_checksum;
2027          else if (text_base_info && text_base_info->normal_base.sha1_checksum)
2028            base_node->checksum = text_base_info->normal_base.sha1_checksum;
2029          else
2030            base_node->checksum = NULL;
2031
2032          /* The base MD5 checksum is available in the entry, unless there
2033           * is a copied WORKING node.  If possible, verify that the entry
2034           * checksum matches the base file that we found. */
2035          if (! (working_node && entry->copied))
2036            {
2037              svn_checksum_t *entry_md5_checksum, *found_md5_checksum;
2038              SVN_ERR(svn_checksum_parse_hex(&entry_md5_checksum,
2039                                             svn_checksum_md5,
2040                                             entry->checksum, scratch_pool));
2041              if (text_base_info && text_base_info->revert_base.md5_checksum)
2042                found_md5_checksum = text_base_info->revert_base.md5_checksum;
2043              else if (text_base_info
2044                       && text_base_info->normal_base.md5_checksum)
2045                found_md5_checksum = text_base_info->normal_base.md5_checksum;
2046              else
2047                found_md5_checksum = NULL;
2048              if (entry_md5_checksum && found_md5_checksum &&
2049                  !svn_checksum_match(entry_md5_checksum, found_md5_checksum))
2050                return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
2051                                         _("Bad base MD5 checksum for '%s'; "
2052                                           "expected: '%s'; found '%s'; "),
2053                                       svn_dirent_local_style(
2054                                         svn_dirent_join(root_abspath,
2055                                                         local_relpath,
2056                                                         scratch_pool),
2057                                         scratch_pool),
2058                                       svn_checksum_to_cstring_display(
2059                                         entry_md5_checksum, scratch_pool),
2060                                       svn_checksum_to_cstring_display(
2061                                         found_md5_checksum, scratch_pool));
2062              else
2063                {
2064                  /* ### Not sure what conditions this should cover. */
2065                  /* SVN_ERR_ASSERT(entry->deleted || ...); */
2066                }
2067            }
2068        }
2069
2070      if (this_dir->repos)
2071        {
2072          base_node->repos_id = repos_id;
2073
2074          if (entry->url != NULL)
2075            {
2076              base_node->repos_relpath = svn_uri_skip_ancestor(
2077                                           this_dir->repos, entry->url,
2078                                           result_pool);
2079            }
2080          else
2081            {
2082              const char *relpath = svn_uri_skip_ancestor(this_dir->repos,
2083                                                          this_dir->url,
2084                                                          scratch_pool);
2085              if (relpath == NULL || *relpath == '\0')
2086                base_node->repos_relpath = entry->name;
2087              else
2088                base_node->repos_relpath =
2089                  svn_dirent_join(relpath, entry->name, result_pool);
2090            }
2091        }
2092
2093      /* TODO: These values should always be present, if they are missing
2094         during an upgrade, set a flag, and then ask the user to talk to the
2095         server.
2096
2097         Note: cmt_rev is the distinguishing value. The others may be 0 or
2098         NULL if the corresponding revprop has been deleted.  */
2099      base_node->changed_rev = entry->cmt_rev;
2100      base_node->changed_date = entry->cmt_date;
2101      base_node->changed_author = entry->cmt_author;
2102
2103      if (entry->file_external_path)
2104        base_node->file_external = TRUE;
2105
2106      /* Switched nodes get an empty iprops cache. */
2107      if (parent_node
2108          && is_switched(parent_node->base, base_node, scratch_pool))
2109        base_node->inherited_props
2110          = apr_array_make(scratch_pool, 0, sizeof(svn_prop_inherited_item_t*));
2111
2112      SVN_ERR(insert_node(sdb, base_node, scratch_pool));
2113
2114      /* We have to insert the lock after the base node, because the node
2115         must exist to lookup various bits of repos related information for
2116         the abs path. */
2117      if (entry->lock_token && create_locks)
2118        {
2119          svn_wc__db_lock_t lock;
2120
2121          lock.token = entry->lock_token;
2122          lock.owner = entry->lock_owner;
2123          lock.comment = entry->lock_comment;
2124          lock.date = entry->lock_creation_date;
2125
2126          SVN_ERR(svn_wc__db_lock_add(db, tmp_entry_abspath, &lock,
2127                                      scratch_pool));
2128        }
2129    }
2130
2131  if (below_working_node)
2132    {
2133      db_node_t *work
2134        = parent_node->below_work ? parent_node->below_work : parent_node->work;
2135
2136      below_working_node->wc_id = wc_id;
2137      below_working_node->local_relpath = local_relpath;
2138      below_working_node->op_depth = work->op_depth;
2139      below_working_node->parent_relpath = parent_relpath;
2140      below_working_node->presence = svn_wc__db_status_normal;
2141      below_working_node->kind = entry->kind;
2142      below_working_node->repos_id = work->repos_id;
2143      below_working_node->revision = work->revision;
2144
2145      /* This is just guessing. If the node below would have been switched
2146         or if it was updated to a different version, the guess would
2147         fail. But we don't have better information pre wc-ng :( */
2148      if (work->repos_relpath)
2149        below_working_node->repos_relpath
2150          = svn_relpath_join(work->repos_relpath,
2151                             svn_relpath_basename(local_relpath, NULL),
2152                             result_pool);
2153      else
2154        below_working_node->repos_relpath = NULL;
2155
2156      /* The revert_base checksum isn't available in the entry structure,
2157         so the caller provides it. */
2158
2159      /* text_base_info is NULL for files scheduled to be added. */
2160      below_working_node->checksum = NULL;
2161      if (text_base_info)
2162        {
2163          if (entry->schedule == svn_wc_schedule_delete)
2164            below_working_node->checksum =
2165              text_base_info->normal_base.sha1_checksum;
2166          else
2167            below_working_node->checksum =
2168              text_base_info->revert_base.sha1_checksum;
2169        }
2170      below_working_node->recorded_size = 0;
2171      below_working_node->changed_rev = SVN_INVALID_REVNUM;
2172      below_working_node->changed_date = 0;
2173      below_working_node->changed_author = NULL;
2174      below_working_node->depth = svn_depth_infinity;
2175      below_working_node->recorded_time = 0;
2176      below_working_node->properties = NULL;
2177
2178      if (working_node
2179          && entry->schedule == svn_wc_schedule_delete
2180          && working_node->repos_relpath)
2181        {
2182          /* We are lucky, our guesses above are not necessary. The known
2183             correct information is in working. But our op_depth design
2184             expects more information here */
2185          below_working_node->repos_relpath = working_node->repos_relpath;
2186          below_working_node->repos_id = working_node->repos_id;
2187          below_working_node->revision = working_node->revision;
2188
2189          /* Nice for 'svn status' */
2190          below_working_node->changed_rev = entry->cmt_rev;
2191          below_working_node->changed_date = entry->cmt_date;
2192          below_working_node->changed_author = entry->cmt_author;
2193
2194          /* And now remove it from WORKING, because in wc-ng code
2195             should read it from the lower layer */
2196          working_node->repos_relpath = NULL;
2197          working_node->repos_id = 0;
2198          working_node->revision = SVN_INVALID_REVNUM;
2199        }
2200
2201      SVN_ERR(insert_node(sdb, below_working_node, scratch_pool));
2202    }
2203
2204  /* Insert the working node. */
2205  if (working_node)
2206    {
2207      working_node->wc_id = wc_id;
2208      working_node->local_relpath = local_relpath;
2209      working_node->parent_relpath = parent_relpath;
2210      working_node->changed_rev = SVN_INVALID_REVNUM;
2211      working_node->recorded_time = entry->text_time;
2212      working_node->recorded_size = entry->working_size;
2213
2214      if (entry->depth != svn_depth_exclude)
2215        working_node->depth = entry->depth;
2216      else
2217        {
2218          working_node->presence = svn_wc__db_status_excluded;
2219          working_node->depth = svn_depth_infinity;
2220        }
2221
2222      if (entry->kind == svn_node_dir)
2223        working_node->checksum = NULL;
2224      else
2225        {
2226          working_node->checksum = NULL;
2227          /* text_base_info is NULL for files scheduled to be added. */
2228          if (text_base_info)
2229            working_node->checksum = text_base_info->normal_base.sha1_checksum;
2230
2231
2232          /* If an MD5 checksum is present in the entry, we can verify that
2233           * it matches the MD5 of the base file we found earlier. */
2234#ifdef SVN_DEBUG
2235          if (entry->checksum && text_base_info)
2236          {
2237            svn_checksum_t *md5_checksum;
2238            SVN_ERR(svn_checksum_parse_hex(&md5_checksum, svn_checksum_md5,
2239                                           entry->checksum, result_pool));
2240            SVN_ERR_ASSERT(
2241              md5_checksum && text_base_info->normal_base.md5_checksum);
2242            SVN_ERR_ASSERT(svn_checksum_match(
2243              md5_checksum, text_base_info->normal_base.md5_checksum));
2244          }
2245#endif
2246        }
2247
2248      working_node->kind = entry->kind;
2249      if (working_node->presence != svn_wc__db_status_excluded)
2250        {
2251          /* All subdirs start of incomplete, and stop being incomplete
2252             when the entries file in the subdir is upgraded. */
2253          if (entry->kind == svn_node_dir
2254              && strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR))
2255            {
2256              working_node->presence = svn_wc__db_status_incomplete;
2257              working_node->kind = svn_node_dir;
2258            }
2259          else if (entry->schedule == svn_wc_schedule_delete)
2260            {
2261              working_node->presence = svn_wc__db_status_base_deleted;
2262              working_node->kind = entry->kind;
2263            }
2264          else
2265            {
2266              /* presence == normal  */
2267              working_node->kind = entry->kind;
2268
2269              if (entry->incomplete)
2270                {
2271                  /* We shouldn't be overwriting another status.  */
2272                  WRITE_ENTRY_ASSERT(working_node->presence
2273                                     == svn_wc__db_status_normal);
2274                  working_node->presence = svn_wc__db_status_incomplete;
2275                }
2276            }
2277        }
2278
2279      /* These should generally be unset for added and deleted files,
2280         and contain whatever information we have for copied files. Let's
2281         just store whatever we have.
2282
2283         Note: cmt_rev is the distinguishing value. The others may be 0 or
2284         NULL if the corresponding revprop has been deleted.  */
2285      if (working_node->presence != svn_wc__db_status_base_deleted)
2286        {
2287          working_node->changed_rev = entry->cmt_rev;
2288          working_node->changed_date = entry->cmt_date;
2289          working_node->changed_author = entry->cmt_author;
2290        }
2291
2292      if (entry->schedule == svn_wc_schedule_delete
2293          && parent_node->work
2294          && parent_node->work->presence == svn_wc__db_status_base_deleted)
2295        {
2296          working_node->op_depth = parent_node->work->op_depth;
2297        }
2298      else if (!entry->copied)
2299        {
2300          working_node->op_depth
2301            = svn_wc__db_op_depth_for_upgrade(local_relpath);
2302        }
2303
2304      SVN_ERR(insert_node(sdb, working_node, scratch_pool));
2305    }
2306
2307  /* Insert the actual node. */
2308  if (actual_node)
2309    {
2310      actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
2311
2312      actual_node->wc_id = wc_id;
2313      actual_node->local_relpath = local_relpath;
2314      actual_node->parent_relpath = parent_relpath;
2315
2316      SVN_ERR(insert_actual_node(sdb, db, tmp_entry_abspath,
2317                                 actual_node, scratch_pool));
2318    }
2319
2320  if (entry_node)
2321    {
2322      *entry_node = apr_palloc(result_pool, sizeof(**entry_node));
2323      (*entry_node)->base = base_node;
2324      (*entry_node)->work = working_node;
2325      (*entry_node)->below_work = below_working_node;
2326      (*entry_node)->tree_conflicts = tree_conflicts;
2327    }
2328
2329  if (entry->file_external_path)
2330    {
2331      /* TODO: Maybe add a file external registration inside EXTERNALS here,
2332               to allow removing file externals that aren't referenced from
2333               svn:externals.
2334
2335         The svn:externals values are processed anyway after everything is
2336         upgraded */
2337    }
2338
2339  return SVN_NO_ERROR;
2340}
2341
2342static svn_error_t *
2343write_actual_only_entries(apr_hash_t *tree_conflicts,
2344                          svn_sqlite__db_t *sdb,
2345                          svn_wc__db_t *db,
2346                          const char *wri_abspath,
2347                          apr_int64_t wc_id,
2348                          const char *parent_relpath,
2349                          apr_pool_t *scratch_pool)
2350{
2351  apr_hash_index_t *hi;
2352
2353  for (hi = apr_hash_first(scratch_pool, tree_conflicts);
2354       hi;
2355       hi = apr_hash_next(hi))
2356    {
2357      db_actual_node_t *actual_node = NULL;
2358
2359      actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
2360      actual_node->wc_id = wc_id;
2361      actual_node->local_relpath = svn__apr_hash_index_key(hi);
2362      actual_node->parent_relpath = parent_relpath;
2363      actual_node->tree_conflict_data = svn__apr_hash_index_val(hi);
2364
2365      SVN_ERR(insert_actual_node(sdb, db, wri_abspath, actual_node,
2366                                 scratch_pool));
2367    }
2368
2369  return SVN_NO_ERROR;
2370}
2371
2372svn_error_t *
2373svn_wc__write_upgraded_entries(void **dir_baton,
2374                               void *parent_baton,
2375                               svn_wc__db_t *db,
2376                               svn_sqlite__db_t *sdb,
2377                               apr_int64_t repos_id,
2378                               apr_int64_t wc_id,
2379                               const char *dir_abspath,
2380                               const char *new_root_abspath,
2381                               apr_hash_t *entries,
2382                               apr_hash_t *text_bases_info,
2383                               apr_pool_t *result_pool,
2384                               apr_pool_t *scratch_pool)
2385{
2386  const svn_wc_entry_t *this_dir;
2387  apr_hash_index_t *hi;
2388  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2389  const char *old_root_abspath, *dir_relpath;
2390  struct write_baton *parent_node = parent_baton;
2391  struct write_baton *dir_node;
2392
2393  /* Get a copy of the "this dir" entry for comparison purposes. */
2394  this_dir = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR);
2395
2396  /* If there is no "this dir" entry, something is wrong. */
2397  if (! this_dir)
2398    return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
2399                             _("No default entry in directory '%s'"),
2400                             svn_dirent_local_style(dir_abspath,
2401                                                    iterpool));
2402  old_root_abspath = svn_dirent_get_longest_ancestor(dir_abspath,
2403                                                     new_root_abspath,
2404                                                     scratch_pool);
2405
2406  SVN_ERR_ASSERT(old_root_abspath[0]);
2407
2408  dir_relpath = svn_dirent_skip_ancestor(old_root_abspath, dir_abspath);
2409
2410  /* Write out "this dir" */
2411  SVN_ERR(write_entry(&dir_node, parent_node, db, sdb,
2412                      wc_id, repos_id, this_dir, NULL, dir_relpath,
2413                      svn_dirent_join(new_root_abspath, dir_relpath,
2414                                      iterpool),
2415                      old_root_abspath,
2416                      this_dir, FALSE, result_pool, iterpool));
2417
2418  for (hi = apr_hash_first(scratch_pool, entries); hi;
2419       hi = apr_hash_next(hi))
2420    {
2421      const char *name = svn__apr_hash_index_key(hi);
2422      const svn_wc_entry_t *this_entry = svn__apr_hash_index_val(hi);
2423      const char *child_abspath, *child_relpath;
2424      svn_wc__text_base_info_t *text_base_info
2425        = svn_hash_gets(text_bases_info, name);
2426
2427      svn_pool_clear(iterpool);
2428
2429      /* Don't rewrite the "this dir" entry! */
2430      if (strcmp(name, SVN_WC_ENTRY_THIS_DIR) == 0)
2431        continue;
2432
2433      /* Write the entry. Pass TRUE for create locks, because we still
2434         use this function for upgrading old working copies. */
2435      child_abspath = svn_dirent_join(dir_abspath, name, iterpool);
2436      child_relpath = svn_dirent_skip_ancestor(old_root_abspath, child_abspath);
2437      SVN_ERR(write_entry(NULL, dir_node, db, sdb,
2438                          wc_id, repos_id,
2439                          this_entry, text_base_info, child_relpath,
2440                          svn_dirent_join(new_root_abspath, child_relpath,
2441                                          iterpool),
2442                          old_root_abspath,
2443                          this_dir, TRUE, iterpool, iterpool));
2444    }
2445
2446  if (dir_node->tree_conflicts)
2447    SVN_ERR(write_actual_only_entries(dir_node->tree_conflicts, sdb, db,
2448                                      new_root_abspath, wc_id, dir_relpath,
2449                                      iterpool));
2450
2451  *dir_baton = dir_node;
2452  svn_pool_destroy(iterpool);
2453  return SVN_NO_ERROR;
2454}
2455
2456
2457svn_wc_entry_t *
2458svn_wc_entry_dup(const svn_wc_entry_t *entry, apr_pool_t *pool)
2459{
2460  svn_wc_entry_t *dupentry = apr_palloc(pool, sizeof(*dupentry));
2461
2462  /* Perform a trivial copy ... */
2463  *dupentry = *entry;
2464
2465  /* ...and then re-copy stuff that needs to be duped into our pool. */
2466  if (entry->name)
2467    dupentry->name = apr_pstrdup(pool, entry->name);
2468  if (entry->url)
2469    dupentry->url = apr_pstrdup(pool, entry->url);
2470  if (entry->repos)
2471    dupentry->repos = apr_pstrdup(pool, entry->repos);
2472  if (entry->uuid)
2473    dupentry->uuid = apr_pstrdup(pool, entry->uuid);
2474  if (entry->copyfrom_url)
2475    dupentry->copyfrom_url = apr_pstrdup(pool, entry->copyfrom_url);
2476  if (entry->conflict_old)
2477    dupentry->conflict_old = apr_pstrdup(pool, entry->conflict_old);
2478  if (entry->conflict_new)
2479    dupentry->conflict_new = apr_pstrdup(pool, entry->conflict_new);
2480  if (entry->conflict_wrk)
2481    dupentry->conflict_wrk = apr_pstrdup(pool, entry->conflict_wrk);
2482  if (entry->prejfile)
2483    dupentry->prejfile = apr_pstrdup(pool, entry->prejfile);
2484  if (entry->checksum)
2485    dupentry->checksum = apr_pstrdup(pool, entry->checksum);
2486  if (entry->cmt_author)
2487    dupentry->cmt_author = apr_pstrdup(pool, entry->cmt_author);
2488  if (entry->lock_token)
2489    dupentry->lock_token = apr_pstrdup(pool, entry->lock_token);
2490  if (entry->lock_owner)
2491    dupentry->lock_owner = apr_pstrdup(pool, entry->lock_owner);
2492  if (entry->lock_comment)
2493    dupentry->lock_comment = apr_pstrdup(pool, entry->lock_comment);
2494  if (entry->changelist)
2495    dupentry->changelist = apr_pstrdup(pool, entry->changelist);
2496
2497  /* NOTE: we do not dup cachable_props or present_props since they
2498     are deprecated. Use "" to indicate "nothing cachable or cached". */
2499  dupentry->cachable_props = "";
2500  dupentry->present_props = "";
2501
2502  if (entry->tree_conflict_data)
2503    dupentry->tree_conflict_data = apr_pstrdup(pool,
2504                                               entry->tree_conflict_data);
2505  if (entry->file_external_path)
2506    dupentry->file_external_path = apr_pstrdup(pool,
2507                                               entry->file_external_path);
2508  return dupentry;
2509}
2510
2511
2512/*** Generic Entry Walker */
2513
2514/* A recursive entry-walker, helper for svn_wc_walk_entries3().
2515 *
2516 * For this directory (DIRPATH, ADM_ACCESS), call the "found_entry" callback
2517 * in WALK_CALLBACKS, passing WALK_BATON to it. Then, for each versioned
2518 * entry in this directory, call the "found entry" callback and then recurse
2519 * (if it is a directory and if DEPTH allows).
2520 *
2521 * If SHOW_HIDDEN is true, include entries that are in a 'deleted' or
2522 * 'absent' state (and not scheduled for re-addition), else skip them.
2523 *
2524 * Call CANCEL_FUNC with CANCEL_BATON to allow cancellation.
2525 */
2526static svn_error_t *
2527walker_helper(const char *dirpath,
2528              svn_wc_adm_access_t *adm_access,
2529              const svn_wc_entry_callbacks2_t *walk_callbacks,
2530              void *walk_baton,
2531              svn_depth_t depth,
2532              svn_boolean_t show_hidden,
2533              svn_cancel_func_t cancel_func,
2534              void *cancel_baton,
2535              apr_pool_t *pool)
2536{
2537  apr_pool_t *subpool = svn_pool_create(pool);
2538  apr_hash_t *entries;
2539  apr_hash_index_t *hi;
2540  svn_wc_entry_t *dot_entry;
2541  svn_error_t *err;
2542  svn_wc__db_t *db = svn_wc__adm_get_db(adm_access);
2543
2544  err = svn_wc__entries_read_internal(&entries, adm_access, show_hidden,
2545                                      pool);
2546
2547  if (err)
2548    SVN_ERR(walk_callbacks->handle_error(dirpath, err, walk_baton, pool));
2549
2550  /* As promised, always return the '.' entry first. */
2551  dot_entry = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR);
2552  if (! dot_entry)
2553    return walk_callbacks->handle_error
2554      (dirpath, svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
2555                                  _("Directory '%s' has no THIS_DIR entry"),
2556                                  svn_dirent_local_style(dirpath, pool)),
2557       walk_baton, pool);
2558
2559  /* Call the "found entry" callback for this directory as a "this dir"
2560   * entry. Note that if this directory has been reached by recursion, this
2561   * is the second visit as it will already have been visited once as a
2562   * child entry of its parent. */
2563
2564  err = walk_callbacks->found_entry(dirpath, dot_entry, walk_baton, subpool);
2565
2566
2567  if(err)
2568    SVN_ERR(walk_callbacks->handle_error(dirpath, err, walk_baton, pool));
2569
2570  if (depth == svn_depth_empty)
2571    return SVN_NO_ERROR;
2572
2573  /* Loop over each of the other entries. */
2574  for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
2575    {
2576      const char *name = svn__apr_hash_index_key(hi);
2577      const svn_wc_entry_t *current_entry = svn__apr_hash_index_val(hi);
2578      const char *entrypath;
2579      const char *entry_abspath;
2580      svn_boolean_t hidden;
2581
2582      svn_pool_clear(subpool);
2583
2584      /* See if someone wants to cancel this operation. */
2585      if (cancel_func)
2586        SVN_ERR(cancel_func(cancel_baton));
2587
2588      /* Skip the "this dir" entry. */
2589      if (strcmp(current_entry->name, SVN_WC_ENTRY_THIS_DIR) == 0)
2590        continue;
2591
2592      entrypath = svn_dirent_join(dirpath, name, subpool);
2593      SVN_ERR(svn_wc__entry_is_hidden(&hidden, current_entry));
2594      SVN_ERR(svn_dirent_get_absolute(&entry_abspath, entrypath, subpool));
2595
2596      /* Call the "found entry" callback for this entry. (For a directory,
2597       * this is the first visit: as a child.) */
2598      if (current_entry->kind == svn_node_file
2599          || depth >= svn_depth_immediates)
2600        {
2601          err = walk_callbacks->found_entry(entrypath, current_entry,
2602                                            walk_baton, subpool);
2603
2604          if (err)
2605            SVN_ERR(walk_callbacks->handle_error(entrypath, err,
2606                                                 walk_baton, pool));
2607        }
2608
2609      /* Recurse into this entry if appropriate. */
2610      if (current_entry->kind == svn_node_dir
2611          && !hidden
2612          && depth >= svn_depth_immediates)
2613        {
2614          svn_wc_adm_access_t *entry_access;
2615          svn_depth_t depth_below_here = depth;
2616
2617          if (depth == svn_depth_immediates)
2618            depth_below_here = svn_depth_empty;
2619
2620          entry_access = svn_wc__adm_retrieve_internal2(db, entry_abspath,
2621                                                        subpool);
2622
2623          if (entry_access)
2624            SVN_ERR(walker_helper(entrypath, entry_access,
2625                                  walk_callbacks, walk_baton,
2626                                  depth_below_here, show_hidden,
2627                                  cancel_func, cancel_baton,
2628                                  subpool));
2629        }
2630    }
2631
2632  svn_pool_destroy(subpool);
2633  return SVN_NO_ERROR;
2634}
2635
2636svn_error_t *
2637svn_wc__walker_default_error_handler(const char *path,
2638                                     svn_error_t *err,
2639                                     void *walk_baton,
2640                                     apr_pool_t *pool)
2641{
2642  /* Note: don't trace this. We don't want to insert a false "stack frame"
2643     onto an error generated elsewhere.  */
2644  return svn_error_trace(err);
2645}
2646
2647
2648/* The public API. */
2649svn_error_t *
2650svn_wc_walk_entries3(const char *path,
2651                     svn_wc_adm_access_t *adm_access,
2652                     const svn_wc_entry_callbacks2_t *walk_callbacks,
2653                     void *walk_baton,
2654                     svn_depth_t walk_depth,
2655                     svn_boolean_t show_hidden,
2656                     svn_cancel_func_t cancel_func,
2657                     void *cancel_baton,
2658                     apr_pool_t *pool)
2659{
2660  const char *local_abspath;
2661  svn_wc__db_t *db = svn_wc__adm_get_db(adm_access);
2662  svn_error_t *err;
2663  svn_node_kind_t kind;
2664  svn_depth_t depth;
2665
2666  SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
2667  err = svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL,
2668                             NULL, NULL, NULL, &depth, NULL, NULL,
2669                             NULL, NULL, NULL, NULL, NULL, NULL,
2670                             NULL, NULL, NULL, NULL, NULL, NULL,
2671                             NULL, NULL, NULL,
2672                             db, local_abspath,
2673                             pool, pool);
2674  if (err)
2675    {
2676      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2677        return svn_error_trace(err);
2678      /* Remap into SVN_ERR_UNVERSIONED_RESOURCE.  */
2679      svn_error_clear(err);
2680      return walk_callbacks->handle_error(
2681        path, svn_error_createf(SVN_ERR_UNVERSIONED_RESOURCE, NULL,
2682                                _("'%s' is not under version control"),
2683                                svn_dirent_local_style(local_abspath, pool)),
2684        walk_baton, pool);
2685    }
2686
2687  if (kind == svn_node_file || depth == svn_depth_exclude)
2688    {
2689      const svn_wc_entry_t *entry;
2690
2691      /* ### we should stop passing out entry structures.
2692         ###
2693         ### we should not call handle_error for an error the *callback*
2694         ###   gave us. let it deal with the problem before returning.  */
2695
2696      if (!show_hidden)
2697        {
2698          svn_boolean_t hidden;
2699          SVN_ERR(svn_wc__db_node_hidden(&hidden, db, local_abspath, pool));
2700
2701          if (hidden)
2702            {
2703              /* The fool asked to walk a "hidden" node. Report the node as
2704                 unversioned.
2705
2706                 ### this is incorrect behavior. see depth_test 36. the walk
2707                 ### API will be revamped to avoid entry structures. we should
2708                 ### be able to solve the problem with the new API. (since we
2709                 ### shouldn't return a hidden entry here)  */
2710              return walk_callbacks->handle_error(
2711                               path, svn_error_createf(
2712                                  SVN_ERR_UNVERSIONED_RESOURCE, NULL,
2713                                  _("'%s' is not under version control"),
2714                                  svn_dirent_local_style(local_abspath, pool)),
2715                               walk_baton, pool);
2716            }
2717        }
2718
2719      SVN_ERR(svn_wc__get_entry(&entry, db, local_abspath, FALSE,
2720                                svn_node_file, pool, pool));
2721
2722      err = walk_callbacks->found_entry(path, entry, walk_baton, pool);
2723      if (err)
2724        return walk_callbacks->handle_error(path, err, walk_baton, pool);
2725
2726      return SVN_NO_ERROR;
2727    }
2728
2729  if (kind == svn_node_dir)
2730    return walker_helper(path, adm_access, walk_callbacks, walk_baton,
2731                         walk_depth, show_hidden, cancel_func, cancel_baton,
2732                         pool);
2733
2734  return walk_callbacks->handle_error(
2735       path, svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL,
2736                               _("'%s' has an unrecognized node kind"),
2737                               svn_dirent_local_style(local_abspath, pool)),
2738       walk_baton, pool);
2739}
2740