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