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