1251881Speter/*
2251881Speter * entries.c :  manipulating the administrative `entries' file.
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter#include <string.h>
25251881Speter#include <assert.h>
26251881Speter
27251881Speter#include <apr_strings.h>
28251881Speter
29251881Speter#include "svn_error.h"
30251881Speter#include "svn_types.h"
31251881Speter#include "svn_time.h"
32251881Speter#include "svn_pools.h"
33251881Speter#include "svn_dirent_uri.h"
34251881Speter#include "svn_path.h"
35251881Speter#include "svn_ctype.h"
36251881Speter#include "svn_string.h"
37251881Speter#include "svn_hash.h"
38251881Speter
39251881Speter#include "wc.h"
40251881Speter#include "adm_files.h"
41251881Speter#include "conflicts.h"
42251881Speter#include "entries.h"
43251881Speter#include "lock.h"
44251881Speter#include "tree_conflicts.h"
45251881Speter#include "wc_db.h"
46251881Speter#include "wc-queries.h"  /* for STMT_*  */
47251881Speter
48251881Speter#include "svn_private_config.h"
49251881Speter#include "private/svn_wc_private.h"
50251881Speter#include "private/svn_sqlite.h"
51251881Speter
52251881Speter#define MAYBE_ALLOC(x,p) ((x) ? (x) : apr_pcalloc((p), sizeof(*(x))))
53251881Speter
54251881Speter
55251881Speter/* Temporary structures which mirror the tables in wc-metadata.sql.
56251881Speter   For detailed descriptions of each field, see that file. */
57251881Spetertypedef struct db_node_t {
58251881Speter  apr_int64_t wc_id;
59251881Speter  const char *local_relpath;
60251881Speter  int op_depth;
61251881Speter  apr_int64_t repos_id;
62251881Speter  const char *repos_relpath;
63251881Speter  const char *parent_relpath;
64251881Speter  svn_wc__db_status_t presence;
65251881Speter  svn_revnum_t revision;
66251881Speter  svn_node_kind_t kind;
67251881Speter  svn_checksum_t *checksum;
68251881Speter  svn_filesize_t recorded_size;
69251881Speter  svn_revnum_t changed_rev;
70251881Speter  apr_time_t changed_date;
71251881Speter  const char *changed_author;
72251881Speter  svn_depth_t depth;
73251881Speter  apr_time_t recorded_time;
74251881Speter  apr_hash_t *properties;
75251881Speter  svn_boolean_t file_external;
76251881Speter  apr_array_header_t *inherited_props;
77251881Speter} db_node_t;
78251881Speter
79251881Spetertypedef struct db_actual_node_t {
80251881Speter  apr_int64_t wc_id;
81251881Speter  const char *local_relpath;
82251881Speter  const char *parent_relpath;
83251881Speter  apr_hash_t *properties;
84251881Speter  const char *conflict_old;
85251881Speter  const char *conflict_new;
86251881Speter  const char *conflict_working;
87251881Speter  const char *prop_reject;
88251881Speter  const char *changelist;
89251881Speter  /* ### enum for text_mod */
90251881Speter  const char *tree_conflict_data;
91251881Speter} db_actual_node_t;
92251881Speter
93251881Speter
94251881Speter
95251881Speter/*** reading and writing the entries file ***/
96251881Speter
97251881Speter
98251881Speter/* */
99251881Speterstatic svn_wc_entry_t *
100251881Speteralloc_entry(apr_pool_t *pool)
101251881Speter{
102251881Speter  svn_wc_entry_t *entry = apr_pcalloc(pool, sizeof(*entry));
103251881Speter  entry->revision = SVN_INVALID_REVNUM;
104251881Speter  entry->copyfrom_rev = SVN_INVALID_REVNUM;
105251881Speter  entry->cmt_rev = SVN_INVALID_REVNUM;
106251881Speter  entry->kind = svn_node_none;
107251881Speter  entry->working_size = SVN_WC_ENTRY_WORKING_SIZE_UNKNOWN;
108251881Speter  entry->depth = svn_depth_infinity;
109251881Speter  entry->file_external_peg_rev.kind = svn_opt_revision_unspecified;
110251881Speter  entry->file_external_rev.kind = svn_opt_revision_unspecified;
111251881Speter  return entry;
112251881Speter}
113251881Speter
114251881Speter
115251881Speter/* Is the entry in a 'hidden' state in the sense of the 'show_hidden'
116251881Speter * switches on svn_wc_entries_read(), svn_wc_walk_entries*(), etc.? */
117251881Spetersvn_error_t *
118251881Spetersvn_wc__entry_is_hidden(svn_boolean_t *hidden, const svn_wc_entry_t *entry)
119251881Speter{
120251881Speter  /* In English, the condition is: "the entry is not present, and I haven't
121251881Speter     scheduled something over the top of it."  */
122251881Speter  if (entry->deleted
123251881Speter      || entry->absent
124251881Speter      || entry->depth == svn_depth_exclude)
125251881Speter    {
126251881Speter      /* These kinds of nodes cannot be marked for deletion (which also
127251881Speter         means no "replace" either).  */
128251881Speter      SVN_ERR_ASSERT(entry->schedule == svn_wc_schedule_add
129251881Speter                     || entry->schedule == svn_wc_schedule_normal);
130251881Speter
131251881Speter      /* Hidden if something hasn't been added over it.
132251881Speter
133251881Speter         ### is this even possible with absent or excluded nodes?  */
134251881Speter      *hidden = entry->schedule != svn_wc_schedule_add;
135251881Speter    }
136251881Speter  else
137251881Speter    *hidden = FALSE;
138251881Speter
139251881Speter  return SVN_NO_ERROR;
140251881Speter}
141251881Speter
142251881Speter
143251881Speter/* Hit the database to check the file external information for the given
144251881Speter   entry.  The entry will be modified in place. */
145251881Speterstatic svn_error_t *
146251881Spetercheck_file_external(svn_wc_entry_t *entry,
147251881Speter                    svn_wc__db_t *db,
148251881Speter                    const char *local_abspath,
149251881Speter                    const char *wri_abspath,
150251881Speter                    apr_pool_t *result_pool,
151251881Speter                    apr_pool_t *scratch_pool)
152251881Speter{
153251881Speter  svn_wc__db_status_t status;
154251881Speter  svn_node_kind_t kind;
155251881Speter  const char *repos_relpath;
156251881Speter  svn_revnum_t peg_revision;
157251881Speter  svn_revnum_t revision;
158251881Speter  svn_error_t *err;
159251881Speter
160251881Speter  err = svn_wc__db_external_read(&status, &kind, NULL, NULL, NULL,
161251881Speter                                 &repos_relpath, &peg_revision, &revision,
162251881Speter                                 db, local_abspath, wri_abspath,
163251881Speter                                 result_pool, scratch_pool);
164251881Speter
165251881Speter  if (err)
166251881Speter    {
167251881Speter      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
168251881Speter        return svn_error_trace(err);
169251881Speter
170251881Speter      svn_error_clear(err);
171251881Speter      return SVN_NO_ERROR;
172251881Speter    }
173251881Speter
174251881Speter  if (status == svn_wc__db_status_normal
175251881Speter      && kind == svn_node_file)
176251881Speter    {
177251881Speter      entry->file_external_path = repos_relpath;
178251881Speter      if (SVN_IS_VALID_REVNUM(peg_revision))
179251881Speter        {
180251881Speter          entry->file_external_peg_rev.kind = svn_opt_revision_number;
181251881Speter          entry->file_external_peg_rev.value.number = peg_revision;
182251881Speter          entry->file_external_rev = entry->file_external_peg_rev;
183251881Speter        }
184251881Speter      if (SVN_IS_VALID_REVNUM(revision))
185251881Speter        {
186251881Speter          entry->file_external_rev.kind = svn_opt_revision_number;
187251881Speter          entry->file_external_rev.value.number = revision;
188251881Speter        }
189251881Speter    }
190251881Speter
191251881Speter  return SVN_NO_ERROR;
192251881Speter}
193251881Speter
194251881Speter
195251881Speter/* Fill in the following fields of ENTRY:
196251881Speter
197251881Speter     REVISION
198251881Speter     REPOS
199251881Speter     UUID
200251881Speter     CMT_REV
201251881Speter     CMT_DATE
202251881Speter     CMT_AUTHOR
203251881Speter     DEPTH
204251881Speter     DELETED
205251881Speter
206251881Speter   Return: KIND, REPOS_RELPATH, CHECKSUM
207251881Speter*/
208251881Speterstatic svn_error_t *
209251881Speterget_info_for_deleted(svn_wc_entry_t *entry,
210251881Speter                     svn_node_kind_t *kind,
211251881Speter                     const char **repos_relpath,
212251881Speter                     const svn_checksum_t **checksum,
213251881Speter                     svn_wc__db_lock_t **lock,
214251881Speter                     svn_wc__db_t *db,
215251881Speter                     const char *entry_abspath,
216251881Speter                     const svn_wc_entry_t *parent_entry,
217251881Speter                     svn_boolean_t have_base,
218251881Speter                     svn_boolean_t have_more_work,
219251881Speter                     apr_pool_t *result_pool,
220251881Speter                     apr_pool_t *scratch_pool)
221251881Speter{
222251881Speter  if (have_base && !have_more_work)
223251881Speter    {
224251881Speter      /* This is the delete of a BASE node */
225251881Speter      SVN_ERR(svn_wc__db_base_get_info(NULL, kind,
226251881Speter                                       &entry->revision,
227251881Speter                                       repos_relpath,
228251881Speter                                       &entry->repos,
229251881Speter                                       &entry->uuid,
230251881Speter                                       &entry->cmt_rev,
231251881Speter                                       &entry->cmt_date,
232251881Speter                                       &entry->cmt_author,
233251881Speter                                       &entry->depth,
234251881Speter                                       checksum,
235251881Speter                                       NULL,
236251881Speter                                       lock,
237251881Speter                                       &entry->has_props, NULL,
238251881Speter                                       NULL,
239251881Speter                                       db,
240251881Speter                                       entry_abspath,
241251881Speter                                       result_pool,
242251881Speter                                       scratch_pool));
243251881Speter    }
244251881Speter  else
245251881Speter    {
246251881Speter      const char *work_del_abspath;
247251881Speter      const char *parent_repos_relpath;
248251881Speter      const char *parent_abspath;
249251881Speter
250251881Speter      /* This is a deleted child of a copy/move-here,
251251881Speter         so we need to scan up the WORKING tree to find the root of
252251881Speter         the deletion. Then examine its parent to discover its
253251881Speter         future location in the repository.  */
254251881Speter      SVN_ERR(svn_wc__db_read_pristine_info(NULL, kind,
255251881Speter                                            &entry->cmt_rev,
256251881Speter                                            &entry->cmt_date,
257251881Speter                                            &entry->cmt_author,
258251881Speter                                            &entry->depth,
259251881Speter                                            checksum,
260251881Speter                                            NULL,
261251881Speter                                            &entry->has_props, NULL,
262251881Speter                                            db,
263251881Speter                                            entry_abspath,
264251881Speter                                            result_pool,
265251881Speter                                            scratch_pool));
266251881Speter      /* working_size and text_time unavailable */
267251881Speter
268251881Speter     SVN_ERR(svn_wc__db_scan_deletion(NULL,
269251881Speter                                      NULL,
270251881Speter                                      &work_del_abspath, NULL,
271251881Speter                                      db, entry_abspath,
272251881Speter                                      scratch_pool, scratch_pool));
273251881Speter
274251881Speter      SVN_ERR_ASSERT(work_del_abspath != NULL);
275251881Speter      parent_abspath = svn_dirent_dirname(work_del_abspath, scratch_pool);
276251881Speter
277251881Speter      /* The parent directory of the delete root must be added, so we
278251881Speter         can find the required information there */
279251881Speter      SVN_ERR(svn_wc__db_scan_addition(NULL, NULL,
280251881Speter                                       &parent_repos_relpath,
281251881Speter                                       &entry->repos,
282251881Speter                                       &entry->uuid,
283251881Speter                                       NULL, NULL, NULL, NULL,
284251881Speter                                       db, parent_abspath,
285251881Speter                                       result_pool, scratch_pool));
286251881Speter
287251881Speter      /* Now glue it all together */
288251881Speter      *repos_relpath = svn_relpath_join(parent_repos_relpath,
289251881Speter                                        svn_dirent_is_child(parent_abspath,
290251881Speter                                                            entry_abspath,
291251881Speter                                                            NULL),
292251881Speter                                        result_pool);
293251881Speter
294251881Speter
295251881Speter      /* Even though this is the delete of a WORKING node, there might still
296251881Speter         be a BASE node somewhere below with an interesting revision */
297251881Speter      if (have_base)
298251881Speter        {
299251881Speter          svn_wc__db_status_t status;
300251881Speter          SVN_ERR(svn_wc__db_base_get_info(&status, NULL, &entry->revision,
301251881Speter                                           NULL, NULL, NULL, NULL, NULL, NULL,
302251881Speter                                           NULL, NULL, NULL, lock, NULL, NULL,
303251881Speter                                           NULL,
304251881Speter                                           db, entry_abspath,
305251881Speter                                           result_pool, scratch_pool));
306251881Speter
307251881Speter          if (status == svn_wc__db_status_not_present)
308251881Speter            entry->deleted = TRUE;
309251881Speter        }
310251881Speter    }
311251881Speter
312251881Speter  /* Do some extra work for the child nodes.  */
313251881Speter  if (!SVN_IS_VALID_REVNUM(entry->revision) && parent_entry != NULL)
314251881Speter    {
315251881Speter      /* For child nodes without a revision, pick up the parent's
316251881Speter         revision.  */
317251881Speter      entry->revision = parent_entry->revision;
318251881Speter    }
319251881Speter
320251881Speter  return SVN_NO_ERROR;
321251881Speter}
322251881Speter
323251881Speter
324251881Speter/*
325251881Speter * Encode tree conflict descriptions into a single string.
326251881Speter *
327251881Speter * Set *CONFLICT_DATA to a string, allocated in POOL, that encodes the tree
328251881Speter * conflicts in CONFLICTS in a form suitable for storage in a single string
329251881Speter * field in a WC entry. CONFLICTS is a hash of zero or more pointers to
330251881Speter * svn_wc_conflict_description2_t objects, index by their basenames. All of the
331251881Speter * conflict victim paths must be siblings.
332251881Speter *
333251881Speter * Do all allocations in POOL.
334251881Speter *
335251881Speter * @see svn_wc__read_tree_conflicts()
336251881Speter */
337251881Speterstatic svn_error_t *
338251881Speterwrite_tree_conflicts(const char **conflict_data,
339251881Speter                     apr_hash_t *conflicts,
340251881Speter                     apr_pool_t *pool)
341251881Speter{
342251881Speter  svn_skel_t *skel = svn_skel__make_empty_list(pool);
343251881Speter  apr_hash_index_t *hi;
344251881Speter
345251881Speter  for (hi = apr_hash_first(pool, conflicts); hi; hi = apr_hash_next(hi))
346251881Speter    {
347251881Speter      svn_skel_t *c_skel;
348251881Speter
349251881Speter      SVN_ERR(svn_wc__serialize_conflict(&c_skel, svn__apr_hash_index_val(hi),
350251881Speter                                         pool, pool));
351251881Speter      svn_skel__prepend(c_skel, skel);
352251881Speter    }
353251881Speter
354251881Speter  *conflict_data = svn_skel__unparse(skel, pool)->data;
355251881Speter
356251881Speter  return SVN_NO_ERROR;
357251881Speter}
358251881Speter
359251881Speter
360251881Speter/* Read one entry from wc_db. It will be allocated in RESULT_POOL and
361251881Speter   returned in *NEW_ENTRY.
362251881Speter
363251881Speter   DIR_ABSPATH is the name of the directory to read this entry from, and
364251881Speter   it will be named NAME (use "" for "this dir").
365251881Speter
366251881Speter   DB specifies the wc_db database, and WC_ID specifies which working copy
367251881Speter   this information is being read from.
368251881Speter
369251881Speter   If this node is "this dir", then PARENT_ENTRY should be NULL. Otherwise,
370251881Speter   it should refer to the entry for the child's parent directory.
371251881Speter
372251881Speter   Temporary allocations are made in SCRATCH_POOL.  */
373251881Speterstatic svn_error_t *
374251881Speterread_one_entry(const svn_wc_entry_t **new_entry,
375251881Speter               svn_wc__db_t *db,
376251881Speter               apr_int64_t wc_id,
377251881Speter               const char *dir_abspath,
378251881Speter               const char *name,
379251881Speter               const svn_wc_entry_t *parent_entry,
380251881Speter               apr_pool_t *result_pool,
381251881Speter               apr_pool_t *scratch_pool)
382251881Speter{
383251881Speter  svn_node_kind_t kind;
384251881Speter  svn_wc__db_status_t status;
385251881Speter  svn_wc__db_lock_t *lock;
386251881Speter  const char *repos_relpath;
387251881Speter  const svn_checksum_t *checksum;
388251881Speter  svn_filesize_t translated_size;
389251881Speter  svn_wc_entry_t *entry = alloc_entry(result_pool);
390251881Speter  const char *entry_abspath;
391251881Speter  const char *original_repos_relpath;
392251881Speter  const char *original_root_url;
393251881Speter  svn_boolean_t conflicted;
394251881Speter  svn_boolean_t have_base;
395251881Speter  svn_boolean_t have_more_work;
396251881Speter
397251881Speter  entry->name = name;
398251881Speter
399251881Speter  entry_abspath = svn_dirent_join(dir_abspath, entry->name, scratch_pool);
400251881Speter
401251881Speter  SVN_ERR(svn_wc__db_read_info(
402251881Speter            &status,
403251881Speter            &kind,
404251881Speter            &entry->revision,
405251881Speter            &repos_relpath,
406251881Speter            &entry->repos,
407251881Speter            &entry->uuid,
408251881Speter            &entry->cmt_rev,
409251881Speter            &entry->cmt_date,
410251881Speter            &entry->cmt_author,
411251881Speter            &entry->depth,
412251881Speter            &checksum,
413251881Speter            NULL,
414251881Speter            &original_repos_relpath,
415251881Speter            &original_root_url,
416251881Speter            NULL,
417251881Speter            &entry->copyfrom_rev,
418251881Speter            &lock,
419251881Speter            &translated_size,
420251881Speter            &entry->text_time,
421251881Speter            &entry->changelist,
422251881Speter            &conflicted,
423251881Speter            NULL /* op_root */,
424251881Speter            &entry->has_props /* have_props */,
425251881Speter            &entry->has_prop_mods /* props_mod */,
426251881Speter            &have_base,
427251881Speter            &have_more_work,
428251881Speter            NULL /* have_work */,
429251881Speter            db,
430251881Speter            entry_abspath,
431251881Speter            result_pool,
432251881Speter            scratch_pool));
433251881Speter
434251881Speter  if (entry->has_prop_mods)
435251881Speter    entry->has_props = TRUE;
436251881Speter
437251881Speter  if (strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR) == 0)
438251881Speter    {
439251881Speter      /* get the tree conflict data. */
440251881Speter      apr_hash_t *tree_conflicts = NULL;
441251881Speter      const apr_array_header_t *conflict_victims;
442251881Speter      int k;
443251881Speter
444251881Speter      SVN_ERR(svn_wc__db_read_conflict_victims(&conflict_victims, db,
445251881Speter                                               dir_abspath,
446251881Speter                                               scratch_pool,
447251881Speter                                               scratch_pool));
448251881Speter
449251881Speter      for (k = 0; k < conflict_victims->nelts; k++)
450251881Speter        {
451251881Speter          int j;
452251881Speter          const apr_array_header_t *child_conflicts;
453251881Speter          const char *child_name;
454251881Speter          const char *child_abspath;
455251881Speter
456251881Speter          child_name = APR_ARRAY_IDX(conflict_victims, k, const char *);
457251881Speter          child_abspath = svn_dirent_join(dir_abspath, child_name,
458251881Speter                                          scratch_pool);
459251881Speter
460251881Speter          SVN_ERR(svn_wc__read_conflicts(&child_conflicts,
461251881Speter                                         db, child_abspath,
462251881Speter                                         FALSE /* create tempfiles */,
463251881Speter                                         scratch_pool, scratch_pool));
464251881Speter
465251881Speter          for (j = 0; j < child_conflicts->nelts; j++)
466251881Speter            {
467251881Speter              const svn_wc_conflict_description2_t *conflict =
468251881Speter                APR_ARRAY_IDX(child_conflicts, j,
469251881Speter                              svn_wc_conflict_description2_t *);
470251881Speter
471251881Speter              if (conflict->kind == svn_wc_conflict_kind_tree)
472251881Speter                {
473251881Speter                  if (!tree_conflicts)
474251881Speter                    tree_conflicts = apr_hash_make(scratch_pool);
475251881Speter                  svn_hash_sets(tree_conflicts, child_name, conflict);
476251881Speter                }
477251881Speter            }
478251881Speter        }
479251881Speter
480251881Speter      if (tree_conflicts)
481251881Speter        {
482251881Speter          SVN_ERR(write_tree_conflicts(&entry->tree_conflict_data,
483251881Speter                                       tree_conflicts, result_pool));
484251881Speter        }
485251881Speter    }
486251881Speter
487251881Speter  if (status == svn_wc__db_status_normal
488251881Speter      || status == svn_wc__db_status_incomplete)
489251881Speter    {
490251881Speter      /* Plain old BASE node.  */
491251881Speter      entry->schedule = svn_wc_schedule_normal;
492251881Speter
493251881Speter      /* Grab inherited repository information, if necessary. */
494251881Speter      if (repos_relpath == NULL)
495251881Speter        {
496251881Speter          SVN_ERR(svn_wc__db_scan_base_repos(&repos_relpath,
497251881Speter                                             &entry->repos,
498251881Speter                                             &entry->uuid,
499251881Speter                                             db,
500251881Speter                                             entry_abspath,
501251881Speter                                             result_pool,
502251881Speter                                             scratch_pool));
503251881Speter        }
504251881Speter
505251881Speter      entry->incomplete = (status == svn_wc__db_status_incomplete);
506251881Speter    }
507251881Speter  else if (status == svn_wc__db_status_deleted)
508251881Speter    {
509251881Speter      svn_node_kind_t path_kind;
510251881Speter
511251881Speter      /* ### we don't have to worry about moves, so this is a delete. */
512251881Speter      entry->schedule = svn_wc_schedule_delete;
513251881Speter
514251881Speter      /* If there are multiple working layers or no BASE layer, then
515251881Speter         this is a WORKING delete or WORKING not-present. */
516251881Speter      if (have_more_work || !have_base)
517251881Speter        entry->copied = TRUE;
518251881Speter      else if (have_base && !have_more_work)
519251881Speter        entry->copied = FALSE;
520251881Speter      else
521251881Speter        {
522251881Speter          const char *work_del_abspath;
523251881Speter          SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL,
524251881Speter                                           &work_del_abspath, NULL,
525251881Speter                                           db, entry_abspath,
526251881Speter                                           scratch_pool, scratch_pool));
527251881Speter
528251881Speter          if (work_del_abspath)
529251881Speter            entry->copied = TRUE;
530251881Speter        }
531251881Speter
532251881Speter      /* If there is still a directory on-disk we keep it, if not it is
533251881Speter         already deleted. Simple, isn't it?
534251881Speter
535251881Speter         Before single-db we had to keep the administative area alive until
536251881Speter         after the commit really deletes it. Setting keep alive stopped the
537251881Speter         commit processing from deleting the directory. We don't delete it
538251881Speter         any more, so all we have to do is provide some 'sane' value.
539251881Speter       */
540251881Speter      SVN_ERR(svn_io_check_path(entry_abspath, &path_kind, scratch_pool));
541251881Speter      entry->keep_local = (path_kind == svn_node_dir);
542251881Speter    }
543251881Speter  else if (status == svn_wc__db_status_added)
544251881Speter    {
545251881Speter      svn_wc__db_status_t work_status;
546251881Speter      const char *op_root_abspath;
547251881Speter      const char *scanned_original_relpath;
548251881Speter      svn_revnum_t original_revision;
549251881Speter
550251881Speter      /* For child nodes, pick up the parent's revision.  */
551251881Speter      if (*entry->name != '\0')
552251881Speter        {
553251881Speter          assert(parent_entry != NULL);
554251881Speter          assert(entry->revision == SVN_INVALID_REVNUM);
555251881Speter
556251881Speter          entry->revision = parent_entry->revision;
557251881Speter        }
558251881Speter
559251881Speter      if (have_base)
560251881Speter        {
561251881Speter          svn_wc__db_status_t base_status;
562251881Speter
563251881Speter          /* ENTRY->REVISION is overloaded. When a node is schedule-add
564251881Speter             or -replace, then REVISION refers to the BASE node's revision
565251881Speter             that is being overwritten. We need to fetch it now.  */
566251881Speter          SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL,
567251881Speter                                           &entry->revision,
568251881Speter                                           NULL, NULL, NULL,
569251881Speter                                           NULL, NULL, NULL,
570251881Speter                                           NULL, NULL, NULL,
571251881Speter                                           NULL, NULL, NULL, NULL,
572251881Speter                                           db, entry_abspath,
573251881Speter                                           scratch_pool,
574251881Speter                                           scratch_pool));
575251881Speter
576251881Speter          if (base_status == svn_wc__db_status_not_present)
577251881Speter            {
578251881Speter              /* The underlying node is DELETED in this revision.  */
579251881Speter              entry->deleted = TRUE;
580251881Speter
581251881Speter              /* This is an add since there isn't a node to replace.  */
582251881Speter              entry->schedule = svn_wc_schedule_add;
583251881Speter            }
584251881Speter          else
585251881Speter            entry->schedule = svn_wc_schedule_replace;
586251881Speter        }
587251881Speter      else
588251881Speter        {
589251881Speter          /* There is NO 'not-present' BASE_NODE for this node.
590251881Speter             Therefore, we are looking at some kind of add/copy
591251881Speter             rather than a replace.  */
592251881Speter
593251881Speter          /* ### if this looks like a plain old add, then rev=0.  */
594251881Speter          if (!SVN_IS_VALID_REVNUM(entry->copyfrom_rev)
595251881Speter              && !SVN_IS_VALID_REVNUM(entry->cmt_rev))
596251881Speter            entry->revision = 0;
597251881Speter
598251881Speter          entry->schedule = svn_wc_schedule_add;
599251881Speter        }
600251881Speter
601251881Speter      /* If we don't have "real" data from the entry (obstruction),
602251881Speter         then we cannot begin a scan for data. The original node may
603251881Speter         have important data. Set up stuff to kill that idea off,
604251881Speter         and finish up this entry.  */
605251881Speter        {
606251881Speter          SVN_ERR(svn_wc__db_scan_addition(&work_status,
607251881Speter                                           &op_root_abspath,
608251881Speter                                           &repos_relpath,
609251881Speter                                           &entry->repos,
610251881Speter                                           &entry->uuid,
611251881Speter                                           &scanned_original_relpath,
612251881Speter                                           NULL, NULL, /* original_root|uuid */
613251881Speter                                           &original_revision,
614251881Speter                                           db,
615251881Speter                                           entry_abspath,
616251881Speter                                           result_pool, scratch_pool));
617251881Speter
618251881Speter          /* In wc.db we want to keep the valid revision of the not-present
619251881Speter             BASE_REV, but when we used entries we set the revision to 0
620251881Speter             when adding a new node over a not present base node. */
621251881Speter          if (work_status == svn_wc__db_status_added
622251881Speter              && entry->deleted)
623251881Speter            entry->revision = 0;
624251881Speter        }
625251881Speter
626251881Speter      if (!SVN_IS_VALID_REVNUM(entry->cmt_rev)
627251881Speter          && scanned_original_relpath == NULL)
628251881Speter        {
629251881Speter          /* There is NOT a last-changed revision (last-changed date and
630251881Speter             author may be unknown, but we can always check the rev).
631251881Speter             The absence of a revision implies this node was added WITHOUT
632251881Speter             any history. Avoid the COPIED checks in the else block.  */
633251881Speter          /* ### scan_addition may need to be updated to avoid returning
634251881Speter             ### status_copied in this case.  */
635251881Speter        }
636251881Speter      /* For backwards-compatiblity purposes we treat moves just like
637251881Speter       * regular copies. */
638251881Speter      else if (work_status == svn_wc__db_status_copied ||
639251881Speter               work_status == svn_wc__db_status_moved_here)
640251881Speter        {
641251881Speter          entry->copied = TRUE;
642251881Speter
643251881Speter          /* If this is a child of a copied subtree, then it should be
644251881Speter             schedule_normal.  */
645251881Speter          if (original_repos_relpath == NULL)
646251881Speter            {
647251881Speter              /* ### what if there is a BASE node under there? */
648251881Speter              entry->schedule = svn_wc_schedule_normal;
649251881Speter            }
650251881Speter
651251881Speter          /* Copied nodes need to mirror their copyfrom_rev, if they
652251881Speter             don't have a revision of their own already. */
653251881Speter          if (!SVN_IS_VALID_REVNUM(entry->revision)
654251881Speter              || entry->revision == 0 /* added */)
655251881Speter            entry->revision = original_revision;
656251881Speter        }
657251881Speter
658251881Speter      /* Does this node have copyfrom_* information?  */
659251881Speter      if (scanned_original_relpath != NULL)
660251881Speter        {
661251881Speter          svn_boolean_t is_copied_child;
662251881Speter          svn_boolean_t is_mixed_rev = FALSE;
663251881Speter
664251881Speter          SVN_ERR_ASSERT(work_status == svn_wc__db_status_copied ||
665251881Speter                         work_status == svn_wc__db_status_moved_here);
666251881Speter
667251881Speter          /* If this node inherits copyfrom information from an
668251881Speter             ancestor node, then it must be a copied child.  */
669251881Speter          is_copied_child = (original_repos_relpath == NULL);
670251881Speter
671251881Speter          /* If this node has copyfrom information on it, then it may
672251881Speter             be an actual copy-root, or it could be participating in
673251881Speter             a mixed-revision copied tree. So if we don't already know
674251881Speter             this is a copied child, then we need to look for this
675251881Speter             mixed-revision situation.  */
676251881Speter          if (!is_copied_child)
677251881Speter            {
678251881Speter              const char *parent_abspath;
679251881Speter              svn_error_t *err;
680251881Speter              const char *parent_repos_relpath;
681251881Speter              const char *parent_root_url;
682251881Speter
683251881Speter              /* When we insert entries into the database, we will
684251881Speter                 construct additional copyfrom records for mixed-revision
685251881Speter                 copies. The old entries would simply record the different
686251881Speter                 revision in the entry->revision field. That is not
687251881Speter                 available within wc-ng, so additional copies are made
688251881Speter                 (see the logic inside write_entry()). However, when
689251881Speter                 reading these back *out* of the database, the additional
690251881Speter                 copies look like new "Added" nodes rather than a simple
691251881Speter                 mixed-rev working copy.
692251881Speter
693251881Speter                 That would be a behavior change if we did not compensate.
694251881Speter                 If there is copyfrom information for this node, then the
695251881Speter                 code below looks at the parent to detect if it *also* has
696251881Speter                 copyfrom information, and if the copyfrom_url would align
697251881Speter                 properly. If it *does*, then we omit storing copyfrom_url
698251881Speter                 and copyfrom_rev (ie. inherit the copyfrom info like a
699251881Speter                 normal child), and update entry->revision with the
700251881Speter                 copyfrom_rev in order to (re)create the mixed-rev copied
701251881Speter                 subtree that was originally presented for storage.  */
702251881Speter
703251881Speter              /* Get the copyfrom information from our parent.
704251881Speter
705251881Speter                 Note that the parent could be added/copied/moved-here.
706251881Speter                 There is no way for it to be deleted/moved-away and
707251881Speter                 have *this* node appear as copied.  */
708251881Speter              parent_abspath = svn_dirent_dirname(entry_abspath,
709251881Speter                                                  scratch_pool);
710251881Speter              err = svn_wc__db_scan_addition(NULL,
711251881Speter                                             &op_root_abspath,
712251881Speter                                             NULL, NULL, NULL,
713251881Speter                                             &parent_repos_relpath,
714251881Speter                                             &parent_root_url,
715251881Speter                                             NULL, NULL,
716251881Speter                                             db, parent_abspath,
717251881Speter                                             scratch_pool,
718251881Speter                                             scratch_pool);
719251881Speter              if (err)
720251881Speter                {
721251881Speter                  if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
722251881Speter                    return svn_error_trace(err);
723251881Speter                  svn_error_clear(err);
724251881Speter                }
725251881Speter              else if (parent_root_url != NULL
726251881Speter                       && strcmp(original_root_url, parent_root_url) == 0)
727251881Speter                {
728251881Speter                  const char *relpath_to_entry = svn_dirent_is_child(
729251881Speter                    op_root_abspath, entry_abspath, NULL);
730251881Speter                  const char *entry_repos_relpath = svn_relpath_join(
731251881Speter                    parent_repos_relpath, relpath_to_entry, scratch_pool);
732251881Speter
733251881Speter                  /* The copyfrom repos roots matched.
734251881Speter
735251881Speter                     Now we look to see if the copyfrom path of the parent
736251881Speter                     would align with our own path. If so, then it means
737251881Speter                     this copyfrom was spontaneously created and inserted
738251881Speter                     for mixed-rev purposes and can be eliminated without
739251881Speter                     changing the semantics of a mixed-rev copied subtree.
740251881Speter
741251881Speter                     See notes/api-errata/wc003.txt for some additional
742251881Speter                     detail, and potential issues.  */
743251881Speter                  if (strcmp(entry_repos_relpath,
744251881Speter                             original_repos_relpath) == 0)
745251881Speter                    {
746251881Speter                      is_copied_child = TRUE;
747251881Speter                      is_mixed_rev = TRUE;
748251881Speter                    }
749251881Speter                }
750251881Speter            }
751251881Speter
752251881Speter          if (is_copied_child)
753251881Speter            {
754251881Speter              /* We won't be settig the  copyfrom_url, yet need to
755251881Speter                 clear out the copyfrom_rev. Thus, this node becomes a
756251881Speter                 child of a copied subtree (rather than its own root).  */
757251881Speter              entry->copyfrom_rev = SVN_INVALID_REVNUM;
758251881Speter
759251881Speter              /* Children in a copied subtree are schedule normal
760251881Speter                 since we don't plan to actually *do* anything with
761251881Speter                 them. Their operation is implied by ancestors.  */
762251881Speter              entry->schedule = svn_wc_schedule_normal;
763251881Speter
764251881Speter              /* And *finally* we turn this entry into the mixed
765251881Speter                 revision node that it was intended to be. This
766251881Speter                 node's revision is taken from the copyfrom record
767251881Speter                 that we spontaneously constructed.  */
768251881Speter              if (is_mixed_rev)
769251881Speter                entry->revision = original_revision;
770251881Speter            }
771251881Speter          else if (original_repos_relpath != NULL)
772251881Speter            {
773251881Speter              entry->copyfrom_url =
774251881Speter                svn_path_url_add_component2(original_root_url,
775251881Speter                                            original_repos_relpath,
776251881Speter                                            result_pool);
777251881Speter            }
778251881Speter          else
779251881Speter            {
780251881Speter              /* NOTE: if original_repos_relpath == NULL, then the
781251881Speter                 second call to scan_addition() will not have occurred.
782251881Speter                 Thus, this use of OP_ROOT_ABSPATH still contains the
783251881Speter                 original value where we fetched a value for
784251881Speter                 SCANNED_REPOS_RELPATH.  */
785251881Speter              const char *relpath_to_entry = svn_dirent_is_child(
786251881Speter                op_root_abspath, entry_abspath, NULL);
787251881Speter              const char *entry_repos_relpath = svn_relpath_join(
788251881Speter                scanned_original_relpath, relpath_to_entry, scratch_pool);
789251881Speter
790251881Speter              entry->copyfrom_url =
791251881Speter                svn_path_url_add_component2(original_root_url,
792251881Speter                                            entry_repos_relpath,
793251881Speter                                            result_pool);
794251881Speter            }
795251881Speter        }
796251881Speter    }
797251881Speter  else if (status == svn_wc__db_status_not_present)
798251881Speter    {
799251881Speter      /* ### buh. 'deleted' nodes are actually supposed to be
800251881Speter         ### schedule "normal" since we aren't going to actually *do*
801251881Speter         ### anything to this node at commit time.  */
802251881Speter      entry->schedule = svn_wc_schedule_normal;
803251881Speter      entry->deleted = TRUE;
804251881Speter    }
805251881Speter  else if (status == svn_wc__db_status_server_excluded)
806251881Speter    {
807251881Speter      entry->absent = TRUE;
808251881Speter    }
809251881Speter  else if (status == svn_wc__db_status_excluded)
810251881Speter    {
811251881Speter      entry->schedule = svn_wc_schedule_normal;
812251881Speter      entry->depth = svn_depth_exclude;
813251881Speter    }
814251881Speter  else
815251881Speter    {
816251881Speter      /* ### we should have handled all possible status values.  */
817251881Speter      SVN_ERR_MALFUNCTION();
818251881Speter    }
819251881Speter
820251881Speter  /* ### higher levels want repos information about deleted nodes, even
821251881Speter     ### tho they are not "part of" a repository any more.  */
822251881Speter  if (entry->schedule == svn_wc_schedule_delete)
823251881Speter    {
824251881Speter      SVN_ERR(get_info_for_deleted(entry,
825251881Speter                                   &kind,
826251881Speter                                   &repos_relpath,
827251881Speter                                   &checksum,
828251881Speter                                   &lock,
829251881Speter                                   db, entry_abspath,
830251881Speter                                   parent_entry,
831251881Speter                                   have_base, have_more_work,
832251881Speter                                   result_pool, scratch_pool));
833251881Speter    }
834251881Speter
835251881Speter  /* ### default to the infinite depth if we don't know it. */
836251881Speter  if (entry->depth == svn_depth_unknown)
837251881Speter    entry->depth = svn_depth_infinity;
838251881Speter
839251881Speter  if (kind == svn_node_dir)
840251881Speter    entry->kind = svn_node_dir;
841251881Speter  else if (kind == svn_node_file)
842251881Speter    entry->kind = svn_node_file;
843251881Speter  else if (kind == svn_node_symlink)
844251881Speter    entry->kind = svn_node_file;  /* ### no symlink kind */
845251881Speter  else
846251881Speter    entry->kind = svn_node_unknown;
847251881Speter
848251881Speter  /* We should always have a REPOS_RELPATH, except for:
849251881Speter     - deleted nodes
850251881Speter     - certain obstructed nodes
851251881Speter     - not-present nodes
852251881Speter     - absent nodes
853251881Speter     - excluded nodes
854251881Speter
855251881Speter     ### the last three should probably have an "implied" REPOS_RELPATH
856251881Speter  */
857251881Speter  SVN_ERR_ASSERT(repos_relpath != NULL
858251881Speter                 || entry->schedule == svn_wc_schedule_delete
859251881Speter                 || status == svn_wc__db_status_not_present
860251881Speter                 || status == svn_wc__db_status_server_excluded
861251881Speter                 || status == svn_wc__db_status_excluded);
862251881Speter  if (repos_relpath)
863251881Speter    entry->url = svn_path_url_add_component2(entry->repos,
864251881Speter                                             repos_relpath,
865251881Speter                                             result_pool);
866251881Speter
867251881Speter  if (checksum)
868251881Speter    {
869251881Speter      /* We got a SHA-1, get the corresponding MD-5. */
870251881Speter      if (checksum->kind != svn_checksum_md5)
871251881Speter        SVN_ERR(svn_wc__db_pristine_get_md5(&checksum, db,
872251881Speter                                            entry_abspath, checksum,
873251881Speter                                            scratch_pool, scratch_pool));
874251881Speter
875251881Speter      SVN_ERR_ASSERT(checksum->kind == svn_checksum_md5);
876251881Speter      entry->checksum = svn_checksum_to_cstring(checksum, result_pool);
877251881Speter    }
878251881Speter
879251881Speter  if (conflicted)
880251881Speter    {
881251881Speter      svn_skel_t *conflict;
882251881Speter      svn_boolean_t text_conflicted;
883251881Speter      svn_boolean_t prop_conflicted;
884251881Speter      SVN_ERR(svn_wc__db_read_conflict(&conflict, db, entry_abspath,
885251881Speter                                       scratch_pool, scratch_pool));
886251881Speter
887251881Speter      SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, &text_conflicted,
888251881Speter                                         &prop_conflicted, NULL,
889251881Speter                                         db, dir_abspath, conflict,
890251881Speter                                         scratch_pool, scratch_pool));
891251881Speter
892251881Speter      if (text_conflicted)
893251881Speter        {
894251881Speter          const char *my_abspath;
895251881Speter          const char *their_old_abspath;
896251881Speter          const char *their_abspath;
897251881Speter          SVN_ERR(svn_wc__conflict_read_text_conflict(&my_abspath,
898251881Speter                                                      &their_old_abspath,
899251881Speter                                                      &their_abspath,
900251881Speter                                                      db, dir_abspath,
901251881Speter                                                      conflict, scratch_pool,
902251881Speter                                                      scratch_pool));
903251881Speter
904251881Speter          if (my_abspath)
905251881Speter            entry->conflict_wrk = svn_dirent_basename(my_abspath, result_pool);
906251881Speter
907251881Speter          if (their_old_abspath)
908251881Speter            entry->conflict_old = svn_dirent_basename(their_old_abspath,
909251881Speter                                                      result_pool);
910251881Speter
911251881Speter          if (their_abspath)
912251881Speter            entry->conflict_new = svn_dirent_basename(their_abspath,
913251881Speter                                                      result_pool);
914251881Speter        }
915251881Speter
916251881Speter      if (prop_conflicted)
917251881Speter        {
918251881Speter          const char *prej_abspath;
919251881Speter
920251881Speter          SVN_ERR(svn_wc__conflict_read_prop_conflict(&prej_abspath, NULL,
921251881Speter                                                      NULL, NULL, NULL,
922251881Speter                                                      db, dir_abspath,
923251881Speter                                                      conflict, scratch_pool,
924251881Speter                                                      scratch_pool));
925251881Speter
926251881Speter          if (prej_abspath)
927251881Speter            entry->prejfile = svn_dirent_basename(prej_abspath, result_pool);
928251881Speter        }
929251881Speter    }
930251881Speter
931251881Speter  if (lock)
932251881Speter    {
933251881Speter      entry->lock_token = lock->token;
934251881Speter      entry->lock_owner = lock->owner;
935251881Speter      entry->lock_comment = lock->comment;
936251881Speter      entry->lock_creation_date = lock->date;
937251881Speter    }
938251881Speter
939251881Speter  /* Let's check for a file external.  ugh.  */
940251881Speter  if (status == svn_wc__db_status_normal
941251881Speter      && kind == svn_node_file)
942251881Speter    SVN_ERR(check_file_external(entry, db, entry_abspath, dir_abspath,
943251881Speter                                result_pool, scratch_pool));
944251881Speter
945251881Speter  entry->working_size = translated_size;
946251881Speter
947251881Speter  *new_entry = entry;
948251881Speter
949251881Speter  return SVN_NO_ERROR;
950251881Speter}
951251881Speter
952251881Speter/* Read entries for PATH/LOCAL_ABSPATH from DB. The entries
953251881Speter   will be allocated in RESULT_POOL, with temporary allocations in
954251881Speter   SCRATCH_POOL. The entries are returned in RESULT_ENTRIES.  */
955251881Speterstatic svn_error_t *
956251881Speterread_entries_new(apr_hash_t **result_entries,
957251881Speter                 svn_wc__db_t *db,
958251881Speter                 const char *local_abspath,
959251881Speter                 apr_pool_t *result_pool,
960251881Speter                 apr_pool_t *scratch_pool)
961251881Speter{
962251881Speter  apr_hash_t *entries;
963251881Speter  const apr_array_header_t *children;
964251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
965251881Speter  int i;
966251881Speter  const svn_wc_entry_t *parent_entry;
967251881Speter  apr_int64_t wc_id = 1;  /* ### hacky. should remove.  */
968251881Speter
969251881Speter  entries = apr_hash_make(result_pool);
970251881Speter
971251881Speter  SVN_ERR(read_one_entry(&parent_entry, db, wc_id, local_abspath,
972251881Speter                         "" /* name */,
973251881Speter                         NULL /* parent_entry */,
974251881Speter                         result_pool, iterpool));
975251881Speter  svn_hash_sets(entries, "", parent_entry);
976251881Speter
977251881Speter  /* Use result_pool so that the child names (used by reference, rather
978251881Speter     than copied) appear in result_pool.  */
979251881Speter  SVN_ERR(svn_wc__db_read_children(&children, db,
980251881Speter                                   local_abspath,
981251881Speter                                   result_pool, iterpool));
982251881Speter  for (i = children->nelts; i--; )
983251881Speter    {
984251881Speter      const char *name = APR_ARRAY_IDX(children, i, const char *);
985251881Speter      const svn_wc_entry_t *entry;
986251881Speter
987251881Speter      svn_pool_clear(iterpool);
988251881Speter
989251881Speter      SVN_ERR(read_one_entry(&entry,
990251881Speter                             db, wc_id, local_abspath, name, parent_entry,
991251881Speter                             result_pool, iterpool));
992251881Speter      svn_hash_sets(entries, entry->name, entry);
993251881Speter    }
994251881Speter
995251881Speter  svn_pool_destroy(iterpool);
996251881Speter
997251881Speter  *result_entries = entries;
998251881Speter
999251881Speter  return SVN_NO_ERROR;
1000251881Speter}
1001251881Speter
1002251881Speter
1003251881Speter/* Read a pair of entries from wc_db in the directory DIR_ABSPATH. Return
1004251881Speter   the directory's entry in *PARENT_ENTRY and NAME's entry in *ENTRY. The
1005251881Speter   two returned pointers will be the same if NAME=="" ("this dir").
1006251881Speter
1007251881Speter   The parent entry must exist.
1008251881Speter
1009251881Speter   The requested entry MAY exist. If it does not, then NULL will be returned.
1010251881Speter
1011251881Speter   The resulting entries are allocated in RESULT_POOL, and all temporary
1012251881Speter   allocations are made in SCRATCH_POOL.  */
1013251881Speterstatic svn_error_t *
1014251881Speterread_entry_pair(const svn_wc_entry_t **parent_entry,
1015251881Speter                const svn_wc_entry_t **entry,
1016251881Speter                svn_wc__db_t *db,
1017251881Speter                const char *dir_abspath,
1018251881Speter                const char *name,
1019251881Speter                apr_pool_t *result_pool,
1020251881Speter                apr_pool_t *scratch_pool)
1021251881Speter{
1022251881Speter  apr_int64_t wc_id = 1;  /* ### hacky. should remove.  */
1023251881Speter
1024251881Speter  SVN_ERR(read_one_entry(parent_entry, db, wc_id, dir_abspath,
1025251881Speter                         "" /* name */,
1026251881Speter                         NULL /* parent_entry */,
1027251881Speter                         result_pool, scratch_pool));
1028251881Speter
1029251881Speter  /* If we need the entry for "this dir", then return the parent_entry
1030251881Speter     in both outputs. Otherwise, read the child node.  */
1031251881Speter  if (*name == '\0')
1032251881Speter    {
1033251881Speter      /* If the retrieved node is a FILE, then we have a problem. We asked
1034251881Speter         for a directory. This implies there is an obstructing, unversioned
1035251881Speter         directory where a FILE should be. We navigated from the obstructing
1036251881Speter         subdir up to the parent dir, then returned the FILE found there.
1037251881Speter
1038251881Speter         Let's return WC_MISSING cuz the caller thought we had a dir, but
1039251881Speter         that (versioned subdir) isn't there.  */
1040251881Speter      if ((*parent_entry)->kind == svn_node_file)
1041251881Speter        {
1042251881Speter          *parent_entry = NULL;
1043251881Speter          return svn_error_createf(SVN_ERR_WC_MISSING, NULL,
1044251881Speter                                 _("'%s' is not a versioned working copy"),
1045251881Speter                                 svn_dirent_local_style(dir_abspath,
1046251881Speter                                                        scratch_pool));
1047251881Speter        }
1048251881Speter
1049251881Speter      *entry = *parent_entry;
1050251881Speter    }
1051251881Speter  else
1052251881Speter    {
1053251881Speter      const apr_array_header_t *children;
1054251881Speter      int i;
1055251881Speter
1056251881Speter      /* Default to not finding the child.  */
1057251881Speter      *entry = NULL;
1058251881Speter
1059251881Speter      /* Determine whether the parent KNOWS about this child. If it does
1060251881Speter         not, then we should not attempt to look for it.
1061251881Speter
1062251881Speter         For example: the parent doesn't "know" about the child, but the
1063251881Speter         versioned directory *does* exist on disk. We don't want to look
1064251881Speter         into that subdir.  */
1065251881Speter      SVN_ERR(svn_wc__db_read_children(&children, db, dir_abspath,
1066251881Speter                                       scratch_pool, scratch_pool));
1067251881Speter      for (i = children->nelts; i--; )
1068251881Speter        {
1069251881Speter          const char *child = APR_ARRAY_IDX(children, i, const char *);
1070251881Speter
1071251881Speter          if (strcmp(child, name) == 0)
1072251881Speter            {
1073251881Speter              svn_error_t *err;
1074251881Speter
1075251881Speter              err = read_one_entry(entry,
1076251881Speter                                   db, wc_id, dir_abspath, name, *parent_entry,
1077251881Speter                                   result_pool, scratch_pool);
1078251881Speter              if (err)
1079251881Speter                {
1080251881Speter                  if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1081251881Speter                    return svn_error_trace(err);
1082251881Speter
1083251881Speter                  /* No problem. Clear the error and leave the default value
1084251881Speter                     of "missing".  */
1085251881Speter                  svn_error_clear(err);
1086251881Speter                }
1087251881Speter
1088251881Speter              /* Found it. No need to keep searching.  */
1089251881Speter              break;
1090251881Speter            }
1091251881Speter        }
1092251881Speter      /* if the loop ends without finding a child, then we have the default
1093251881Speter         ENTRY value of NULL.  */
1094251881Speter    }
1095251881Speter
1096251881Speter  return SVN_NO_ERROR;
1097251881Speter}
1098251881Speter
1099251881Speter
1100251881Speter/* */
1101251881Speterstatic svn_error_t *
1102251881Speterread_entries(apr_hash_t **entries,
1103251881Speter             svn_wc__db_t *db,
1104251881Speter             const char *wcroot_abspath,
1105251881Speter             apr_pool_t *result_pool,
1106251881Speter             apr_pool_t *scratch_pool)
1107251881Speter{
1108251881Speter  int wc_format;
1109251881Speter
1110251881Speter  SVN_ERR(svn_wc__db_temp_get_format(&wc_format, db, wcroot_abspath,
1111251881Speter                                     scratch_pool));
1112251881Speter
1113251881Speter  if (wc_format < SVN_WC__WC_NG_VERSION)
1114251881Speter    return svn_error_trace(svn_wc__read_entries_old(entries,
1115251881Speter                                                    wcroot_abspath,
1116251881Speter                                                    result_pool,
1117251881Speter                                                    scratch_pool));
1118251881Speter
1119251881Speter  return svn_error_trace(read_entries_new(entries, db, wcroot_abspath,
1120251881Speter                                          result_pool, scratch_pool));
1121251881Speter}
1122251881Speter
1123251881Speter
1124251881Speter/* For a given LOCAL_ABSPATH, using DB, set *ADM_ABSPATH to the directory in
1125251881Speter   which the entry information is located, and *ENTRY_NAME to the entry name
1126251881Speter   to access that entry.
1127251881Speter
1128251881Speter   KIND is as in svn_wc__get_entry().
1129251881Speter
1130251881Speter   Return the results in RESULT_POOL and use SCRATCH_POOL for temporary
1131251881Speter   allocations. */
1132251881Speterstatic svn_error_t *
1133251881Speterget_entry_access_info(const char **adm_abspath,
1134251881Speter                      const char **entry_name,
1135251881Speter                      svn_wc__db_t *db,
1136251881Speter                      const char *local_abspath,
1137251881Speter                      svn_node_kind_t kind,
1138251881Speter                      apr_pool_t *result_pool,
1139251881Speter                      apr_pool_t *scratch_pool)
1140251881Speter{
1141251881Speter  svn_wc_adm_access_t *adm_access;
1142251881Speter  svn_boolean_t read_from_subdir = FALSE;
1143251881Speter
1144251881Speter  /* If the caller didn't know the node kind, then stat the path. Maybe
1145251881Speter     it is really there, and we can speed up the steps below.  */
1146251881Speter  if (kind == svn_node_unknown)
1147251881Speter    {
1148251881Speter      svn_node_kind_t on_disk;
1149251881Speter
1150251881Speter      /* Do we already have an access baton for LOCAL_ABSPATH?  */
1151251881Speter      adm_access = svn_wc__adm_retrieve_internal2(db, local_abspath,
1152251881Speter                                                  scratch_pool);
1153251881Speter      if (adm_access)
1154251881Speter        {
1155251881Speter          /* Sweet. The node is a directory.  */
1156251881Speter          on_disk = svn_node_dir;
1157251881Speter        }
1158251881Speter      else
1159251881Speter        {
1160251881Speter          svn_boolean_t special;
1161251881Speter
1162251881Speter          /* What's on disk?  */
1163251881Speter          SVN_ERR(svn_io_check_special_path(local_abspath, &on_disk, &special,
1164251881Speter                                            scratch_pool));
1165251881Speter        }
1166251881Speter
1167251881Speter      if (on_disk != svn_node_dir)
1168251881Speter        {
1169251881Speter          /* If this is *anything* besides a directory (FILE, NONE, or
1170251881Speter             UNKNOWN), then we cannot treat it as a versioned directory
1171251881Speter             containing entries to read. Leave READ_FROM_SUBDIR as FALSE,
1172251881Speter             so that the parent will be examined.
1173251881Speter
1174251881Speter             For NONE and UNKNOWN, it may be that metadata exists for the
1175251881Speter             node, even though on-disk is unhelpful.
1176251881Speter
1177251881Speter             If NEED_PARENT_STUB is TRUE, and the entry is not a DIRECTORY,
1178251881Speter             then we'll error.
1179251881Speter
1180251881Speter             If NEED_PARENT_STUB if FALSE, and we successfully read a stub,
1181251881Speter             then this on-disk node is obstructing the read.  */
1182251881Speter        }
1183251881Speter      else
1184251881Speter        {
1185251881Speter          /* We found a directory for this UNKNOWN node. Determine whether
1186251881Speter             we need to read inside it.  */
1187251881Speter          read_from_subdir = TRUE;
1188251881Speter        }
1189251881Speter    }
1190251881Speter  else if (kind == svn_node_dir)
1191251881Speter    {
1192251881Speter      read_from_subdir = TRUE;
1193251881Speter    }
1194251881Speter
1195251881Speter  if (read_from_subdir)
1196251881Speter    {
1197251881Speter      /* KIND must be a DIR or UNKNOWN (and we found a subdir). We want
1198251881Speter         the "real" data, so treat LOCAL_ABSPATH as a versioned directory.  */
1199251881Speter      *adm_abspath = apr_pstrdup(result_pool, local_abspath);
1200251881Speter      *entry_name = "";
1201251881Speter    }
1202251881Speter  else
1203251881Speter    {
1204251881Speter      /* FILE node needs to read the parent directory. Or a DIR node
1205251881Speter         needs to read from the parent to get at the stub entry. Or this
1206251881Speter         is an UNKNOWN node, and we need to examine the parent.  */
1207251881Speter      svn_dirent_split(adm_abspath, entry_name, local_abspath, result_pool);
1208251881Speter    }
1209251881Speter
1210251881Speter  return SVN_NO_ERROR;
1211251881Speter}
1212251881Speter
1213251881Speter
1214251881Spetersvn_error_t *
1215251881Spetersvn_wc__get_entry(const svn_wc_entry_t **entry,
1216251881Speter                  svn_wc__db_t *db,
1217251881Speter                  const char *local_abspath,
1218251881Speter                  svn_boolean_t allow_unversioned,
1219251881Speter                  svn_node_kind_t kind,
1220251881Speter                  apr_pool_t *result_pool,
1221251881Speter                  apr_pool_t *scratch_pool)
1222251881Speter{
1223251881Speter  const char *dir_abspath;
1224251881Speter  const char *entry_name;
1225251881Speter
1226251881Speter  SVN_ERR(get_entry_access_info(&dir_abspath, &entry_name, db, local_abspath,
1227251881Speter                                kind, scratch_pool, scratch_pool));
1228251881Speter
1229251881Speter    {
1230251881Speter      const svn_wc_entry_t *parent_entry;
1231251881Speter      svn_error_t *err;
1232251881Speter
1233251881Speter      /* NOTE: if KIND is UNKNOWN and we decided to examine the *parent*
1234251881Speter         directory, then it is possible we moved out of the working copy.
1235251881Speter         If the on-disk node is a DIR, and we asked for a stub, then we
1236251881Speter         obviously can't provide that (parent has no info). If the on-disk
1237251881Speter         node is a FILE/NONE/UNKNOWN, then it is obstructing the real
1238251881Speter         LOCAL_ABSPATH (or it was never a versioned item). In all these
1239251881Speter         cases, the read_entries() will (properly) throw an error.
1240251881Speter
1241251881Speter         NOTE: if KIND is a DIR and we asked for the real data, but it is
1242251881Speter         obstructed on-disk by some other node kind (NONE, FILE, UNKNOWN),
1243251881Speter         then this will throw an error.  */
1244251881Speter
1245251881Speter      err = read_entry_pair(&parent_entry, entry,
1246251881Speter                            db, dir_abspath, entry_name,
1247251881Speter                            result_pool, scratch_pool);
1248251881Speter      if (err)
1249251881Speter        {
1250251881Speter          if (err->apr_err != SVN_ERR_WC_MISSING || kind != svn_node_unknown
1251251881Speter              || *entry_name != '\0')
1252251881Speter            return svn_error_trace(err);
1253251881Speter          svn_error_clear(err);
1254251881Speter
1255251881Speter          /* The caller didn't know the node type, we saw a directory there,
1256251881Speter             we attempted to read IN that directory, and then wc_db reports
1257251881Speter             that it is NOT a working copy directory. It is possible that
1258251881Speter             one of two things has happened:
1259251881Speter
1260251881Speter             1) a directory is obstructing a file in the parent
1261251881Speter             2) the (versioned) directory's contents have been removed
1262251881Speter
1263251881Speter             Let's assume situation (1); if that is true, then we can just
1264251881Speter             return the newly-found data.
1265251881Speter
1266251881Speter             If we assumed (2), then a valid result still won't help us
1267251881Speter             since the caller asked for the actual contents, not the stub
1268251881Speter             (which is why we read *into* the directory). However, if we
1269251881Speter             assume (1) and get back a stub, then we have verified a
1270251881Speter             missing, versioned directory, and can return an error
1271251881Speter             describing that.
1272251881Speter
1273251881Speter             Redo the fetch, but "insist" we are trying to find a file.
1274251881Speter             This will read from the parent directory of the "file".  */
1275251881Speter          err = svn_wc__get_entry(entry, db, local_abspath, allow_unversioned,
1276251881Speter                                  svn_node_file, result_pool, scratch_pool);
1277251881Speter          if (err == SVN_NO_ERROR)
1278251881Speter            return SVN_NO_ERROR;
1279251881Speter          if (err->apr_err != SVN_ERR_NODE_UNEXPECTED_KIND)
1280251881Speter            return svn_error_trace(err);
1281251881Speter          svn_error_clear(err);
1282251881Speter
1283251881Speter          /* We asked for a FILE, but the node found is a DIR. Thus, we
1284251881Speter             are looking at a stub. Originally, we tried to read into the
1285251881Speter             subdir because NEED_PARENT_STUB is FALSE. The stub we just
1286251881Speter             read is not going to work for the caller, so inform them of
1287251881Speter             the missing subdirectory.  */
1288251881Speter          SVN_ERR_ASSERT(*entry != NULL && (*entry)->kind == svn_node_dir);
1289251881Speter          return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
1290251881Speter                                 _("Admin area of '%s' is missing"),
1291251881Speter                                 svn_dirent_local_style(local_abspath,
1292251881Speter                                                        scratch_pool));
1293251881Speter        }
1294251881Speter    }
1295251881Speter
1296251881Speter  if (*entry == NULL)
1297251881Speter    {
1298251881Speter      if (allow_unversioned)
1299251881Speter        return SVN_NO_ERROR;
1300251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
1301251881Speter                               _("'%s' is not under version control"),
1302251881Speter                               svn_dirent_local_style(local_abspath,
1303251881Speter                                                      scratch_pool));
1304251881Speter    }
1305251881Speter
1306251881Speter  /* The caller had the wrong information.  */
1307251881Speter  if ((kind == svn_node_file && (*entry)->kind != svn_node_file)
1308251881Speter      || (kind == svn_node_dir && (*entry)->kind != svn_node_dir))
1309251881Speter    return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
1310251881Speter                             _("'%s' is not of the right kind"),
1311251881Speter                             svn_dirent_local_style(local_abspath,
1312251881Speter                                                    scratch_pool));
1313251881Speter
1314251881Speter  return SVN_NO_ERROR;
1315251881Speter}
1316251881Speter
1317251881Speter/* TODO ### Rewrite doc string to mention ENTRIES_ALL; not ADM_ACCESS.
1318251881Speter
1319251881Speter   Prune the deleted entries from the cached entries in ADM_ACCESS, and
1320251881Speter   return that collection in *ENTRIES_PRUNED.  SCRATCH_POOL is used for local,
1321251881Speter   short term, memory allocation, RESULT_POOL for permanent stuff.  */
1322251881Speterstatic svn_error_t *
1323251881Speterprune_deleted(apr_hash_t **entries_pruned,
1324251881Speter              apr_hash_t *entries_all,
1325251881Speter              apr_pool_t *result_pool,
1326251881Speter              apr_pool_t *scratch_pool)
1327251881Speter{
1328251881Speter  apr_hash_index_t *hi;
1329251881Speter
1330251881Speter  if (!entries_all)
1331251881Speter    {
1332251881Speter      *entries_pruned = NULL;
1333251881Speter      return SVN_NO_ERROR;
1334251881Speter    }
1335251881Speter
1336251881Speter  /* I think it will be common for there to be no deleted entries, so
1337251881Speter     it is worth checking for that case as we can optimise it. */
1338251881Speter  for (hi = apr_hash_first(scratch_pool, entries_all);
1339251881Speter       hi;
1340251881Speter       hi = apr_hash_next(hi))
1341251881Speter    {
1342251881Speter      svn_boolean_t hidden;
1343251881Speter
1344251881Speter      SVN_ERR(svn_wc__entry_is_hidden(&hidden,
1345251881Speter                                      svn__apr_hash_index_val(hi)));
1346251881Speter      if (hidden)
1347251881Speter        break;
1348251881Speter    }
1349251881Speter
1350251881Speter  if (! hi)
1351251881Speter    {
1352251881Speter      /* There are no deleted entries, so we can use the full hash */
1353251881Speter      *entries_pruned = entries_all;
1354251881Speter      return SVN_NO_ERROR;
1355251881Speter    }
1356251881Speter
1357251881Speter  /* Construct pruned hash without deleted entries */
1358251881Speter  *entries_pruned = apr_hash_make(result_pool);
1359251881Speter  for (hi = apr_hash_first(scratch_pool, entries_all);
1360251881Speter       hi;
1361251881Speter       hi = apr_hash_next(hi))
1362251881Speter    {
1363251881Speter      const void *key = svn__apr_hash_index_key(hi);
1364251881Speter      const svn_wc_entry_t *entry = svn__apr_hash_index_val(hi);
1365251881Speter      svn_boolean_t hidden;
1366251881Speter
1367251881Speter      SVN_ERR(svn_wc__entry_is_hidden(&hidden, entry));
1368251881Speter      if (!hidden)
1369251881Speter        svn_hash_sets(*entries_pruned, key, entry);
1370251881Speter    }
1371251881Speter
1372251881Speter  return SVN_NO_ERROR;
1373251881Speter}
1374251881Speter
1375251881Speterstruct entries_read_baton_t
1376251881Speter{
1377251881Speter  apr_hash_t **new_entries;
1378251881Speter  svn_wc__db_t *db;
1379251881Speter  const char *local_abspath;
1380251881Speter  apr_pool_t *result_pool;
1381251881Speter};
1382251881Speter
1383251881Speterstatic svn_error_t *
1384251881Speterentries_read_txn(void *baton, svn_sqlite__db_t *db, apr_pool_t *scratch_pool)
1385251881Speter{
1386251881Speter  struct entries_read_baton_t *erb = baton;
1387251881Speter
1388251881Speter  SVN_ERR(read_entries(erb->new_entries, erb->db, erb->local_abspath,
1389251881Speter                       erb->result_pool, scratch_pool));
1390251881Speter
1391251881Speter  return NULL;
1392251881Speter}
1393251881Speter
1394251881Spetersvn_error_t *
1395251881Spetersvn_wc__entries_read_internal(apr_hash_t **entries,
1396251881Speter                              svn_wc_adm_access_t *adm_access,
1397251881Speter                              svn_boolean_t show_hidden,
1398251881Speter                              apr_pool_t *pool)
1399251881Speter{
1400251881Speter  apr_hash_t *new_entries;
1401251881Speter
1402251881Speter  new_entries = svn_wc__adm_access_entries(adm_access);
1403251881Speter  if (! new_entries)
1404251881Speter    {
1405251881Speter      svn_wc__db_t *db = svn_wc__adm_get_db(adm_access);
1406251881Speter      const char *local_abspath = svn_wc__adm_access_abspath(adm_access);
1407251881Speter      apr_pool_t *result_pool = svn_wc__adm_access_pool_internal(adm_access);
1408251881Speter      svn_sqlite__db_t *sdb;
1409251881Speter      struct entries_read_baton_t erb;
1410251881Speter
1411251881Speter      /* ### Use the borrow DB api to handle all calls in a single read
1412251881Speter         ### transaction. This api is used extensively in our test suite
1413251881Speter         ### via the entries-read application. */
1414251881Speter
1415251881Speter      SVN_ERR(svn_wc__db_temp_borrow_sdb(&sdb, db, local_abspath, pool));
1416251881Speter
1417251881Speter      erb.db = db;
1418251881Speter      erb.local_abspath = local_abspath;
1419251881Speter      erb.new_entries = &new_entries;
1420251881Speter      erb.result_pool = result_pool;
1421251881Speter
1422251881Speter      SVN_ERR(svn_sqlite__with_lock(sdb, entries_read_txn, &erb, pool));
1423251881Speter
1424251881Speter      svn_wc__adm_access_set_entries(adm_access, new_entries);
1425251881Speter    }
1426251881Speter
1427251881Speter  if (show_hidden)
1428251881Speter    *entries = new_entries;
1429251881Speter  else
1430251881Speter    SVN_ERR(prune_deleted(entries, new_entries,
1431251881Speter                          svn_wc__adm_access_pool_internal(adm_access),
1432251881Speter                          pool));
1433251881Speter
1434251881Speter  return SVN_NO_ERROR;
1435251881Speter}
1436251881Speter
1437251881Spetersvn_error_t *
1438251881Spetersvn_wc_entries_read(apr_hash_t **entries,
1439251881Speter                    svn_wc_adm_access_t *adm_access,
1440251881Speter                    svn_boolean_t show_hidden,
1441251881Speter                    apr_pool_t *pool)
1442251881Speter{
1443251881Speter  return svn_error_trace(svn_wc__entries_read_internal(entries, adm_access,
1444251881Speter                                                       show_hidden, pool));
1445251881Speter}
1446251881Speter
1447251881Speter/* No transaction required: called from write_entry which is itself
1448251881Speter   transaction-wrapped. */
1449251881Speterstatic svn_error_t *
1450251881Speterinsert_node(svn_sqlite__db_t *sdb,
1451251881Speter            const db_node_t *node,
1452251881Speter            apr_pool_t *scratch_pool)
1453251881Speter{
1454251881Speter  svn_sqlite__stmt_t *stmt;
1455251881Speter
1456251881Speter  SVN_ERR_ASSERT(node->op_depth > 0 || node->repos_relpath);
1457251881Speter
1458251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE));
1459251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnnnsnrisnnni",
1460251881Speter                            node->wc_id,
1461251881Speter                            node->local_relpath,
1462251881Speter                            node->op_depth,
1463251881Speter                            node->parent_relpath,
1464251881Speter                            /* Setting depth for files? */
1465251881Speter                            svn_depth_to_word(node->depth),
1466251881Speter                            node->changed_rev,
1467251881Speter                            node->changed_date,
1468251881Speter                            node->changed_author,
1469251881Speter                            node->recorded_time));
1470251881Speter
1471251881Speter  if (node->repos_relpath)
1472251881Speter    {
1473251881Speter      SVN_ERR(svn_sqlite__bind_int64(stmt, 5,
1474251881Speter                                     node->repos_id));
1475251881Speter      SVN_ERR(svn_sqlite__bind_text(stmt, 6,
1476251881Speter                                    node->repos_relpath));
1477251881Speter      SVN_ERR(svn_sqlite__bind_revnum(stmt, 7, node->revision));
1478251881Speter    }
1479251881Speter
1480251881Speter  if (node->presence == svn_wc__db_status_normal)
1481251881Speter    SVN_ERR(svn_sqlite__bind_text(stmt, 8, "normal"));
1482251881Speter  else if (node->presence == svn_wc__db_status_not_present)
1483251881Speter    SVN_ERR(svn_sqlite__bind_text(stmt, 8, "not-present"));
1484251881Speter  else if (node->presence == svn_wc__db_status_base_deleted)
1485251881Speter    SVN_ERR(svn_sqlite__bind_text(stmt, 8, "base-deleted"));
1486251881Speter  else if (node->presence == svn_wc__db_status_incomplete)
1487251881Speter    SVN_ERR(svn_sqlite__bind_text(stmt, 8, "incomplete"));
1488251881Speter  else if (node->presence == svn_wc__db_status_excluded)
1489251881Speter    SVN_ERR(svn_sqlite__bind_text(stmt, 8, "excluded"));
1490251881Speter  else if (node->presence == svn_wc__db_status_server_excluded)
1491251881Speter    SVN_ERR(svn_sqlite__bind_text(stmt, 8, "server-excluded"));
1492251881Speter
1493251881Speter  if (node->kind == svn_node_none)
1494251881Speter    SVN_ERR(svn_sqlite__bind_text(stmt, 10, "unknown"));
1495251881Speter  else
1496251881Speter    SVN_ERR(svn_sqlite__bind_text(stmt, 10,
1497251881Speter                                  svn_node_kind_to_word(node->kind)));
1498251881Speter
1499251881Speter  if (node->kind == svn_node_file)
1500251881Speter    {
1501251881Speter      if (!node->checksum
1502251881Speter          && node->op_depth == 0
1503251881Speter          && node->presence != svn_wc__db_status_not_present
1504251881Speter          && node->presence != svn_wc__db_status_excluded
1505251881Speter          && node->presence != svn_wc__db_status_server_excluded)
1506251881Speter        return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
1507251881Speter                                 _("The file '%s' has no checksum"),
1508251881Speter                                 svn_dirent_local_style(node->local_relpath,
1509251881Speter                                                        scratch_pool));
1510251881Speter
1511251881Speter      SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, node->checksum,
1512251881Speter                                        scratch_pool));
1513251881Speter    }
1514251881Speter
1515251881Speter  if (node->properties) /* ### Never set, props done later */
1516251881Speter    SVN_ERR(svn_sqlite__bind_properties(stmt, 15, node->properties,
1517251881Speter                                        scratch_pool));
1518251881Speter
1519251881Speter  if (node->recorded_size != SVN_INVALID_FILESIZE)
1520251881Speter    SVN_ERR(svn_sqlite__bind_int64(stmt, 16, node->recorded_size));
1521251881Speter
1522251881Speter  if (node->file_external)
1523251881Speter    SVN_ERR(svn_sqlite__bind_int(stmt, 20, 1));
1524251881Speter
1525251881Speter  if (node->inherited_props)
1526251881Speter    SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, node->inherited_props,
1527251881Speter                                    scratch_pool));
1528251881Speter
1529251881Speter  SVN_ERR(svn_sqlite__insert(NULL, stmt));
1530251881Speter
1531251881Speter  return SVN_NO_ERROR;
1532251881Speter}
1533251881Speter
1534251881Speter
1535251881Speter/* */
1536251881Speterstatic svn_error_t *
1537251881Speterinsert_actual_node(svn_sqlite__db_t *sdb,
1538251881Speter                   svn_wc__db_t *db,
1539251881Speter                   const char *wri_abspath,
1540251881Speter                   const db_actual_node_t *actual_node,
1541251881Speter                   apr_pool_t *scratch_pool)
1542251881Speter{
1543251881Speter  svn_sqlite__stmt_t *stmt;
1544251881Speter  svn_skel_t *conflict_data = NULL;
1545251881Speter
1546251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_ACTUAL_NODE));
1547251881Speter
1548251881Speter  SVN_ERR(svn_sqlite__bind_int64(stmt, 1, actual_node->wc_id));
1549251881Speter  SVN_ERR(svn_sqlite__bind_text(stmt, 2, actual_node->local_relpath));
1550251881Speter  SVN_ERR(svn_sqlite__bind_text(stmt, 3, actual_node->parent_relpath));
1551251881Speter
1552251881Speter  if (actual_node->properties)
1553251881Speter    SVN_ERR(svn_sqlite__bind_properties(stmt, 4, actual_node->properties,
1554251881Speter                                        scratch_pool));
1555251881Speter
1556251881Speter  if (actual_node->changelist)
1557251881Speter    SVN_ERR(svn_sqlite__bind_text(stmt, 5, actual_node->changelist));
1558251881Speter
1559251881Speter  SVN_ERR(svn_wc__upgrade_conflict_skel_from_raw(
1560251881Speter                                &conflict_data,
1561251881Speter                                db, wri_abspath,
1562251881Speter                                actual_node->local_relpath,
1563251881Speter                                actual_node->conflict_old,
1564251881Speter                                actual_node->conflict_working,
1565251881Speter                                actual_node->conflict_new,
1566251881Speter                                actual_node->prop_reject,
1567251881Speter                                actual_node->tree_conflict_data,
1568251881Speter                                actual_node->tree_conflict_data
1569251881Speter                                    ? strlen(actual_node->tree_conflict_data)
1570251881Speter                                    : 0,
1571251881Speter                                scratch_pool, scratch_pool));
1572251881Speter
1573251881Speter  if (conflict_data)
1574251881Speter    {
1575251881Speter      svn_stringbuf_t *data = svn_skel__unparse(conflict_data, scratch_pool);
1576251881Speter
1577251881Speter      SVN_ERR(svn_sqlite__bind_blob(stmt, 6, data->data, data->len));
1578251881Speter    }
1579251881Speter
1580251881Speter  /* Execute and reset the insert clause. */
1581251881Speter  return svn_error_trace(svn_sqlite__insert(NULL, stmt));
1582251881Speter}
1583251881Speter
1584251881Speterstatic svn_boolean_t
1585251881Speteris_switched(db_node_t *parent,
1586251881Speter            db_node_t *child,
1587251881Speter            apr_pool_t *scratch_pool)
1588251881Speter{
1589251881Speter  if (parent && child)
1590251881Speter    {
1591251881Speter      if (parent->repos_id != child->repos_id)
1592251881Speter        return TRUE;
1593251881Speter
1594251881Speter      if (parent->repos_relpath && child->repos_relpath)
1595251881Speter        {
1596251881Speter          const char *unswitched
1597251881Speter            = svn_relpath_join(parent->repos_relpath,
1598251881Speter                               svn_relpath_basename(child->local_relpath,
1599251881Speter                                                    scratch_pool),
1600251881Speter                               scratch_pool);
1601251881Speter          if (strcmp(unswitched, child->repos_relpath))
1602251881Speter            return TRUE;
1603251881Speter        }
1604251881Speter    }
1605251881Speter
1606251881Speter  return FALSE;
1607251881Speter}
1608251881Speter
1609251881Speterstruct write_baton {
1610251881Speter  db_node_t *base;
1611251881Speter  db_node_t *work;
1612251881Speter  db_node_t *below_work;
1613251881Speter  apr_hash_t *tree_conflicts;
1614251881Speter};
1615251881Speter
1616251881Speter#define WRITE_ENTRY_ASSERT(expr) \
1617251881Speter  if (!(expr)) \
1618251881Speter    return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,  \
1619251881Speter                             _("Unable to upgrade '%s' at line %d"),    \
1620251881Speter                             svn_dirent_local_style( \
1621251881Speter                               svn_dirent_join(root_abspath, \
1622251881Speter                                               local_relpath,           \
1623251881Speter                                               scratch_pool),           \
1624251881Speter                               scratch_pool), __LINE__)
1625251881Speter
1626251881Speter/* Write the information for ENTRY to WC_DB.  The WC_ID, REPOS_ID and
1627251881Speter   REPOS_ROOT will all be used for writing ENTRY.
1628251881Speter   ### transitioning from straight sql to using the wc_db APIs.  For the
1629251881Speter   ### time being, we'll need both parameters. */
1630251881Speterstatic svn_error_t *
1631251881Speterwrite_entry(struct write_baton **entry_node,
1632251881Speter            const struct write_baton *parent_node,
1633251881Speter            svn_wc__db_t *db,
1634251881Speter            svn_sqlite__db_t *sdb,
1635251881Speter            apr_int64_t wc_id,
1636251881Speter            apr_int64_t repos_id,
1637251881Speter            const svn_wc_entry_t *entry,
1638251881Speter            const svn_wc__text_base_info_t *text_base_info,
1639251881Speter            const char *local_relpath,
1640251881Speter            const char *tmp_entry_abspath,
1641251881Speter            const char *root_abspath,
1642251881Speter            const svn_wc_entry_t *this_dir,
1643251881Speter            svn_boolean_t create_locks,
1644251881Speter            apr_pool_t *result_pool,
1645251881Speter            apr_pool_t *scratch_pool)
1646251881Speter{
1647251881Speter  db_node_t *base_node = NULL;
1648251881Speter  db_node_t *working_node = NULL, *below_working_node = NULL;
1649251881Speter  db_actual_node_t *actual_node = NULL;
1650251881Speter  const char *parent_relpath;
1651251881Speter  apr_hash_t *tree_conflicts;
1652251881Speter
1653251881Speter  if (*local_relpath == '\0')
1654251881Speter    parent_relpath = NULL;
1655251881Speter  else
1656251881Speter    parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
1657251881Speter
1658251881Speter  /* This is how it should work, it doesn't work like this yet because
1659251881Speter     we need proper op_depth to layer the working nodes.
1660251881Speter
1661251881Speter     Using "svn add", "svn rm", "svn cp" only files can be replaced
1662251881Speter     pre-wcng; directories can only be normal, deleted or added.
1663251881Speter     Files cannot be replaced within a deleted directory, so replaced
1664251881Speter     files can only exist in a normal directory, or a directory that
1665251881Speter     is added+copied.  In a normal directory a replaced file needs a
1666251881Speter     base node and a working node, in an added+copied directory a
1667251881Speter     replaced file needs two working nodes at different op-depths.
1668251881Speter
1669251881Speter     With just the above operations the conversion for files and
1670251881Speter     directories is straightforward:
1671251881Speter
1672251881Speter           pre-wcng                             wcng
1673251881Speter     parent         child                 parent     child
1674251881Speter
1675251881Speter     normal         normal                base       base
1676251881Speter     add+copied     normal+copied         work       work
1677251881Speter     normal+copied  normal+copied         work       work
1678251881Speter     normal         delete                base       base+work
1679251881Speter     delete         delete                base+work  base+work
1680251881Speter     add+copied     delete                work       work
1681251881Speter     normal         add                   base       work
1682251881Speter     add            add                   work       work
1683251881Speter     add+copied     add                   work       work
1684251881Speter     normal         add+copied            base       work
1685251881Speter     add            add+copied            work       work
1686251881Speter     add+copied     add+copied            work       work
1687251881Speter     normal         replace               base       base+work
1688251881Speter     add+copied     replace               work       work+work
1689251881Speter     normal         replace+copied        base       base+work
1690251881Speter     add+copied     replace+copied        work       work+work
1691251881Speter
1692251881Speter     However "svn merge" make this more complicated.  The pre-wcng
1693251881Speter     "svn merge" is capable of replacing a directory, that is it can
1694251881Speter     mark the whole tree deleted, and then copy another tree on top.
1695251881Speter     The entries then represent the replacing tree overlayed on the
1696251881Speter     deleted tree.
1697251881Speter
1698251881Speter       original       replace          schedule in
1699251881Speter       tree           tree             combined tree
1700251881Speter
1701251881Speter       A              A                replace+copied
1702251881Speter       A/f                             delete+copied
1703251881Speter       A/g            A/g              replace+copied
1704251881Speter                      A/h              add+copied
1705251881Speter       A/B            A/B              replace+copied
1706251881Speter       A/B/f                           delete+copied
1707251881Speter       A/B/g          A/B/g            replace+copied
1708251881Speter                      A/B/h            add+copied
1709251881Speter       A/C                             delete+copied
1710251881Speter       A/C/f                           delete+copied
1711251881Speter                      A/D              add+copied
1712251881Speter                      A/D/f            add+copied
1713251881Speter
1714251881Speter     The original tree could be normal tree, or an add+copied tree.
1715251881Speter     Committing such a merge generally worked, but making further tree
1716251881Speter     modifications before commit sometimes failed.
1717251881Speter
1718251881Speter     The root of the replace is handled like the file replace:
1719251881Speter
1720251881Speter           pre-wcng                             wcng
1721251881Speter     parent         child                 parent     child
1722251881Speter
1723251881Speter     normal         replace+copied        base       base+work
1724251881Speter     add+copied     replace+copied        work       work+work
1725251881Speter
1726251881Speter     although obviously the node is a directory rather then a file.
1727251881Speter     There are then more conversion states where the parent is
1728251881Speter     replaced.
1729251881Speter
1730251881Speter           pre-wcng                                wcng
1731251881Speter     parent           child              parent            child
1732251881Speter
1733251881Speter     replace+copied   add                [base|work]+work  work
1734251881Speter     replace+copied   add+copied         [base|work]+work  work
1735251881Speter     replace+copied   delete+copied      [base|work]+work  [base|work]+work
1736251881Speter     delete+copied    delete+copied      [base|work]+work  [base|work]+work
1737251881Speter     replace+copied   replace+copied     [base|work]+work  [base|work]+work
1738251881Speter  */
1739251881Speter
1740251881Speter  WRITE_ENTRY_ASSERT(parent_node || entry->schedule == svn_wc_schedule_normal);
1741251881Speter
1742251881Speter  WRITE_ENTRY_ASSERT(!parent_node || parent_node->base
1743251881Speter                     || parent_node->below_work || parent_node->work);
1744251881Speter
1745251881Speter  switch (entry->schedule)
1746251881Speter    {
1747251881Speter      case svn_wc_schedule_normal:
1748251881Speter        if (entry->copied ||
1749251881Speter            (entry->depth == svn_depth_exclude
1750251881Speter             && parent_node && !parent_node->base && parent_node->work))
1751251881Speter          working_node = MAYBE_ALLOC(working_node, result_pool);
1752251881Speter        else
1753251881Speter          base_node = MAYBE_ALLOC(base_node, result_pool);
1754251881Speter        break;
1755251881Speter
1756251881Speter      case svn_wc_schedule_add:
1757251881Speter        working_node = MAYBE_ALLOC(working_node, result_pool);
1758251881Speter        if (entry->deleted)
1759251881Speter          {
1760251881Speter            if (parent_node->base)
1761251881Speter              base_node = MAYBE_ALLOC(base_node, result_pool);
1762251881Speter            else
1763251881Speter              below_working_node = MAYBE_ALLOC(below_working_node, result_pool);
1764251881Speter          }
1765251881Speter        break;
1766251881Speter
1767251881Speter      case svn_wc_schedule_delete:
1768251881Speter        working_node = MAYBE_ALLOC(working_node, result_pool);
1769251881Speter        if (parent_node->base)
1770251881Speter          base_node = MAYBE_ALLOC(base_node, result_pool);
1771251881Speter        if (parent_node->work)
1772251881Speter          below_working_node = MAYBE_ALLOC(below_working_node, result_pool);
1773251881Speter        break;
1774251881Speter
1775251881Speter      case svn_wc_schedule_replace:
1776251881Speter        working_node = MAYBE_ALLOC(working_node, result_pool);
1777251881Speter        if (parent_node->base)
1778251881Speter          base_node = MAYBE_ALLOC(base_node, result_pool);
1779251881Speter        else
1780251881Speter          below_working_node = MAYBE_ALLOC(below_working_node, result_pool);
1781251881Speter        break;
1782251881Speter    }
1783251881Speter
1784251881Speter  /* Something deleted in this revision means there should always be a
1785251881Speter     BASE node to indicate the not-present node.  */
1786251881Speter  if (entry->deleted)
1787251881Speter    {
1788251881Speter      WRITE_ENTRY_ASSERT(base_node || below_working_node);
1789251881Speter      WRITE_ENTRY_ASSERT(!entry->incomplete);
1790251881Speter      if (base_node)
1791251881Speter        base_node->presence = svn_wc__db_status_not_present;
1792251881Speter      else
1793251881Speter        below_working_node->presence = svn_wc__db_status_not_present;
1794251881Speter    }
1795251881Speter  else if (entry->absent)
1796251881Speter    {
1797251881Speter      WRITE_ENTRY_ASSERT(base_node && !working_node && !below_working_node);
1798251881Speter      WRITE_ENTRY_ASSERT(!entry->incomplete);
1799251881Speter      base_node->presence = svn_wc__db_status_server_excluded;
1800251881Speter    }
1801251881Speter
1802251881Speter  if (entry->copied)
1803251881Speter    {
1804251881Speter      if (entry->copyfrom_url)
1805251881Speter        {
1806251881Speter          working_node->repos_id = repos_id;
1807251881Speter          working_node->repos_relpath = svn_uri_skip_ancestor(
1808251881Speter                                          this_dir->repos, entry->copyfrom_url,
1809251881Speter                                          result_pool);
1810251881Speter          working_node->revision = entry->copyfrom_rev;
1811251881Speter          working_node->op_depth
1812251881Speter            = svn_wc__db_op_depth_for_upgrade(local_relpath);
1813251881Speter        }
1814251881Speter      else if (parent_node->work && parent_node->work->repos_relpath)
1815251881Speter        {
1816251881Speter          working_node->repos_id = repos_id;
1817251881Speter          working_node->repos_relpath
1818251881Speter            = svn_relpath_join(parent_node->work->repos_relpath,
1819251881Speter                               svn_relpath_basename(local_relpath, NULL),
1820251881Speter                               result_pool);
1821251881Speter          working_node->revision = parent_node->work->revision;
1822251881Speter          working_node->op_depth = parent_node->work->op_depth;
1823251881Speter        }
1824251881Speter      else if (parent_node->below_work
1825251881Speter                && parent_node->below_work->repos_relpath)
1826251881Speter        {
1827251881Speter          working_node->repos_id = repos_id;
1828251881Speter          working_node->repos_relpath
1829251881Speter            = svn_relpath_join(parent_node->below_work->repos_relpath,
1830251881Speter                               svn_relpath_basename(local_relpath, NULL),
1831251881Speter                               result_pool);
1832251881Speter          working_node->revision = parent_node->below_work->revision;
1833251881Speter          working_node->op_depth = parent_node->below_work->op_depth;
1834251881Speter        }
1835251881Speter      else
1836251881Speter        return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
1837251881Speter                                 _("No copyfrom URL for '%s'"),
1838251881Speter                                 svn_dirent_local_style(local_relpath,
1839251881Speter                                                        scratch_pool));
1840251881Speter    }
1841251881Speter
1842251881Speter  if (entry->conflict_old)
1843251881Speter    {
1844251881Speter      actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
1845251881Speter      if (parent_relpath && entry->conflict_old)
1846251881Speter        actual_node->conflict_old = svn_relpath_join(parent_relpath,
1847251881Speter                                                     entry->conflict_old,
1848251881Speter                                                     scratch_pool);
1849251881Speter      else
1850251881Speter        actual_node->conflict_old = entry->conflict_old;
1851251881Speter      if (parent_relpath && entry->conflict_new)
1852251881Speter        actual_node->conflict_new = svn_relpath_join(parent_relpath,
1853251881Speter                                                     entry->conflict_new,
1854251881Speter                                                     scratch_pool);
1855251881Speter      else
1856251881Speter        actual_node->conflict_new = entry->conflict_new;
1857251881Speter      if (parent_relpath && entry->conflict_wrk)
1858251881Speter        actual_node->conflict_working = svn_relpath_join(parent_relpath,
1859251881Speter                                                         entry->conflict_wrk,
1860251881Speter                                                         scratch_pool);
1861251881Speter      else
1862251881Speter        actual_node->conflict_working = entry->conflict_wrk;
1863251881Speter    }
1864251881Speter
1865251881Speter  if (entry->prejfile)
1866251881Speter    {
1867251881Speter      actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
1868251881Speter      actual_node->prop_reject = svn_relpath_join((entry->kind == svn_node_dir
1869251881Speter                                                   ? local_relpath
1870251881Speter                                                   : parent_relpath),
1871251881Speter                                                  entry->prejfile,
1872251881Speter                                                  scratch_pool);
1873251881Speter    }
1874251881Speter
1875251881Speter  if (entry->changelist)
1876251881Speter    {
1877251881Speter      actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
1878251881Speter      actual_node->changelist = entry->changelist;
1879251881Speter    }
1880251881Speter
1881251881Speter  /* ### set the text_mod value? */
1882251881Speter
1883251881Speter  if (entry_node && entry->tree_conflict_data)
1884251881Speter    {
1885251881Speter      /* Issues #3840/#3916: 1.6 stores multiple tree conflicts on the
1886251881Speter         parent node, 1.7 stores them directly on the conflited nodes.
1887251881Speter         So "((skel1) (skel2))" becomes "(skel1)" and "(skel2)" */
1888251881Speter      svn_skel_t *skel;
1889251881Speter
1890251881Speter      skel = svn_skel__parse(entry->tree_conflict_data,
1891251881Speter                             strlen(entry->tree_conflict_data),
1892251881Speter                             scratch_pool);
1893251881Speter      tree_conflicts = apr_hash_make(result_pool);
1894251881Speter      skel = skel->children;
1895251881Speter      while(skel)
1896251881Speter        {
1897251881Speter          svn_wc_conflict_description2_t *conflict;
1898251881Speter          svn_skel_t *new_skel;
1899251881Speter          const char *key;
1900251881Speter
1901251881Speter          /* *CONFLICT is allocated so it is safe to use a non-const pointer */
1902251881Speter          SVN_ERR(svn_wc__deserialize_conflict(
1903251881Speter                             (const svn_wc_conflict_description2_t**)&conflict,
1904251881Speter                                               skel,
1905251881Speter                                               svn_dirent_join(root_abspath,
1906251881Speter                                                               local_relpath,
1907251881Speter                                                               scratch_pool),
1908251881Speter                                               scratch_pool, scratch_pool));
1909251881Speter
1910251881Speter          WRITE_ENTRY_ASSERT(conflict->kind == svn_wc_conflict_kind_tree);
1911251881Speter
1912251881Speter          /* Fix dubious data stored by old clients, local adds don't have
1913251881Speter             a repository URL. */
1914251881Speter          if (conflict->reason == svn_wc_conflict_reason_added)
1915251881Speter            conflict->src_left_version = NULL;
1916251881Speter
1917251881Speter          SVN_ERR(svn_wc__serialize_conflict(&new_skel, conflict,
1918251881Speter                                             scratch_pool, scratch_pool));
1919251881Speter
1920251881Speter          /* Store in hash to be retrieved when writing the child
1921251881Speter             row. */
1922251881Speter          key = svn_dirent_skip_ancestor(root_abspath, conflict->local_abspath);
1923251881Speter          svn_hash_sets(tree_conflicts, apr_pstrdup(result_pool, key),
1924251881Speter                        svn_skel__unparse(new_skel, result_pool)->data);
1925251881Speter          skel = skel->next;
1926251881Speter        }
1927251881Speter    }
1928251881Speter  else
1929251881Speter    tree_conflicts = NULL;
1930251881Speter
1931251881Speter  if (parent_node && parent_node->tree_conflicts)
1932251881Speter    {
1933251881Speter      const char *tree_conflict_data =
1934251881Speter          svn_hash_gets(parent_node->tree_conflicts, local_relpath);
1935251881Speter      if (tree_conflict_data)
1936251881Speter        {
1937251881Speter          actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
1938251881Speter          actual_node->tree_conflict_data = tree_conflict_data;
1939251881Speter        }
1940251881Speter
1941251881Speter      /* Reset hash so that we don't write the row again when writing
1942251881Speter         actual-only nodes */
1943251881Speter      svn_hash_sets(parent_node->tree_conflicts, local_relpath, NULL);
1944251881Speter    }
1945251881Speter
1946251881Speter  if (entry->file_external_path != NULL)
1947251881Speter    {
1948251881Speter      base_node = MAYBE_ALLOC(base_node, result_pool);
1949251881Speter    }
1950251881Speter
1951251881Speter
1952251881Speter  /* Insert the base node. */
1953251881Speter  if (base_node)
1954251881Speter    {
1955251881Speter      base_node->wc_id = wc_id;
1956251881Speter      base_node->local_relpath = local_relpath;
1957251881Speter      base_node->op_depth = 0;
1958251881Speter      base_node->parent_relpath = parent_relpath;
1959251881Speter      base_node->revision = entry->revision;
1960251881Speter      base_node->recorded_time = entry->text_time;
1961251881Speter      base_node->recorded_size = entry->working_size;
1962251881Speter
1963251881Speter      if (entry->depth != svn_depth_exclude)
1964251881Speter        base_node->depth = entry->depth;
1965251881Speter      else
1966251881Speter        {
1967251881Speter          base_node->presence = svn_wc__db_status_excluded;
1968251881Speter          base_node->depth = svn_depth_infinity;
1969251881Speter        }
1970251881Speter
1971251881Speter      if (entry->deleted)
1972251881Speter        {
1973251881Speter          WRITE_ENTRY_ASSERT(base_node->presence
1974251881Speter                             == svn_wc__db_status_not_present);
1975251881Speter          /* ### should be svn_node_unknown, but let's store what we have. */
1976251881Speter          base_node->kind = entry->kind;
1977251881Speter        }
1978251881Speter      else if (entry->absent)
1979251881Speter        {
1980251881Speter          WRITE_ENTRY_ASSERT(base_node->presence
1981251881Speter                             == svn_wc__db_status_server_excluded);
1982251881Speter          /* ### should be svn_node_unknown, but let's store what we have. */
1983251881Speter          base_node->kind = entry->kind;
1984251881Speter
1985251881Speter          /* Store the most likely revision in the node to avoid
1986251881Speter             base nodes without a valid revision. Of course
1987251881Speter             we remember that the data is still incomplete. */
1988251881Speter          if (!SVN_IS_VALID_REVNUM(base_node->revision) && parent_node->base)
1989251881Speter            base_node->revision = parent_node->base->revision;
1990251881Speter        }
1991251881Speter      else
1992251881Speter        {
1993251881Speter          base_node->kind = entry->kind;
1994251881Speter
1995251881Speter          if (base_node->presence != svn_wc__db_status_excluded)
1996251881Speter            {
1997251881Speter              /* All subdirs are initially incomplete, they stop being
1998251881Speter                 incomplete when the entries file in the subdir is
1999251881Speter                 upgraded and remain incomplete if that doesn't happen. */
2000251881Speter              if (entry->kind == svn_node_dir
2001251881Speter                  && strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR))
2002251881Speter                {
2003251881Speter                  base_node->presence = svn_wc__db_status_incomplete;
2004251881Speter
2005251881Speter                  /* Store the most likely revision in the node to avoid
2006251881Speter                     base nodes without a valid revision. Of course
2007251881Speter                     we remember that the data is still incomplete. */
2008251881Speter                  if (parent_node->base)
2009251881Speter                    base_node->revision = parent_node->base->revision;
2010251881Speter                }
2011251881Speter              else if (entry->incomplete)
2012251881Speter                {
2013251881Speter                  /* ### nobody should have set the presence.  */
2014251881Speter                  WRITE_ENTRY_ASSERT(base_node->presence
2015251881Speter                                     == svn_wc__db_status_normal);
2016251881Speter                  base_node->presence = svn_wc__db_status_incomplete;
2017251881Speter                }
2018251881Speter            }
2019251881Speter        }
2020251881Speter
2021251881Speter      if (entry->kind == svn_node_dir)
2022251881Speter        base_node->checksum = NULL;
2023251881Speter      else
2024251881Speter        {
2025251881Speter          if (text_base_info && text_base_info->revert_base.sha1_checksum)
2026251881Speter            base_node->checksum = text_base_info->revert_base.sha1_checksum;
2027251881Speter          else if (text_base_info && text_base_info->normal_base.sha1_checksum)
2028251881Speter            base_node->checksum = text_base_info->normal_base.sha1_checksum;
2029251881Speter          else
2030251881Speter            base_node->checksum = NULL;
2031251881Speter
2032251881Speter          /* The base MD5 checksum is available in the entry, unless there
2033251881Speter           * is a copied WORKING node.  If possible, verify that the entry
2034251881Speter           * checksum matches the base file that we found. */
2035251881Speter          if (! (working_node && entry->copied))
2036251881Speter            {
2037251881Speter              svn_checksum_t *entry_md5_checksum, *found_md5_checksum;
2038251881Speter              SVN_ERR(svn_checksum_parse_hex(&entry_md5_checksum,
2039251881Speter                                             svn_checksum_md5,
2040251881Speter                                             entry->checksum, scratch_pool));
2041251881Speter              if (text_base_info && text_base_info->revert_base.md5_checksum)
2042251881Speter                found_md5_checksum = text_base_info->revert_base.md5_checksum;
2043251881Speter              else if (text_base_info
2044251881Speter                       && text_base_info->normal_base.md5_checksum)
2045251881Speter                found_md5_checksum = text_base_info->normal_base.md5_checksum;
2046251881Speter              else
2047251881Speter                found_md5_checksum = NULL;
2048251881Speter              if (entry_md5_checksum && found_md5_checksum &&
2049251881Speter                  !svn_checksum_match(entry_md5_checksum, found_md5_checksum))
2050251881Speter                return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
2051251881Speter                                         _("Bad base MD5 checksum for '%s'; "
2052251881Speter                                           "expected: '%s'; found '%s'; "),
2053251881Speter                                       svn_dirent_local_style(
2054251881Speter                                         svn_dirent_join(root_abspath,
2055251881Speter                                                         local_relpath,
2056251881Speter                                                         scratch_pool),
2057251881Speter                                         scratch_pool),
2058251881Speter                                       svn_checksum_to_cstring_display(
2059251881Speter                                         entry_md5_checksum, scratch_pool),
2060251881Speter                                       svn_checksum_to_cstring_display(
2061251881Speter                                         found_md5_checksum, scratch_pool));
2062251881Speter              else
2063251881Speter                {
2064251881Speter                  /* ### Not sure what conditions this should cover. */
2065251881Speter                  /* SVN_ERR_ASSERT(entry->deleted || ...); */
2066251881Speter                }
2067251881Speter            }
2068251881Speter        }
2069251881Speter
2070251881Speter      if (this_dir->repos)
2071251881Speter        {
2072251881Speter          base_node->repos_id = repos_id;
2073251881Speter
2074251881Speter          if (entry->url != NULL)
2075251881Speter            {
2076251881Speter              base_node->repos_relpath = svn_uri_skip_ancestor(
2077251881Speter                                           this_dir->repos, entry->url,
2078251881Speter                                           result_pool);
2079251881Speter            }
2080251881Speter          else
2081251881Speter            {
2082251881Speter              const char *relpath = svn_uri_skip_ancestor(this_dir->repos,
2083251881Speter                                                          this_dir->url,
2084251881Speter                                                          scratch_pool);
2085251881Speter              if (relpath == NULL || *relpath == '\0')
2086251881Speter                base_node->repos_relpath = entry->name;
2087251881Speter              else
2088251881Speter                base_node->repos_relpath =
2089251881Speter                  svn_dirent_join(relpath, entry->name, result_pool);
2090251881Speter            }
2091251881Speter        }
2092251881Speter
2093251881Speter      /* TODO: These values should always be present, if they are missing
2094251881Speter         during an upgrade, set a flag, and then ask the user to talk to the
2095251881Speter         server.
2096251881Speter
2097251881Speter         Note: cmt_rev is the distinguishing value. The others may be 0 or
2098251881Speter         NULL if the corresponding revprop has been deleted.  */
2099251881Speter      base_node->changed_rev = entry->cmt_rev;
2100251881Speter      base_node->changed_date = entry->cmt_date;
2101251881Speter      base_node->changed_author = entry->cmt_author;
2102251881Speter
2103251881Speter      if (entry->file_external_path)
2104251881Speter        base_node->file_external = TRUE;
2105251881Speter
2106251881Speter      /* Switched nodes get an empty iprops cache. */
2107251881Speter      if (parent_node
2108251881Speter          && is_switched(parent_node->base, base_node, scratch_pool))
2109251881Speter        base_node->inherited_props
2110251881Speter          = apr_array_make(scratch_pool, 0, sizeof(svn_prop_inherited_item_t*));
2111251881Speter
2112251881Speter      SVN_ERR(insert_node(sdb, base_node, scratch_pool));
2113251881Speter
2114251881Speter      /* We have to insert the lock after the base node, because the node
2115251881Speter         must exist to lookup various bits of repos related information for
2116251881Speter         the abs path. */
2117251881Speter      if (entry->lock_token && create_locks)
2118251881Speter        {
2119251881Speter          svn_wc__db_lock_t lock;
2120251881Speter
2121251881Speter          lock.token = entry->lock_token;
2122251881Speter          lock.owner = entry->lock_owner;
2123251881Speter          lock.comment = entry->lock_comment;
2124251881Speter          lock.date = entry->lock_creation_date;
2125251881Speter
2126251881Speter          SVN_ERR(svn_wc__db_lock_add(db, tmp_entry_abspath, &lock,
2127251881Speter                                      scratch_pool));
2128251881Speter        }
2129251881Speter    }
2130251881Speter
2131251881Speter  if (below_working_node)
2132251881Speter    {
2133251881Speter      db_node_t *work
2134251881Speter        = parent_node->below_work ? parent_node->below_work : parent_node->work;
2135251881Speter
2136251881Speter      below_working_node->wc_id = wc_id;
2137251881Speter      below_working_node->local_relpath = local_relpath;
2138251881Speter      below_working_node->op_depth = work->op_depth;
2139251881Speter      below_working_node->parent_relpath = parent_relpath;
2140251881Speter      below_working_node->presence = svn_wc__db_status_normal;
2141251881Speter      below_working_node->kind = entry->kind;
2142251881Speter      below_working_node->repos_id = work->repos_id;
2143251881Speter
2144251881Speter      /* This is just guessing. If the node below would have been switched
2145251881Speter         or if it was updated to a different version, the guess would
2146251881Speter         fail. But we don't have better information pre wc-ng :( */
2147251881Speter      if (work->repos_relpath)
2148251881Speter        below_working_node->repos_relpath
2149251881Speter          = svn_relpath_join(work->repos_relpath, entry->name,
2150251881Speter                             result_pool);
2151251881Speter      else
2152251881Speter        below_working_node->repos_relpath = NULL;
2153251881Speter      below_working_node->revision = parent_node->work->revision;
2154251881Speter
2155251881Speter      /* The revert_base checksum isn't available in the entry structure,
2156251881Speter         so the caller provides it. */
2157251881Speter
2158251881Speter      /* text_base_info is NULL for files scheduled to be added. */
2159251881Speter      below_working_node->checksum = NULL;
2160251881Speter      if (text_base_info)
2161251881Speter        {
2162251881Speter          if (entry->schedule == svn_wc_schedule_delete)
2163251881Speter            below_working_node->checksum =
2164251881Speter              text_base_info->normal_base.sha1_checksum;
2165251881Speter          else
2166251881Speter            below_working_node->checksum =
2167251881Speter              text_base_info->revert_base.sha1_checksum;
2168251881Speter        }
2169251881Speter      below_working_node->recorded_size = 0;
2170251881Speter      below_working_node->changed_rev = SVN_INVALID_REVNUM;
2171251881Speter      below_working_node->changed_date = 0;
2172251881Speter      below_working_node->changed_author = NULL;
2173251881Speter      below_working_node->depth = svn_depth_infinity;
2174251881Speter      below_working_node->recorded_time = 0;
2175251881Speter      below_working_node->properties = NULL;
2176251881Speter
2177251881Speter      if (working_node
2178251881Speter          && entry->schedule == svn_wc_schedule_delete
2179251881Speter          && working_node->repos_relpath)
2180251881Speter        {
2181251881Speter          /* We are lucky, our guesses above are not necessary. The known
2182251881Speter             correct information is in working. But our op_depth design
2183251881Speter             expects more information here */
2184251881Speter          below_working_node->repos_relpath = working_node->repos_relpath;
2185251881Speter          below_working_node->repos_id = working_node->repos_id;
2186251881Speter          below_working_node->revision = working_node->revision;
2187251881Speter
2188251881Speter          /* Nice for 'svn status' */
2189251881Speter          below_working_node->changed_rev = entry->cmt_rev;
2190251881Speter          below_working_node->changed_date = entry->cmt_date;
2191251881Speter          below_working_node->changed_author = entry->cmt_author;
2192251881Speter
2193251881Speter          /* And now remove it from WORKING, because in wc-ng code
2194251881Speter             should read it from the lower layer */
2195251881Speter          working_node->repos_relpath = NULL;
2196251881Speter          working_node->repos_id = 0;
2197251881Speter          working_node->revision = SVN_INVALID_REVNUM;
2198251881Speter        }
2199251881Speter
2200251881Speter      SVN_ERR(insert_node(sdb, below_working_node, scratch_pool));
2201251881Speter    }
2202251881Speter
2203251881Speter  /* Insert the working node. */
2204251881Speter  if (working_node)
2205251881Speter    {
2206251881Speter      working_node->wc_id = wc_id;
2207251881Speter      working_node->local_relpath = local_relpath;
2208251881Speter      working_node->parent_relpath = parent_relpath;
2209251881Speter      working_node->changed_rev = SVN_INVALID_REVNUM;
2210251881Speter      working_node->recorded_time = entry->text_time;
2211251881Speter      working_node->recorded_size = entry->working_size;
2212251881Speter
2213251881Speter      if (entry->depth != svn_depth_exclude)
2214251881Speter        working_node->depth = entry->depth;
2215251881Speter      else
2216251881Speter        {
2217251881Speter          working_node->presence = svn_wc__db_status_excluded;
2218251881Speter          working_node->depth = svn_depth_infinity;
2219251881Speter        }
2220251881Speter
2221251881Speter      if (entry->kind == svn_node_dir)
2222251881Speter        working_node->checksum = NULL;
2223251881Speter      else
2224251881Speter        {
2225251881Speter          working_node->checksum = NULL;
2226251881Speter          /* text_base_info is NULL for files scheduled to be added. */
2227251881Speter          if (text_base_info)
2228251881Speter            working_node->checksum = text_base_info->normal_base.sha1_checksum;
2229251881Speter
2230251881Speter
2231251881Speter          /* If an MD5 checksum is present in the entry, we can verify that
2232251881Speter           * it matches the MD5 of the base file we found earlier. */
2233251881Speter#ifdef SVN_DEBUG
2234251881Speter          if (entry->checksum && text_base_info)
2235251881Speter          {
2236251881Speter            svn_checksum_t *md5_checksum;
2237251881Speter            SVN_ERR(svn_checksum_parse_hex(&md5_checksum, svn_checksum_md5,
2238251881Speter                                           entry->checksum, result_pool));
2239251881Speter            SVN_ERR_ASSERT(
2240251881Speter              md5_checksum && text_base_info->normal_base.md5_checksum);
2241251881Speter            SVN_ERR_ASSERT(svn_checksum_match(
2242251881Speter              md5_checksum, text_base_info->normal_base.md5_checksum));
2243251881Speter          }
2244251881Speter#endif
2245251881Speter        }
2246251881Speter
2247251881Speter      working_node->kind = entry->kind;
2248251881Speter      if (working_node->presence != svn_wc__db_status_excluded)
2249251881Speter        {
2250251881Speter          /* All subdirs start of incomplete, and stop being incomplete
2251251881Speter             when the entries file in the subdir is upgraded. */
2252251881Speter          if (entry->kind == svn_node_dir
2253251881Speter              && strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR))
2254251881Speter            {
2255251881Speter              working_node->presence = svn_wc__db_status_incomplete;
2256251881Speter              working_node->kind = svn_node_dir;
2257251881Speter            }
2258251881Speter          else if (entry->schedule == svn_wc_schedule_delete)
2259251881Speter            {
2260251881Speter              working_node->presence = svn_wc__db_status_base_deleted;
2261251881Speter              working_node->kind = entry->kind;
2262251881Speter            }
2263251881Speter          else
2264251881Speter            {
2265251881Speter              /* presence == normal  */
2266251881Speter              working_node->kind = entry->kind;
2267251881Speter
2268251881Speter              if (entry->incomplete)
2269251881Speter                {
2270251881Speter                  /* We shouldn't be overwriting another status.  */
2271251881Speter                  WRITE_ENTRY_ASSERT(working_node->presence
2272251881Speter                                     == svn_wc__db_status_normal);
2273251881Speter                  working_node->presence = svn_wc__db_status_incomplete;
2274251881Speter                }
2275251881Speter            }
2276251881Speter        }
2277251881Speter
2278251881Speter      /* These should generally be unset for added and deleted files,
2279251881Speter         and contain whatever information we have for copied files. Let's
2280251881Speter         just store whatever we have.
2281251881Speter
2282251881Speter         Note: cmt_rev is the distinguishing value. The others may be 0 or
2283251881Speter         NULL if the corresponding revprop has been deleted.  */
2284251881Speter      if (working_node->presence != svn_wc__db_status_base_deleted)
2285251881Speter        {
2286251881Speter          working_node->changed_rev = entry->cmt_rev;
2287251881Speter          working_node->changed_date = entry->cmt_date;
2288251881Speter          working_node->changed_author = entry->cmt_author;
2289251881Speter        }
2290251881Speter
2291251881Speter      if (entry->schedule == svn_wc_schedule_delete
2292251881Speter          && parent_node->work
2293251881Speter          && parent_node->work->presence == svn_wc__db_status_base_deleted)
2294251881Speter        {
2295251881Speter          working_node->op_depth = parent_node->work->op_depth;
2296251881Speter        }
2297251881Speter      else if (!entry->copied)
2298251881Speter        {
2299251881Speter          working_node->op_depth
2300251881Speter            = svn_wc__db_op_depth_for_upgrade(local_relpath);
2301251881Speter        }
2302251881Speter
2303251881Speter      SVN_ERR(insert_node(sdb, working_node, scratch_pool));
2304251881Speter    }
2305251881Speter
2306251881Speter  /* Insert the actual node. */
2307251881Speter  if (actual_node)
2308251881Speter    {
2309251881Speter      actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
2310251881Speter
2311251881Speter      actual_node->wc_id = wc_id;
2312251881Speter      actual_node->local_relpath = local_relpath;
2313251881Speter      actual_node->parent_relpath = parent_relpath;
2314251881Speter
2315251881Speter      SVN_ERR(insert_actual_node(sdb, db, tmp_entry_abspath,
2316251881Speter                                 actual_node, scratch_pool));
2317251881Speter    }
2318251881Speter
2319251881Speter  if (entry_node)
2320251881Speter    {
2321251881Speter      *entry_node = apr_palloc(result_pool, sizeof(**entry_node));
2322251881Speter      (*entry_node)->base = base_node;
2323251881Speter      (*entry_node)->work = working_node;
2324251881Speter      (*entry_node)->below_work = below_working_node;
2325251881Speter      (*entry_node)->tree_conflicts = tree_conflicts;
2326251881Speter    }
2327251881Speter
2328251881Speter  if (entry->file_external_path)
2329251881Speter    {
2330251881Speter      /* TODO: Maybe add a file external registration inside EXTERNALS here,
2331251881Speter               to allow removing file externals that aren't referenced from
2332251881Speter               svn:externals.
2333251881Speter
2334251881Speter         The svn:externals values are processed anyway after everything is
2335251881Speter         upgraded */
2336251881Speter    }
2337251881Speter
2338251881Speter  return SVN_NO_ERROR;
2339251881Speter}
2340251881Speter
2341251881Speterstatic svn_error_t *
2342251881Speterwrite_actual_only_entries(apr_hash_t *tree_conflicts,
2343251881Speter                          svn_sqlite__db_t *sdb,
2344251881Speter                          svn_wc__db_t *db,
2345251881Speter                          const char *wri_abspath,
2346251881Speter                          apr_int64_t wc_id,
2347251881Speter                          const char *parent_relpath,
2348251881Speter                          apr_pool_t *scratch_pool)
2349251881Speter{
2350251881Speter  apr_hash_index_t *hi;
2351251881Speter
2352251881Speter  for (hi = apr_hash_first(scratch_pool, tree_conflicts);
2353251881Speter       hi;
2354251881Speter       hi = apr_hash_next(hi))
2355251881Speter    {
2356251881Speter      db_actual_node_t *actual_node = NULL;
2357251881Speter
2358251881Speter      actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
2359251881Speter      actual_node->wc_id = wc_id;
2360251881Speter      actual_node->local_relpath = svn__apr_hash_index_key(hi);
2361251881Speter      actual_node->parent_relpath = parent_relpath;
2362251881Speter      actual_node->tree_conflict_data = svn__apr_hash_index_val(hi);
2363251881Speter
2364251881Speter      SVN_ERR(insert_actual_node(sdb, db, wri_abspath, actual_node,
2365251881Speter                                 scratch_pool));
2366251881Speter    }
2367251881Speter
2368251881Speter  return SVN_NO_ERROR;
2369251881Speter}
2370251881Speter
2371251881Spetersvn_error_t *
2372251881Spetersvn_wc__write_upgraded_entries(void **dir_baton,
2373251881Speter                               void *parent_baton,
2374251881Speter                               svn_wc__db_t *db,
2375251881Speter                               svn_sqlite__db_t *sdb,
2376251881Speter                               apr_int64_t repos_id,
2377251881Speter                               apr_int64_t wc_id,
2378251881Speter                               const char *dir_abspath,
2379251881Speter                               const char *new_root_abspath,
2380251881Speter                               apr_hash_t *entries,
2381251881Speter                               apr_hash_t *text_bases_info,
2382251881Speter                               apr_pool_t *result_pool,
2383251881Speter                               apr_pool_t *scratch_pool)
2384251881Speter{
2385251881Speter  const svn_wc_entry_t *this_dir;
2386251881Speter  apr_hash_index_t *hi;
2387251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2388251881Speter  const char *old_root_abspath, *dir_relpath;
2389251881Speter  struct write_baton *parent_node = parent_baton;
2390251881Speter  struct write_baton *dir_node;
2391251881Speter
2392251881Speter  /* Get a copy of the "this dir" entry for comparison purposes. */
2393251881Speter  this_dir = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR);
2394251881Speter
2395251881Speter  /* If there is no "this dir" entry, something is wrong. */
2396251881Speter  if (! this_dir)
2397251881Speter    return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
2398251881Speter                             _("No default entry in directory '%s'"),
2399251881Speter                             svn_dirent_local_style(dir_abspath,
2400251881Speter                                                    iterpool));
2401251881Speter  old_root_abspath = svn_dirent_get_longest_ancestor(dir_abspath,
2402251881Speter                                                     new_root_abspath,
2403251881Speter                                                     scratch_pool);
2404251881Speter
2405251881Speter  SVN_ERR_ASSERT(old_root_abspath[0]);
2406251881Speter
2407251881Speter  dir_relpath = svn_dirent_skip_ancestor(old_root_abspath, dir_abspath);
2408251881Speter
2409251881Speter  /* Write out "this dir" */
2410251881Speter  SVN_ERR(write_entry(&dir_node, parent_node, db, sdb,
2411251881Speter                      wc_id, repos_id, this_dir, NULL, dir_relpath,
2412251881Speter                      svn_dirent_join(new_root_abspath, dir_relpath,
2413251881Speter                                      iterpool),
2414251881Speter                      old_root_abspath,
2415251881Speter                      this_dir, FALSE, result_pool, iterpool));
2416251881Speter
2417251881Speter  for (hi = apr_hash_first(scratch_pool, entries); hi;
2418251881Speter       hi = apr_hash_next(hi))
2419251881Speter    {
2420251881Speter      const char *name = svn__apr_hash_index_key(hi);
2421251881Speter      const svn_wc_entry_t *this_entry = svn__apr_hash_index_val(hi);
2422251881Speter      const char *child_abspath, *child_relpath;
2423251881Speter      svn_wc__text_base_info_t *text_base_info
2424251881Speter        = svn_hash_gets(text_bases_info, name);
2425251881Speter
2426251881Speter      svn_pool_clear(iterpool);
2427251881Speter
2428251881Speter      /* Don't rewrite the "this dir" entry! */
2429251881Speter      if (strcmp(name, SVN_WC_ENTRY_THIS_DIR) == 0)
2430251881Speter        continue;
2431251881Speter
2432251881Speter      /* Write the entry. Pass TRUE for create locks, because we still
2433251881Speter         use this function for upgrading old working copies. */
2434251881Speter      child_abspath = svn_dirent_join(dir_abspath, name, iterpool);
2435251881Speter      child_relpath = svn_dirent_skip_ancestor(old_root_abspath, child_abspath);
2436251881Speter      SVN_ERR(write_entry(NULL, dir_node, db, sdb,
2437251881Speter                          wc_id, repos_id,
2438251881Speter                          this_entry, text_base_info, child_relpath,
2439251881Speter                          svn_dirent_join(new_root_abspath, child_relpath,
2440251881Speter                                          iterpool),
2441251881Speter                          old_root_abspath,
2442251881Speter                          this_dir, TRUE, iterpool, iterpool));
2443251881Speter    }
2444251881Speter
2445251881Speter  if (dir_node->tree_conflicts)
2446251881Speter    SVN_ERR(write_actual_only_entries(dir_node->tree_conflicts, sdb, db,
2447251881Speter                                      new_root_abspath, wc_id, dir_relpath,
2448251881Speter                                      iterpool));
2449251881Speter
2450251881Speter  *dir_baton = dir_node;
2451251881Speter  svn_pool_destroy(iterpool);
2452251881Speter  return SVN_NO_ERROR;
2453251881Speter}
2454251881Speter
2455251881Speter
2456251881Spetersvn_wc_entry_t *
2457251881Spetersvn_wc_entry_dup(const svn_wc_entry_t *entry, apr_pool_t *pool)
2458251881Speter{
2459251881Speter  svn_wc_entry_t *dupentry = apr_palloc(pool, sizeof(*dupentry));
2460251881Speter
2461251881Speter  /* Perform a trivial copy ... */
2462251881Speter  *dupentry = *entry;
2463251881Speter
2464251881Speter  /* ...and then re-copy stuff that needs to be duped into our pool. */
2465251881Speter  if (entry->name)
2466251881Speter    dupentry->name = apr_pstrdup(pool, entry->name);
2467251881Speter  if (entry->url)
2468251881Speter    dupentry->url = apr_pstrdup(pool, entry->url);
2469251881Speter  if (entry->repos)
2470251881Speter    dupentry->repos = apr_pstrdup(pool, entry->repos);
2471251881Speter  if (entry->uuid)
2472251881Speter    dupentry->uuid = apr_pstrdup(pool, entry->uuid);
2473251881Speter  if (entry->copyfrom_url)
2474251881Speter    dupentry->copyfrom_url = apr_pstrdup(pool, entry->copyfrom_url);
2475251881Speter  if (entry->conflict_old)
2476251881Speter    dupentry->conflict_old = apr_pstrdup(pool, entry->conflict_old);
2477251881Speter  if (entry->conflict_new)
2478251881Speter    dupentry->conflict_new = apr_pstrdup(pool, entry->conflict_new);
2479251881Speter  if (entry->conflict_wrk)
2480251881Speter    dupentry->conflict_wrk = apr_pstrdup(pool, entry->conflict_wrk);
2481251881Speter  if (entry->prejfile)
2482251881Speter    dupentry->prejfile = apr_pstrdup(pool, entry->prejfile);
2483251881Speter  if (entry->checksum)
2484251881Speter    dupentry->checksum = apr_pstrdup(pool, entry->checksum);
2485251881Speter  if (entry->cmt_author)
2486251881Speter    dupentry->cmt_author = apr_pstrdup(pool, entry->cmt_author);
2487251881Speter  if (entry->lock_token)
2488251881Speter    dupentry->lock_token = apr_pstrdup(pool, entry->lock_token);
2489251881Speter  if (entry->lock_owner)
2490251881Speter    dupentry->lock_owner = apr_pstrdup(pool, entry->lock_owner);
2491251881Speter  if (entry->lock_comment)
2492251881Speter    dupentry->lock_comment = apr_pstrdup(pool, entry->lock_comment);
2493251881Speter  if (entry->changelist)
2494251881Speter    dupentry->changelist = apr_pstrdup(pool, entry->changelist);
2495251881Speter
2496251881Speter  /* NOTE: we do not dup cachable_props or present_props since they
2497251881Speter     are deprecated. Use "" to indicate "nothing cachable or cached". */
2498251881Speter  dupentry->cachable_props = "";
2499251881Speter  dupentry->present_props = "";
2500251881Speter
2501251881Speter  if (entry->tree_conflict_data)
2502251881Speter    dupentry->tree_conflict_data = apr_pstrdup(pool,
2503251881Speter                                               entry->tree_conflict_data);
2504251881Speter  if (entry->file_external_path)
2505251881Speter    dupentry->file_external_path = apr_pstrdup(pool,
2506251881Speter                                               entry->file_external_path);
2507251881Speter  return dupentry;
2508251881Speter}
2509251881Speter
2510251881Speter
2511251881Speter/*** Generic Entry Walker */
2512251881Speter
2513251881Speter/* A recursive entry-walker, helper for svn_wc_walk_entries3().
2514251881Speter *
2515251881Speter * For this directory (DIRPATH, ADM_ACCESS), call the "found_entry" callback
2516251881Speter * in WALK_CALLBACKS, passing WALK_BATON to it. Then, for each versioned
2517251881Speter * entry in this directory, call the "found entry" callback and then recurse
2518251881Speter * (if it is a directory and if DEPTH allows).
2519251881Speter *
2520251881Speter * If SHOW_HIDDEN is true, include entries that are in a 'deleted' or
2521251881Speter * 'absent' state (and not scheduled for re-addition), else skip them.
2522251881Speter *
2523251881Speter * Call CANCEL_FUNC with CANCEL_BATON to allow cancellation.
2524251881Speter */
2525251881Speterstatic svn_error_t *
2526251881Speterwalker_helper(const char *dirpath,
2527251881Speter              svn_wc_adm_access_t *adm_access,
2528251881Speter              const svn_wc_entry_callbacks2_t *walk_callbacks,
2529251881Speter              void *walk_baton,
2530251881Speter              svn_depth_t depth,
2531251881Speter              svn_boolean_t show_hidden,
2532251881Speter              svn_cancel_func_t cancel_func,
2533251881Speter              void *cancel_baton,
2534251881Speter              apr_pool_t *pool)
2535251881Speter{
2536251881Speter  apr_pool_t *subpool = svn_pool_create(pool);
2537251881Speter  apr_hash_t *entries;
2538251881Speter  apr_hash_index_t *hi;
2539251881Speter  svn_wc_entry_t *dot_entry;
2540251881Speter  svn_error_t *err;
2541251881Speter  svn_wc__db_t *db = svn_wc__adm_get_db(adm_access);
2542251881Speter
2543251881Speter  err = svn_wc__entries_read_internal(&entries, adm_access, show_hidden,
2544251881Speter                                      pool);
2545251881Speter
2546251881Speter  if (err)
2547251881Speter    SVN_ERR(walk_callbacks->handle_error(dirpath, err, walk_baton, pool));
2548251881Speter
2549251881Speter  /* As promised, always return the '.' entry first. */
2550251881Speter  dot_entry = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR);
2551251881Speter  if (! dot_entry)
2552251881Speter    return walk_callbacks->handle_error
2553251881Speter      (dirpath, svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
2554251881Speter                                  _("Directory '%s' has no THIS_DIR entry"),
2555251881Speter                                  svn_dirent_local_style(dirpath, pool)),
2556251881Speter       walk_baton, pool);
2557251881Speter
2558251881Speter  /* Call the "found entry" callback for this directory as a "this dir"
2559251881Speter   * entry. Note that if this directory has been reached by recursion, this
2560251881Speter   * is the second visit as it will already have been visited once as a
2561251881Speter   * child entry of its parent. */
2562251881Speter
2563251881Speter  err = walk_callbacks->found_entry(dirpath, dot_entry, walk_baton, subpool);
2564251881Speter
2565251881Speter
2566251881Speter  if(err)
2567251881Speter    SVN_ERR(walk_callbacks->handle_error(dirpath, err, walk_baton, pool));
2568251881Speter
2569251881Speter  if (depth == svn_depth_empty)
2570251881Speter    return SVN_NO_ERROR;
2571251881Speter
2572251881Speter  /* Loop over each of the other entries. */
2573251881Speter  for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
2574251881Speter    {
2575251881Speter      const char *name = svn__apr_hash_index_key(hi);
2576251881Speter      const svn_wc_entry_t *current_entry = svn__apr_hash_index_val(hi);
2577251881Speter      const char *entrypath;
2578251881Speter      const char *entry_abspath;
2579251881Speter      svn_boolean_t hidden;
2580251881Speter
2581251881Speter      svn_pool_clear(subpool);
2582251881Speter
2583251881Speter      /* See if someone wants to cancel this operation. */
2584251881Speter      if (cancel_func)
2585251881Speter        SVN_ERR(cancel_func(cancel_baton));
2586251881Speter
2587251881Speter      /* Skip the "this dir" entry. */
2588251881Speter      if (strcmp(current_entry->name, SVN_WC_ENTRY_THIS_DIR) == 0)
2589251881Speter        continue;
2590251881Speter
2591251881Speter      entrypath = svn_dirent_join(dirpath, name, subpool);
2592251881Speter      SVN_ERR(svn_wc__entry_is_hidden(&hidden, current_entry));
2593251881Speter      SVN_ERR(svn_dirent_get_absolute(&entry_abspath, entrypath, subpool));
2594251881Speter
2595251881Speter      /* Call the "found entry" callback for this entry. (For a directory,
2596251881Speter       * this is the first visit: as a child.) */
2597251881Speter      if (current_entry->kind == svn_node_file
2598251881Speter          || depth >= svn_depth_immediates)
2599251881Speter        {
2600251881Speter          err = walk_callbacks->found_entry(entrypath, current_entry,
2601251881Speter                                            walk_baton, subpool);
2602251881Speter
2603251881Speter          if (err)
2604251881Speter            SVN_ERR(walk_callbacks->handle_error(entrypath, err,
2605251881Speter                                                 walk_baton, pool));
2606251881Speter        }
2607251881Speter
2608251881Speter      /* Recurse into this entry if appropriate. */
2609251881Speter      if (current_entry->kind == svn_node_dir
2610251881Speter          && !hidden
2611251881Speter          && depth >= svn_depth_immediates)
2612251881Speter        {
2613251881Speter          svn_wc_adm_access_t *entry_access;
2614251881Speter          svn_depth_t depth_below_here = depth;
2615251881Speter
2616251881Speter          if (depth == svn_depth_immediates)
2617251881Speter            depth_below_here = svn_depth_empty;
2618251881Speter
2619251881Speter          entry_access = svn_wc__adm_retrieve_internal2(db, entry_abspath,
2620251881Speter                                                        subpool);
2621251881Speter
2622251881Speter          if (entry_access)
2623251881Speter            SVN_ERR(walker_helper(entrypath, entry_access,
2624251881Speter                                  walk_callbacks, walk_baton,
2625251881Speter                                  depth_below_here, show_hidden,
2626251881Speter                                  cancel_func, cancel_baton,
2627251881Speter                                  subpool));
2628251881Speter        }
2629251881Speter    }
2630251881Speter
2631251881Speter  svn_pool_destroy(subpool);
2632251881Speter  return SVN_NO_ERROR;
2633251881Speter}
2634251881Speter
2635251881Spetersvn_error_t *
2636251881Spetersvn_wc__walker_default_error_handler(const char *path,
2637251881Speter                                     svn_error_t *err,
2638251881Speter                                     void *walk_baton,
2639251881Speter                                     apr_pool_t *pool)
2640251881Speter{
2641251881Speter  /* Note: don't trace this. We don't want to insert a false "stack frame"
2642251881Speter     onto an error generated elsewhere.  */
2643251881Speter  return svn_error_trace(err);
2644251881Speter}
2645251881Speter
2646251881Speter
2647251881Speter/* The public API. */
2648251881Spetersvn_error_t *
2649251881Spetersvn_wc_walk_entries3(const char *path,
2650251881Speter                     svn_wc_adm_access_t *adm_access,
2651251881Speter                     const svn_wc_entry_callbacks2_t *walk_callbacks,
2652251881Speter                     void *walk_baton,
2653251881Speter                     svn_depth_t walk_depth,
2654251881Speter                     svn_boolean_t show_hidden,
2655251881Speter                     svn_cancel_func_t cancel_func,
2656251881Speter                     void *cancel_baton,
2657251881Speter                     apr_pool_t *pool)
2658251881Speter{
2659251881Speter  const char *local_abspath;
2660251881Speter  svn_wc__db_t *db = svn_wc__adm_get_db(adm_access);
2661251881Speter  svn_error_t *err;
2662251881Speter  svn_node_kind_t kind;
2663251881Speter  svn_depth_t depth;
2664251881Speter
2665251881Speter  SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
2666251881Speter  err = svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL,
2667251881Speter                             NULL, NULL, NULL, &depth, NULL, NULL,
2668251881Speter                             NULL, NULL, NULL, NULL, NULL, NULL,
2669251881Speter                             NULL, NULL, NULL, NULL, NULL, NULL,
2670251881Speter                             NULL, NULL, NULL,
2671251881Speter                             db, local_abspath,
2672251881Speter                             pool, pool);
2673251881Speter  if (err)
2674251881Speter    {
2675251881Speter      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2676251881Speter        return svn_error_trace(err);
2677251881Speter      /* Remap into SVN_ERR_UNVERSIONED_RESOURCE.  */
2678251881Speter      svn_error_clear(err);
2679251881Speter      return walk_callbacks->handle_error(
2680251881Speter        path, svn_error_createf(SVN_ERR_UNVERSIONED_RESOURCE, NULL,
2681251881Speter                                _("'%s' is not under version control"),
2682251881Speter                                svn_dirent_local_style(local_abspath, pool)),
2683251881Speter        walk_baton, pool);
2684251881Speter    }
2685251881Speter
2686251881Speter  if (kind == svn_node_file || depth == svn_depth_exclude)
2687251881Speter    {
2688251881Speter      const svn_wc_entry_t *entry;
2689251881Speter
2690251881Speter      /* ### we should stop passing out entry structures.
2691251881Speter         ###
2692251881Speter         ### we should not call handle_error for an error the *callback*
2693251881Speter         ###   gave us. let it deal with the problem before returning.  */
2694251881Speter
2695251881Speter      if (!show_hidden)
2696251881Speter        {
2697251881Speter          svn_boolean_t hidden;
2698251881Speter          SVN_ERR(svn_wc__db_node_hidden(&hidden, db, local_abspath, pool));
2699251881Speter
2700251881Speter          if (hidden)
2701251881Speter            {
2702251881Speter              /* The fool asked to walk a "hidden" node. Report the node as
2703251881Speter                 unversioned.
2704251881Speter
2705251881Speter                 ### this is incorrect behavior. see depth_test 36. the walk
2706251881Speter                 ### API will be revamped to avoid entry structures. we should
2707251881Speter                 ### be able to solve the problem with the new API. (since we
2708251881Speter                 ### shouldn't return a hidden entry here)  */
2709251881Speter              return walk_callbacks->handle_error(
2710251881Speter                               path, svn_error_createf(
2711251881Speter                                  SVN_ERR_UNVERSIONED_RESOURCE, NULL,
2712251881Speter                                  _("'%s' is not under version control"),
2713251881Speter                                  svn_dirent_local_style(local_abspath, pool)),
2714251881Speter                               walk_baton, pool);
2715251881Speter            }
2716251881Speter        }
2717251881Speter
2718251881Speter      SVN_ERR(svn_wc__get_entry(&entry, db, local_abspath, FALSE,
2719251881Speter                                svn_node_file, pool, pool));
2720251881Speter
2721251881Speter      err = walk_callbacks->found_entry(path, entry, walk_baton, pool);
2722251881Speter      if (err)
2723251881Speter        return walk_callbacks->handle_error(path, err, walk_baton, pool);
2724251881Speter
2725251881Speter      return SVN_NO_ERROR;
2726251881Speter    }
2727251881Speter
2728251881Speter  if (kind == svn_node_dir)
2729251881Speter    return walker_helper(path, adm_access, walk_callbacks, walk_baton,
2730251881Speter                         walk_depth, show_hidden, cancel_func, cancel_baton,
2731251881Speter                         pool);
2732251881Speter
2733251881Speter  return walk_callbacks->handle_error(
2734251881Speter       path, svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL,
2735251881Speter                               _("'%s' has an unrecognized node kind"),
2736251881Speter                               svn_dirent_local_style(local_abspath, pool)),
2737251881Speter       walk_baton, pool);
2738251881Speter}
2739