1 /*
2  * update_editor.c :  main editor for checkouts and updates
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 
25 
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include <apr_pools.h>
30 #include <apr_hash.h>
31 #include <apr_md5.h>
32 #include <apr_tables.h>
33 #include <apr_strings.h>
34 
35 #include "svn_types.h"
36 #include "svn_pools.h"
37 #include "svn_hash.h"
38 #include "svn_string.h"
39 #include "svn_dirent_uri.h"
40 #include "svn_path.h"
41 #include "svn_error.h"
42 #include "svn_io.h"
43 #include "svn_private_config.h"
44 #include "svn_time.h"
45 
46 #include "wc.h"
47 #include "adm_files.h"
48 #include "conflicts.h"
49 #include "translate.h"
50 #include "workqueue.h"
51 
52 #include "private/svn_subr_private.h"
53 #include "private/svn_wc_private.h"
54 #include "private/svn_editor.h"
55 
56 /* Checks whether a svn_wc__db_status_t indicates whether a node is
57    present in a working copy. Used by the editor implementation */
58 #define IS_NODE_PRESENT(status)                             \
59            ((status) != svn_wc__db_status_server_excluded &&\
60             (status) != svn_wc__db_status_excluded &&       \
61             (status) != svn_wc__db_status_not_present)
62 
63 static svn_error_t *
64 path_join_under_root(const char **result_path,
65                      const char *base_path,
66                      const char *add_path,
67                      apr_pool_t *result_pool);
68 
69 
70 /*
71  * This code handles "checkout" and "update" and "switch".
72  * A checkout is similar to an update that is only adding new items.
73  *
74  * The intended behaviour of "update" and "switch", focusing on the checks
75  * to be made before applying a change, is:
76  *
77  *   For each incoming change:
78  *     if target is already in conflict or obstructed:
79  *       skip this change
80  *     else
81  *     if this action will cause a tree conflict:
82  *       record the tree conflict
83  *       skip this change
84  *     else:
85  *       make this change
86  *
87  * In more detail:
88  *
89  *   For each incoming change:
90  *
91  *   1.   if  # Incoming change is inside an item already in conflict:
92  *    a.    tree/text/prop change to node beneath tree-conflicted dir
93  *        then  # Skip all changes in this conflicted subtree [*1]:
94  *          do not update the Base nor the Working
95  *          notify "skipped because already in conflict" just once
96  *            for the whole conflicted subtree
97  *
98  *        if  # Incoming change affects an item already in conflict:
99  *    b.    tree/text/prop change to tree-conflicted dir/file, or
100  *    c.    tree change to a text/prop-conflicted file/dir, or
101  *    d.    text/prop change to a text/prop-conflicted file/dir [*2], or
102  *    e.    tree change to a dir tree containing any conflicts,
103  *        then  # Skip this change [*1]:
104  *          do not update the Base nor the Working
105  *          notify "skipped because already in conflict"
106  *
107  *   2.   if  # Incoming change affects an item that's "obstructed":
108  *    a.    on-disk node kind doesn't match recorded Working node kind
109  *            (including an absence/presence mis-match),
110  *        then  # Skip this change [*1]:
111  *          do not update the Base nor the Working
112  *          notify "skipped because obstructed"
113  *
114  *   3.   if  # Incoming change raises a tree conflict:
115  *    a.    tree/text/prop change to node beneath sched-delete dir, or
116  *    b.    tree/text/prop change to sched-delete dir/file, or
117  *    c.    text/prop change to tree-scheduled dir/file,
118  *        then  # Skip this change:
119  *          do not update the Base nor the Working [*3]
120  *          notify "tree conflict"
121  *
122  *   4.   Apply the change:
123  *          update the Base
124  *          update the Working, possibly raising text/prop conflicts
125  *          notify
126  *
127  * Notes:
128  *
129  *      "Tree change" here refers to an add or delete of the target node,
130  *      including the add or delete part of a copy or move or rename.
131  *
132  * [*1] We should skip changes to an entire node, as the base revision number
133  *      applies to the entire node. Not sure how this affects attempts to
134  *      handle text and prop changes separately.
135  *
136  * [*2] Details of which combinations of property and text changes conflict
137  *      are not specified here.
138  *
139  * [*3] For now, we skip the update, and require the user to:
140  *        - Modify the WC to be compatible with the incoming change;
141  *        - Mark the conflict as resolved;
142  *        - Repeat the update.
143  *      Ideally, it would be possible to resolve any conflict without
144  *      repeating the update. To achieve this, we would have to store the
145  *      necessary data at conflict detection time, and delay the update of
146  *      the Base until the time of resolving.
147  */
148 
149 
150 /*** batons ***/
151 
152 struct edit_baton
153 {
154   /* For updates, the "destination" of the edit is ANCHOR_ABSPATH, the
155      directory containing TARGET_ABSPATH. If ANCHOR_ABSPATH itself is the
156      target, the values are identical.
157 
158      TARGET_BASENAME is the name of TARGET_ABSPATH in ANCHOR_ABSPATH, or "" if
159      ANCHOR_ABSPATH is the target */
160   const char *target_basename;
161 
162   /* Absolute variants of ANCHOR and TARGET */
163   const char *anchor_abspath;
164   const char *target_abspath;
165 
166   /* The DB handle for managing the working copy state.  */
167   svn_wc__db_t *db;
168 
169   /* Array of file extension patterns to preserve as extensions in
170      generated conflict files. */
171   const apr_array_header_t *ext_patterns;
172 
173   /* Hash mapping const char * absolute working copy paths to depth-first
174      ordered arrays of svn_prop_inherited_item_t * structures representing
175      the properties inherited by the base node at that working copy path.
176      May be NULL. */
177   apr_hash_t *wcroot_iprops;
178 
179   /* The revision we're targeting...or something like that.  This
180      starts off as a pointer to the revision to which we are updating,
181      or SVN_INVALID_REVNUM, but by the end of the edit, should be
182      pointing to the final revision. */
183   svn_revnum_t *target_revision;
184 
185   /* The requested depth of this edit. */
186   svn_depth_t requested_depth;
187 
188   /* Is the requested depth merely an operational limitation, or is
189      also the new sticky ambient depth of the update target? */
190   svn_boolean_t depth_is_sticky;
191 
192   /* Need to know if the user wants us to overwrite the 'now' times on
193      edited/added files with the last-commit-time. */
194   svn_boolean_t use_commit_times;
195 
196   /* Was the root actually opened (was this a non-empty edit)? */
197   svn_boolean_t root_opened;
198 
199   /* Was the update-target deleted?  This is a special situation. */
200   svn_boolean_t target_deleted;
201 
202   /* Allow unversioned obstructions when adding a path. */
203   svn_boolean_t allow_unver_obstructions;
204 
205   /* Handle local additions as modifications of new nodes */
206   svn_boolean_t adds_as_modification;
207 
208   /* If set, we check out into an empty directory. This allows for a number
209      of conflict checks to be omitted. */
210   svn_boolean_t clean_checkout;
211 
212   /* If this is a 'switch' operation, the new relpath of target_abspath,
213      else NULL. */
214   const char *switch_repos_relpath;
215 
216   /* The URL to the root of the repository. */
217   const char *repos_root;
218 
219   /* The UUID of the repos, or NULL. */
220   const char *repos_uuid;
221 
222   /* External diff3 to use for merges (can be null, in which case
223      internal merge code is used). */
224   const char *diff3_cmd;
225 
226   /* Externals handler */
227   svn_wc_external_update_t external_func;
228   void *external_baton;
229 
230   /* This editor sends back notifications as it edits. */
231   svn_wc_notify_func2_t notify_func;
232   void *notify_baton;
233 
234   /* This editor is normally wrapped in a cancellation editor anyway,
235      so it doesn't bother to check for cancellation itself.  However,
236      it needs a cancel_func and cancel_baton available to pass to
237      long-running functions. */
238   svn_cancel_func_t cancel_func;
239   void *cancel_baton;
240 
241   /* This editor will invoke a interactive conflict-resolution
242      callback, if available. */
243   svn_wc_conflict_resolver_func2_t conflict_func;
244   void *conflict_baton;
245 
246   /* Subtrees that were skipped during the edit, and therefore shouldn't
247      have their revision/url info updated at the end.  If a path is a
248      directory, its descendants will also be skipped.  The keys are paths
249      relative to the working copy root and the values unspecified. */
250   apr_hash_t *skipped_trees;
251 
252   /* A mapping from const char * repos_relpaths to the apr_hash_t * instances
253      returned from fetch_dirents_func for that repos_relpath. These
254      are used to avoid issue #3569 in specific update scenarios where a
255      restricted depth is used. */
256   apr_hash_t *dir_dirents;
257 
258   /* Absolute path of the working copy root or NULL if not initialized yet */
259   const char *wcroot_abspath;
260 
261   /* After closing the root directory a copy of its edited value */
262   svn_boolean_t edited;
263 
264   apr_pool_t *pool;
265 };
266 
267 
268 /* Record in the edit baton EB that LOCAL_ABSPATH's base version is not being
269  * updated.
270  *
271  * Add to EB->skipped_trees a copy (allocated in EB->pool) of the string
272  * LOCAL_ABSPATH.
273  */
274 static svn_error_t *
remember_skipped_tree(struct edit_baton * eb,const char * local_abspath,apr_pool_t * scratch_pool)275 remember_skipped_tree(struct edit_baton *eb,
276                       const char *local_abspath,
277                       apr_pool_t *scratch_pool)
278 {
279   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
280 
281   svn_hash_sets(eb->skipped_trees,
282                 apr_pstrdup(eb->pool,
283                             svn_dirent_skip_ancestor(eb->wcroot_abspath,
284                                                      local_abspath)),
285                 (void *)1);
286 
287   return SVN_NO_ERROR;
288 }
289 
290 /* Per directory baton. Lives in its own subpool of the parent directory
291    or of the edit baton if there is no parent directory */
292 struct dir_baton
293 {
294   /* Basename of this directory. */
295   const char *name;
296 
297   /* Absolute path of this directory */
298   const char *local_abspath;
299 
300   /* The repository relative path this directory will correspond to. */
301   const char *new_repos_relpath;
302 
303   /* The revision of the directory before updating */
304   svn_revnum_t old_revision;
305 
306   /* The repos_relpath before updating/switching */
307   const char *old_repos_relpath;
308 
309   /* The global edit baton. */
310   struct edit_baton *edit_baton;
311 
312   /* Baton for this directory's parent, or NULL if this is the root
313      directory. */
314   struct dir_baton *parent_baton;
315 
316   /* Set if updates to this directory are skipped */
317   svn_boolean_t skip_this;
318 
319   /* Set if there was a previous notification for this directory */
320   svn_boolean_t already_notified;
321 
322   /* Set if this directory is being added during this editor drive. */
323   svn_boolean_t adding_dir;
324 
325   /* Set on a node and its descendants are not present in the working copy
326      but should still be updated (not skipped). These nodes should all be
327      marked as deleted. */
328   svn_boolean_t shadowed;
329 
330   /* Set on a node when the existing node is obstructed, and the edit operation
331      continues as semi-shadowed update */
332   svn_boolean_t edit_obstructed;
333 
334   /* The (new) changed_* information, cached to avoid retrieving it later */
335   svn_revnum_t changed_rev;
336   apr_time_t changed_date;
337   const char *changed_author;
338 
339   /* If not NULL, contains a mapping of const char* basenames of children that
340      have been deleted to their svn_skel_t* tree conflicts.
341      We store this hash to allow replacements to continue under a just
342      installed tree conflict.
343 
344      The add after the delete will then update the tree conflicts information
345      and reinstall it. */
346   apr_hash_t *deletion_conflicts;
347 
348   /* A hash of file names (only the hash key matters) seen by add_file and
349      add_directory and not yet added to the database, mapping to a const
350      char * node kind (via svn_node_kind_to_word(). */
351   apr_hash_t *not_present_nodes;
352 
353   /* Set if an unversioned dir of the same name already existed in
354      this directory. */
355   svn_boolean_t obstruction_found;
356 
357   /* Set if a dir of the same name already exists and is
358      scheduled for addition without history. */
359   svn_boolean_t add_existed;
360 
361   /* An array of svn_prop_t structures, representing all the property
362      changes to be applied to this directory. */
363   apr_array_header_t *propchanges;
364 
365   /* A boolean indicating whether this node or one of its children has
366      received any 'real' changes. Used to avoid tree conflicts for simple
367      entryprop changes, like lock management */
368   svn_boolean_t edited;
369 
370   /* The tree conflict to install once the node is really edited */
371   svn_skel_t *edit_conflict;
372 
373   /* The bump information for this directory. */
374   struct bump_dir_info *bump_info;
375 
376   /* The depth of the directory in the wc (or inferred if added).  Not
377      used for filtering; we have a separate wrapping editor for that. */
378   svn_depth_t ambient_depth;
379 
380   /* Was the directory marked as incomplete before the update?
381      (In other words, are we resuming an interrupted update?)
382 
383      If WAS_INCOMPLETE is set to TRUE we expect to receive all child nodes
384      and properties for/of the directory. If WAS_INCOMPLETE is FALSE then
385      we only receive the changes in/for children and properties.*/
386   svn_boolean_t was_incomplete;
387 
388   /* The pool in which this baton itself is allocated. */
389   apr_pool_t *pool;
390 
391   /* how many nodes are referring to baton? */
392   int ref_count;
393 
394 };
395 
396 
397 struct handler_baton
398 {
399   svn_txdelta_window_handler_t apply_handler;
400   void *apply_baton;
401   apr_pool_t *pool;
402   struct file_baton *fb;
403 
404   /* Where we are assembling the new file. */
405   svn_wc__db_install_data_t *install_data;
406 
407     /* The expected source checksum of the text source or NULL if no base
408      checksum is available (MD5 if the server provides a checksum, SHA1 if
409      the server doesn't) */
410   svn_checksum_t *expected_source_checksum;
411 
412   /* Why two checksums?
413      The editor currently provides an md5 which we use to detect corruption
414      during transmission.  We use the sha1 inside libsvn_wc both for pristine
415      handling and corruption detection.  In the future, the editor will also
416      provide a sha1, so we may not have to calculate both, but for the time
417      being, that's the way it is. */
418 
419   /* The calculated checksum of the text source or NULL if the actual
420      checksum is not being calculated. The checksum kind is identical to the
421      kind of expected_source_checksum. */
422   svn_checksum_t *actual_source_checksum;
423 
424   /* The stream used to calculate the source checksums */
425   svn_stream_t *source_checksum_stream;
426 
427   /* A calculated MD5 digest of NEW_TEXT_BASE_TMP_ABSPATH.
428      This is initialized to all zeroes when the baton is created, then
429      populated with the MD5 digest of the resultant fulltext after the
430      last window is handled by the handler returned from
431      apply_textdelta(). */
432   unsigned char new_text_base_md5_digest[APR_MD5_DIGESTSIZE];
433 
434   /* A calculated SHA-1 of NEW_TEXT_BASE_TMP_ABSPATH, which we'll use for
435      eventually writing the pristine. */
436   svn_checksum_t * new_text_base_sha1_checksum;
437 };
438 
439 
440 /* Get an empty file in the temporary area for WRI_ABSPATH.  The file will
441    not be set for automatic deletion, and the name will be returned in
442    TMP_FILENAME.
443 
444    This implementation creates a new empty file with a unique name.
445 
446    ### This is inefficient for callers that just want an empty file to read
447    ### from.  There could be (and there used to be) a permanent, shared
448    ### empty file for this purpose.
449 
450    ### This is inefficient for callers that just want to reserve a unique
451    ### file name to create later.  A better way may not be readily available.
452  */
453 static svn_error_t *
get_empty_tmp_file(const char ** tmp_filename,svn_wc__db_t * db,const char * wri_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)454 get_empty_tmp_file(const char **tmp_filename,
455                    svn_wc__db_t *db,
456                    const char *wri_abspath,
457                    apr_pool_t *result_pool,
458                    apr_pool_t *scratch_pool)
459 {
460   const char *temp_dir_abspath;
461 
462   SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db, wri_abspath,
463                                          scratch_pool, scratch_pool));
464   SVN_ERR(svn_io_open_unique_file3(NULL, tmp_filename, temp_dir_abspath,
465                                    svn_io_file_del_none,
466                                    scratch_pool, scratch_pool));
467 
468   return SVN_NO_ERROR;
469 }
470 
471 /* An APR pool cleanup handler.  This runs the working queue for an
472    editor baton. */
473 static apr_status_t
cleanup_edit_baton(void * edit_baton)474 cleanup_edit_baton(void *edit_baton)
475 {
476   struct edit_baton *eb = edit_baton;
477   svn_error_t *err;
478   apr_pool_t *pool = apr_pool_parent_get(eb->pool);
479 
480   err = svn_wc__wq_run(eb->db, eb->wcroot_abspath,
481                        NULL /* cancel_func */, NULL /* cancel_baton */,
482                        pool);
483 
484   if (err)
485     {
486       apr_status_t apr_err = err->apr_err;
487       svn_error_clear(err);
488       return apr_err;
489     }
490   return APR_SUCCESS;
491 }
492 
493 /* Calculate the new repos_relpath for a directory or file */
494 static svn_error_t *
calculate_repos_relpath(const char ** new_repos_relpath,const char * local_abspath,const char * old_repos_relpath,struct edit_baton * eb,struct dir_baton * pb,apr_pool_t * result_pool,apr_pool_t * scratch_pool)495 calculate_repos_relpath(const char **new_repos_relpath,
496                         const char *local_abspath,
497                         const char *old_repos_relpath,
498                         struct edit_baton *eb,
499                         struct dir_baton *pb,
500                         apr_pool_t *result_pool,
501                         apr_pool_t *scratch_pool)
502 {
503   const char *name = svn_dirent_basename(local_abspath, NULL);
504 
505   /* Figure out the new_repos_relpath for this directory. */
506   if (eb->switch_repos_relpath)
507     {
508       /* Handle switches... */
509 
510       if (pb == NULL)
511         {
512           if (*eb->target_basename == '\0')
513             {
514               /* No parent baton and target_basename=="" means that we are
515                  the target of the switch. Thus, our new_repos_relpath will be
516                  the switch_repos_relpath.  */
517               *new_repos_relpath = eb->switch_repos_relpath;
518             }
519           else
520             {
521               /* This node is NOT the target of the switch (one of our
522                  children is the target); therefore, it must already exist.
523                  Get its old REPOS_RELPATH, as it won't be changing.  */
524               *new_repos_relpath = apr_pstrdup(result_pool, old_repos_relpath);
525             }
526         }
527       else
528         {
529           /* This directory is *not* the root (has a parent). If there is
530              no grandparent, then we may have anchored at the parent,
531              and self is the target. If we match the target, then set
532              new_repos_relpath to the switch_repos_relpath.
533 
534              Otherwise, we simply extend new_repos_relpath from the parent.  */
535 
536           if (pb->parent_baton == NULL
537               && strcmp(eb->target_basename, name) == 0)
538             *new_repos_relpath = eb->switch_repos_relpath;
539           else
540             *new_repos_relpath = svn_relpath_join(pb->new_repos_relpath, name,
541                                                   result_pool);
542         }
543     }
544   else  /* must be an update */
545     {
546       /* If we are adding the node, then simply extend the parent's
547          relpath for our own.  */
548       if (old_repos_relpath == NULL)
549         {
550           SVN_ERR_ASSERT(pb != NULL);
551           *new_repos_relpath = svn_relpath_join(pb->new_repos_relpath, name,
552                                                 result_pool);
553         }
554       else
555         {
556           *new_repos_relpath = apr_pstrdup(result_pool, old_repos_relpath);
557         }
558     }
559 
560   return SVN_NO_ERROR;
561 }
562 
563 /* Make a new dir baton in a subpool of PB->pool. PB is the parent baton.
564    If PATH and PB are NULL, this is the root directory of the edit; in this
565    case, make the new dir baton in a subpool of EB->pool.
566    ADDING should be TRUE if we are adding this directory.  */
567 static svn_error_t *
make_dir_baton(struct dir_baton ** d_p,const char * path,struct edit_baton * eb,struct dir_baton * pb,svn_boolean_t adding,apr_pool_t * scratch_pool)568 make_dir_baton(struct dir_baton **d_p,
569                const char *path,
570                struct edit_baton *eb,
571                struct dir_baton *pb,
572                svn_boolean_t adding,
573                apr_pool_t *scratch_pool)
574 {
575   apr_pool_t *dir_pool;
576   struct dir_baton *d;
577 
578   if (pb != NULL)
579     dir_pool = svn_pool_create(pb->pool);
580   else
581     dir_pool = svn_pool_create(eb->pool);
582 
583   SVN_ERR_ASSERT(path || (! pb));
584 
585   /* Okay, no easy out, so allocate and initialize a dir baton. */
586   d = apr_pcalloc(dir_pool, sizeof(*d));
587 
588   /* Construct the PATH and baseNAME of this directory. */
589   if (path)
590     {
591       d->name = svn_dirent_basename(path, dir_pool);
592       SVN_ERR(path_join_under_root(&d->local_abspath,
593                                    pb->local_abspath, d->name, dir_pool));
594     }
595   else
596     {
597       /* This is the root baton. */
598       d->name = NULL;
599       d->local_abspath = eb->anchor_abspath;
600     }
601 
602   d->edit_baton   = eb;
603   d->parent_baton = pb;
604   d->pool         = dir_pool;
605   d->propchanges  = apr_array_make(dir_pool, 1, sizeof(svn_prop_t));
606   d->obstruction_found = FALSE;
607   d->add_existed  = FALSE;
608   d->ref_count = 1;
609   d->old_revision = SVN_INVALID_REVNUM;
610   d->adding_dir   = adding;
611   d->changed_rev  = SVN_INVALID_REVNUM;
612   d->not_present_nodes = apr_hash_make(dir_pool);
613 
614   /* Copy some flags from the parent baton */
615   if (pb)
616     {
617       d->skip_this = pb->skip_this;
618       d->shadowed = pb->shadowed || pb->edit_obstructed;
619 
620       /* the parent's bump info has one more referer */
621       pb->ref_count++;
622     }
623 
624   /* The caller of this function needs to fill these in. */
625   d->ambient_depth = svn_depth_unknown;
626   d->was_incomplete = FALSE;
627 
628   *d_p = d;
629   return SVN_NO_ERROR;
630 }
631 
632 /* Forward declarations. */
633 static svn_error_t *
634 already_in_a_tree_conflict(svn_boolean_t *conflicted,
635                            svn_boolean_t *ignored,
636                            svn_wc__db_t *db,
637                            const char *local_abspath,
638                            apr_pool_t *scratch_pool);
639 
640 
641 static void
do_notification(const struct edit_baton * eb,const char * local_abspath,svn_node_kind_t kind,svn_wc_notify_action_t action,apr_pool_t * scratch_pool)642 do_notification(const struct edit_baton *eb,
643                 const char *local_abspath,
644                 svn_node_kind_t kind,
645                 svn_wc_notify_action_t action,
646                 apr_pool_t *scratch_pool)
647 {
648   svn_wc_notify_t *notify;
649 
650   if (eb->notify_func == NULL)
651     return;
652 
653   notify = svn_wc_create_notify(local_abspath, action, scratch_pool);
654   notify->kind = kind;
655 
656   (*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
657 }
658 
659 /* Decrement the directory's reference count. If it hits zero,
660    then this directory is "done". This means it is safe to clear its pool.
661 
662    In addition, when the directory is "done", we recurse to possible cleanup
663    the parent directory.
664 */
665 static svn_error_t *
maybe_release_dir_info(struct dir_baton * db)666 maybe_release_dir_info(struct dir_baton *db)
667 {
668   db->ref_count--;
669 
670   if (!db->ref_count)
671     {
672       struct dir_baton *pb = db->parent_baton;
673 
674       svn_pool_destroy(db->pool);
675 
676       if (pb)
677         SVN_ERR(maybe_release_dir_info(pb));
678     }
679 
680   return SVN_NO_ERROR;
681 }
682 
683 /* Per file baton. Lives in its own subpool below the pool of the parent
684    directory */
685 struct file_baton
686 {
687   /* Pool specific to this file_baton. */
688   apr_pool_t *pool;
689 
690   /* Name of this file (its entry in the directory). */
691   const char *name;
692 
693   /* Absolute path to this file */
694   const char *local_abspath;
695 
696   /* The repository relative path this file will correspond to. */
697   const char *new_repos_relpath;
698 
699   /* The revision of the file before updating */
700   svn_revnum_t old_revision;
701 
702   /* The repos_relpath before updating/switching */
703   const char *old_repos_relpath;
704 
705   /* The global edit baton. */
706   struct edit_baton *edit_baton;
707 
708   /* The parent directory of this file. */
709   struct dir_baton *dir_baton;
710 
711   /* Set if updates to this directory are skipped */
712   svn_boolean_t skip_this;
713 
714   /* Set if there was a previous notification  */
715   svn_boolean_t already_notified;
716 
717   /* Set if this file is new. */
718   svn_boolean_t adding_file;
719 
720   /* Set if an unversioned file of the same name already existed in
721      this directory. */
722   svn_boolean_t obstruction_found;
723 
724   /* Set if a file of the same name already exists and is
725      scheduled for addition without history. */
726   svn_boolean_t add_existed;
727 
728   /* Set if this file is being added in the BASE layer, but is not-present
729      in the working copy (replaced, deleted, etc.). */
730   svn_boolean_t shadowed;
731 
732   /* Set on a node when the existing node is obstructed, and the edit operation
733      continues as semi-shadowed update */
734   svn_boolean_t edit_obstructed;
735 
736   /* The (new) changed_* information, cached to avoid retrieving it later */
737   svn_revnum_t changed_rev;
738   apr_time_t changed_date;
739   const char *changed_author;
740 
741   /* If there are file content changes, these are the checksums of the
742      resulting new text base, which is in the pristine store, else NULL. */
743   const svn_checksum_t *new_text_base_md5_checksum;
744   const svn_checksum_t *new_text_base_sha1_checksum;
745 
746   /* The checksum of the file before the update */
747   const svn_checksum_t *original_checksum;
748 
749   /* An array of svn_prop_t structures, representing all the property
750      changes to be applied to this file.  Once a file baton is
751      initialized, this is never NULL, but it may have zero elements.  */
752   apr_array_header_t *propchanges;
753 
754   /* For existing files, whether there are local modifications. FALSE for added
755      files */
756   svn_boolean_t local_prop_mods;
757 
758   /* Bump information for the directory this file lives in */
759   struct bump_dir_info *bump_info;
760 
761   /* A boolean indicating whether this node or one of its children has
762      received any 'real' changes. Used to avoid tree conflicts for simple
763      entryprop changes, like lock management */
764   svn_boolean_t edited;
765 
766   /* The tree conflict to install once the node is really edited */
767   svn_skel_t *edit_conflict;
768 };
769 
770 
771 /* Make a new file baton in a subpool of PB->pool. PB is the parent baton.
772  * PATH is relative to the root of the edit. ADDING tells whether this file
773  * is being added. */
774 static svn_error_t *
make_file_baton(struct file_baton ** f_p,struct dir_baton * pb,const char * path,svn_boolean_t adding,apr_pool_t * scratch_pool)775 make_file_baton(struct file_baton **f_p,
776                 struct dir_baton *pb,
777                 const char *path,
778                 svn_boolean_t adding,
779                 apr_pool_t *scratch_pool)
780 {
781   apr_pool_t *file_pool = svn_pool_create(pb->pool);
782   struct file_baton *f = apr_pcalloc(file_pool, sizeof(*f));
783 
784   SVN_ERR_ASSERT(path);
785 
786   /* Make the file's on-disk name. */
787   f->name = svn_dirent_basename(path, file_pool);
788   f->old_revision = SVN_INVALID_REVNUM;
789   SVN_ERR(path_join_under_root(&f->local_abspath,
790                                pb->local_abspath, f->name, file_pool));
791 
792   f->pool              = file_pool;
793   f->edit_baton        = pb->edit_baton;
794   f->propchanges       = apr_array_make(file_pool, 1, sizeof(svn_prop_t));
795   f->bump_info         = pb->bump_info;
796   f->adding_file       = adding;
797   f->obstruction_found = FALSE;
798   f->add_existed       = FALSE;
799   f->skip_this         = pb->skip_this;
800   f->shadowed          = pb->shadowed || pb->edit_obstructed;
801   f->dir_baton         = pb;
802   f->changed_rev       = SVN_INVALID_REVNUM;
803 
804   /* the directory has one more referer now */
805   pb->ref_count++;
806 
807   *f_p = f;
808   return SVN_NO_ERROR;
809 }
810 
811 /* Complete a conflict skel by describing the update.
812  *
813  * LOCAL_KIND is the node kind of the tree conflict victim in the
814  * working copy.
815  *
816  * All temporary allocations are be made in SCRATCH_POOL, while allocations
817  * needed for the returned conflict struct are made in RESULT_POOL.
818  */
819 static svn_error_t *
complete_conflict(svn_skel_t * conflict,const struct edit_baton * eb,const char * local_abspath,const char * old_repos_relpath,svn_revnum_t old_revision,const char * new_repos_relpath,svn_node_kind_t local_kind,svn_node_kind_t target_kind,const svn_skel_t * delete_conflict,apr_pool_t * result_pool,apr_pool_t * scratch_pool)820 complete_conflict(svn_skel_t *conflict,
821                   const struct edit_baton *eb,
822                   const char *local_abspath,
823                   const char *old_repos_relpath,
824                   svn_revnum_t old_revision,
825                   const char *new_repos_relpath,
826                   svn_node_kind_t local_kind,
827                   svn_node_kind_t target_kind,
828                   const svn_skel_t *delete_conflict,
829                   apr_pool_t *result_pool,
830                   apr_pool_t *scratch_pool)
831 {
832   const svn_wc_conflict_version_t *original_version = NULL;
833   svn_wc_conflict_version_t *target_version;
834   svn_boolean_t is_complete;
835 
836   SVN_ERR_ASSERT(new_repos_relpath);
837 
838   if (!conflict)
839     return SVN_NO_ERROR; /* Not conflicted */
840 
841   SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict));
842 
843   if (is_complete)
844     return SVN_NO_ERROR; /* Already completed */
845 
846   if (old_repos_relpath)
847     original_version = svn_wc_conflict_version_create2(eb->repos_root,
848                                                        eb->repos_uuid,
849                                                        old_repos_relpath,
850                                                        old_revision,
851                                                        local_kind,
852                                                        result_pool);
853   else if (delete_conflict)
854     {
855       const apr_array_header_t *locations;
856 
857       SVN_ERR(svn_wc__conflict_read_info(NULL, &locations, NULL, NULL, NULL,
858                                          eb->db, local_abspath,
859                                          delete_conflict,
860                                          scratch_pool, scratch_pool));
861 
862       if (locations)
863         {
864           original_version = APR_ARRAY_IDX(locations, 0,
865                                            const svn_wc_conflict_version_t *);
866         }
867     }
868 
869   target_version = svn_wc_conflict_version_create2(eb->repos_root,
870                                                    eb->repos_uuid,
871                                                    new_repos_relpath,
872                                                    *eb->target_revision,
873                                                    target_kind,
874                                                    result_pool);
875 
876   if (eb->switch_repos_relpath)
877     SVN_ERR(svn_wc__conflict_skel_set_op_switch(conflict,
878                                                 original_version,
879                                                 target_version,
880                                                 result_pool, scratch_pool));
881   else
882     SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict,
883                                                 original_version,
884                                                 target_version,
885                                                 result_pool, scratch_pool));
886 
887   return SVN_NO_ERROR;
888 }
889 
890 
891 /* Called when a directory is really edited, to avoid marking a
892    tree conflict on a node for a no-change edit */
893 static svn_error_t *
mark_directory_edited(struct dir_baton * db,apr_pool_t * scratch_pool)894 mark_directory_edited(struct dir_baton *db, apr_pool_t *scratch_pool)
895 {
896   if (db->edited)
897     return SVN_NO_ERROR;
898 
899   if (db->parent_baton)
900     SVN_ERR(mark_directory_edited(db->parent_baton, scratch_pool));
901 
902   db->edited = TRUE;
903 
904   if (db->edit_conflict)
905     {
906       /* We have a (delayed) tree conflict to install */
907 
908       SVN_ERR(complete_conflict(db->edit_conflict, db->edit_baton,
909                                 db->local_abspath,
910                                 db->old_repos_relpath, db->old_revision,
911                                 db->new_repos_relpath,
912                                 svn_node_dir, svn_node_dir,
913                                 NULL,
914                                 db->pool, scratch_pool));
915       SVN_ERR(svn_wc__db_op_mark_conflict(db->edit_baton->db,
916                                           db->local_abspath,
917                                           db->edit_conflict, NULL,
918                                           scratch_pool));
919 
920       do_notification(db->edit_baton, db->local_abspath, svn_node_dir,
921                       svn_wc_notify_tree_conflict, scratch_pool);
922       db->already_notified = TRUE;
923     }
924 
925   return SVN_NO_ERROR;
926 }
927 
928 /* Called when a file is really edited, to avoid marking a
929    tree conflict on a node for a no-change edit */
930 static svn_error_t *
mark_file_edited(struct file_baton * fb,apr_pool_t * scratch_pool)931 mark_file_edited(struct file_baton *fb, apr_pool_t *scratch_pool)
932 {
933   if (fb->edited)
934     return SVN_NO_ERROR;
935 
936   SVN_ERR(mark_directory_edited(fb->dir_baton, scratch_pool));
937 
938   fb->edited = TRUE;
939 
940   if (fb->edit_conflict)
941     {
942       /* We have a (delayed) tree conflict to install */
943 
944       SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton,
945                                 fb->local_abspath, fb->old_repos_relpath,
946                                 fb->old_revision, fb->new_repos_relpath,
947                                 svn_node_file, svn_node_file,
948                                 NULL,
949                                 fb->pool, scratch_pool));
950 
951       SVN_ERR(svn_wc__db_op_mark_conflict(fb->edit_baton->db,
952                                           fb->local_abspath,
953                                           fb->edit_conflict, NULL,
954                                           scratch_pool));
955 
956       do_notification(fb->edit_baton, fb->local_abspath, svn_node_file,
957                       svn_wc_notify_tree_conflict, scratch_pool);
958       fb->already_notified = TRUE;
959     }
960 
961   return SVN_NO_ERROR;
962 }
963 
964 
965 /* Handle the next delta window of the file described by BATON.  If it is
966  * the end (WINDOW == NULL), then check the checksum, store the text in the
967  * pristine store and write its details into BATON->fb->new_text_base_*. */
968 static svn_error_t *
window_handler(svn_txdelta_window_t * window,void * baton)969 window_handler(svn_txdelta_window_t *window, void *baton)
970 {
971   struct handler_baton *hb = baton;
972   struct file_baton *fb = hb->fb;
973   svn_error_t *err;
974 
975   /* Apply this window.  We may be done at that point.  */
976   err = hb->apply_handler(window, hb->apply_baton);
977   if (window != NULL && !err)
978     return SVN_NO_ERROR;
979 
980   if (hb->expected_source_checksum)
981     {
982       /* Close the stream to calculate HB->actual_source_md5_checksum. */
983       svn_error_t *err2 = svn_stream_close(hb->source_checksum_stream);
984 
985       if (!err2)
986         {
987           SVN_ERR_ASSERT(hb->expected_source_checksum->kind ==
988                         hb->actual_source_checksum->kind);
989 
990           if (!svn_checksum_match(hb->expected_source_checksum,
991                                   hb->actual_source_checksum))
992             {
993               err = svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, err,
994                         _("Checksum mismatch while updating '%s':\n"
995                           "   expected:  %s\n"
996                           "     actual:  %s\n"),
997                         svn_dirent_local_style(fb->local_abspath, hb->pool),
998                         svn_checksum_to_cstring(hb->expected_source_checksum,
999                                                 hb->pool),
1000                         svn_checksum_to_cstring(hb->actual_source_checksum,
1001                                                 hb->pool));
1002             }
1003         }
1004 
1005       err = svn_error_compose_create(err, err2);
1006     }
1007 
1008   if (err)
1009     {
1010       /* We failed to apply the delta; clean up the temporary file if it
1011          already created by lazy_open_target(). */
1012       if (hb->install_data)
1013         {
1014           svn_error_clear(svn_wc__db_pristine_install_abort(hb->install_data,
1015                                                             hb->pool));
1016         }
1017     }
1018   else
1019     {
1020       /* Tell the file baton about the new text base's checksums. */
1021       fb->new_text_base_md5_checksum =
1022         svn_checksum__from_digest_md5(hb->new_text_base_md5_digest, fb->pool);
1023       fb->new_text_base_sha1_checksum =
1024         svn_checksum_dup(hb->new_text_base_sha1_checksum, fb->pool);
1025 
1026       /* Store the new pristine text in the pristine store now.  Later, in a
1027          single transaction we will update the BASE_NODE to include a
1028          reference to this pristine text's checksum. */
1029       SVN_ERR(svn_wc__db_pristine_install(hb->install_data,
1030                                           fb->new_text_base_sha1_checksum,
1031                                           fb->new_text_base_md5_checksum,
1032                                           hb->pool));
1033     }
1034 
1035   svn_pool_destroy(hb->pool);
1036 
1037   return err;
1038 }
1039 
1040 
1041 /* Find the last-change info within ENTRY_PROPS, and return then in the
1042    CHANGED_* parameters. Each parameter will be initialized to its "none"
1043    value, and will contain the relavent info if found.
1044 
1045    CHANGED_AUTHOR will be allocated in RESULT_POOL. SCRATCH_POOL will be
1046    used for some temporary allocations.
1047 */
1048 static svn_error_t *
accumulate_last_change(svn_revnum_t * changed_rev,apr_time_t * changed_date,const char ** changed_author,const apr_array_header_t * entry_props,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1049 accumulate_last_change(svn_revnum_t *changed_rev,
1050                        apr_time_t *changed_date,
1051                        const char **changed_author,
1052                        const apr_array_header_t *entry_props,
1053                        apr_pool_t *result_pool,
1054                        apr_pool_t *scratch_pool)
1055 {
1056   int i;
1057 
1058   *changed_rev = SVN_INVALID_REVNUM;
1059   *changed_date = 0;
1060   *changed_author = NULL;
1061 
1062   for (i = 0; i < entry_props->nelts; ++i)
1063     {
1064       const svn_prop_t *prop = &APR_ARRAY_IDX(entry_props, i, svn_prop_t);
1065 
1066       /* A prop value of NULL means the information was not
1067          available.  We don't remove this field from the entries
1068          file; we have convention just leave it empty.  So let's
1069          just skip those entry props that have no values. */
1070       if (! prop->value)
1071         continue;
1072 
1073       if (! strcmp(prop->name, SVN_PROP_ENTRY_LAST_AUTHOR))
1074         *changed_author = apr_pstrdup(result_pool, prop->value->data);
1075       else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_REV))
1076         {
1077           apr_int64_t rev;
1078           SVN_ERR(svn_cstring_atoi64(&rev, prop->value->data));
1079           *changed_rev = (svn_revnum_t)rev;
1080         }
1081       else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_DATE))
1082         SVN_ERR(svn_time_from_cstring(changed_date, prop->value->data,
1083                                       scratch_pool));
1084 
1085       /* Starting with Subversion 1.7 we ignore the SVN_PROP_ENTRY_UUID
1086          property here. */
1087     }
1088 
1089   return SVN_NO_ERROR;
1090 }
1091 
1092 
1093 /* Join ADD_PATH to BASE_PATH.  If ADD_PATH is absolute, or if any ".."
1094  * component of it resolves to a path above BASE_PATH, then return
1095  * SVN_ERR_WC_OBSTRUCTED_UPDATE.
1096  *
1097  * This is to prevent the situation where the repository contains,
1098  * say, "..\nastyfile".  Although that's perfectly legal on some
1099  * systems, when checked out onto Win32 it would cause "nastyfile" to
1100  * be created in the parent of the current edit directory.
1101  *
1102  * (http://cve.mitre.org/cgi-bin/cvename.cgi?name=2007-3846)
1103  */
1104 static svn_error_t *
path_join_under_root(const char ** result_path,const char * base_path,const char * add_path,apr_pool_t * pool)1105 path_join_under_root(const char **result_path,
1106                      const char *base_path,
1107                      const char *add_path,
1108                      apr_pool_t *pool)
1109 {
1110   svn_boolean_t under_root;
1111 
1112   SVN_ERR(svn_dirent_is_under_root(&under_root,
1113                                    result_path, base_path, add_path, pool));
1114 
1115   if (! under_root)
1116     {
1117       return svn_error_createf(
1118           SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
1119           _("Path '%s' is not in the working copy"),
1120           svn_dirent_local_style(svn_dirent_join(base_path, add_path, pool),
1121                                  pool));
1122     }
1123 
1124   /* This catches issue #3288 */
1125   if (strcmp(add_path, svn_dirent_basename(*result_path, NULL)) != 0)
1126     {
1127       return svn_error_createf(
1128           SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
1129           _("'%s' is not valid as filename in directory '%s'"),
1130           svn_dirent_local_style(add_path, pool),
1131           svn_dirent_local_style(base_path, pool));
1132     }
1133 
1134   return SVN_NO_ERROR;
1135 }
1136 
1137 
1138 /*** The callbacks we'll plug into an svn_delta_editor_t structure. ***/
1139 
1140 /* An svn_delta_editor_t function. */
1141 static svn_error_t *
set_target_revision(void * edit_baton,svn_revnum_t target_revision,apr_pool_t * pool)1142 set_target_revision(void *edit_baton,
1143                     svn_revnum_t target_revision,
1144                     apr_pool_t *pool)
1145 {
1146   struct edit_baton *eb = edit_baton;
1147 
1148   *(eb->target_revision) = target_revision;
1149   return SVN_NO_ERROR;
1150 }
1151 
1152 /* An svn_delta_editor_t function. */
1153 static svn_error_t *
open_root(void * edit_baton,svn_revnum_t base_revision,apr_pool_t * pool,void ** dir_baton)1154 open_root(void *edit_baton,
1155           svn_revnum_t base_revision, /* This is ignored in co */
1156           apr_pool_t *pool,
1157           void **dir_baton)
1158 {
1159   struct edit_baton *eb = edit_baton;
1160   struct dir_baton *db;
1161   svn_boolean_t already_conflicted, conflict_ignored;
1162   svn_error_t *err;
1163   svn_wc__db_status_t status;
1164   svn_wc__db_status_t base_status;
1165   svn_node_kind_t kind;
1166   svn_boolean_t have_work;
1167 
1168   /* Note that something interesting is actually happening in this
1169      edit run. */
1170   eb->root_opened = TRUE;
1171 
1172   SVN_ERR(make_dir_baton(&db, NULL, eb, NULL, FALSE, pool));
1173   *dir_baton = db;
1174 
1175   err = already_in_a_tree_conflict(&already_conflicted, &conflict_ignored,
1176                                    eb->db, db->local_abspath, pool);
1177 
1178   if (err)
1179     {
1180       if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1181         return svn_error_trace(err);
1182 
1183       svn_error_clear(err);
1184       already_conflicted = conflict_ignored = FALSE;
1185     }
1186   else if (already_conflicted)
1187     {
1188       /* Record a skip of both the anchor and target in the skipped tree
1189          as the anchor itself might not be updated */
1190       SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
1191       SVN_ERR(remember_skipped_tree(eb, eb->target_abspath, pool));
1192 
1193       db->skip_this = TRUE;
1194       db->already_notified = TRUE;
1195 
1196       /* Notify that we skipped the target, while we actually skipped
1197          the anchor */
1198       do_notification(eb, eb->target_abspath, svn_node_unknown,
1199                       svn_wc_notify_skip_conflicted, pool);
1200 
1201       return SVN_NO_ERROR;
1202     }
1203 
1204 
1205   SVN_ERR(svn_wc__db_read_info(&status, &kind, &db->old_revision,
1206                                &db->old_repos_relpath, NULL, NULL,
1207                                &db->changed_rev, &db->changed_date,
1208                                &db->changed_author, &db->ambient_depth,
1209                                NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1210                                NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1211                                NULL, NULL, &have_work,
1212                                eb->db, db->local_abspath,
1213                                db->pool, pool));
1214 
1215   if (have_work)
1216     {
1217       SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL,
1218                                        &db->old_revision,
1219                                        &db->old_repos_relpath, NULL, NULL,
1220                                        &db->changed_rev, &db->changed_date,
1221                                        &db->changed_author,
1222                                        &db->ambient_depth,
1223                                        NULL, NULL, NULL, NULL, NULL, NULL,
1224                                        eb->db, db->local_abspath,
1225                                        db->pool, pool));
1226     }
1227   else
1228     base_status = status;
1229 
1230   SVN_ERR(calculate_repos_relpath(&db->new_repos_relpath, db->local_abspath,
1231                                   db->old_repos_relpath, eb, NULL,
1232                                   db->pool, pool));
1233 
1234   if (conflict_ignored)
1235     db->shadowed = TRUE;
1236   else if (have_work)
1237     {
1238       const char *move_dst_op_root_abspath;
1239       const char *move_src_root_abspath;
1240 
1241       SVN_ERR(svn_wc__db_base_moved_to(NULL, &move_dst_op_root_abspath,
1242                                        &move_src_root_abspath,
1243                                        NULL, eb->db, db->local_abspath,
1244                                        pool, pool));
1245 
1246       if (move_src_root_abspath)
1247         {
1248           /* This is an update anchored inside a move. We need to
1249              raise a move-edit tree-conflict on the move root to
1250              update the move destination. */
1251           svn_skel_t *tree_conflict = svn_wc__conflict_skel_create(pool);
1252 
1253           SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
1254                     tree_conflict, eb->db, move_src_root_abspath,
1255                     svn_wc_conflict_reason_moved_away,
1256                     svn_wc_conflict_action_edit,
1257                     move_src_root_abspath,
1258                     move_dst_op_root_abspath, pool, pool));
1259 
1260           if (strcmp(db->local_abspath, move_src_root_abspath))
1261             {
1262               /* We are raising the tree-conflict on some parent of
1263                  the edit root, we won't be handling that path again
1264                  so raise the conflict now. */
1265               SVN_ERR(complete_conflict(tree_conflict, eb,
1266                                         move_src_root_abspath,
1267                                         db->old_repos_relpath,
1268                                         db->old_revision,
1269                                         db->new_repos_relpath,
1270                                         svn_node_dir, svn_node_dir,
1271                                         NULL, pool, pool));
1272               SVN_ERR(svn_wc__db_op_mark_conflict(eb->db,
1273                                                   move_src_root_abspath,
1274                                                   tree_conflict,
1275                                                   NULL, pool));
1276               do_notification(eb, move_src_root_abspath, svn_node_dir,
1277                               svn_wc_notify_tree_conflict, pool);
1278             }
1279           else
1280             db->edit_conflict = tree_conflict;
1281         }
1282 
1283       db->shadowed = TRUE; /* Needed for the close_directory() on the root, to
1284                               make sure it doesn't use the ACTUAL tree */
1285     }
1286 
1287   if (*eb->target_basename == '\0')
1288     {
1289       /* For an update with a NULL target, this is equivalent to open_dir(): */
1290 
1291       db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
1292 
1293       /* ### TODO: Add some tree conflict and obstruction detection, etc. like
1294                    open_directory() does.
1295                    (or find a way to reuse that code here)
1296 
1297          ### BH 2013: I don't think we need all of the detection here, as the
1298                       user explicitly asked to update this node. So we don't
1299                       have to tell that it is a local replacement/delete.
1300        */
1301 
1302       SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db,
1303                                                         db->local_abspath,
1304                                                         db->new_repos_relpath,
1305                                                         *eb->target_revision,
1306                                                         pool));
1307     }
1308 
1309   return SVN_NO_ERROR;
1310 }
1311 
1312 
1313 /* ===================================================================== */
1314 /* Checking for local modifications. */
1315 
1316 /* Indicates an unset svn_wc_conflict_reason_t. */
1317 #define SVN_WC_CONFLICT_REASON_NONE (svn_wc_conflict_reason_t)(-1)
1318 
1319 /* Check whether the incoming change ACTION on FULL_PATH would conflict with
1320  * LOCAL_ABSPATH's scheduled change. If so, then raise a tree conflict with
1321  * LOCAL_ABSPATH as the victim.
1322  *
1323  * The edit baton EB gives information including whether the operation is
1324  * an update or a switch.
1325  *
1326  * WORKING_STATUS is the current node status of LOCAL_ABSPATH
1327  * and EXISTS_IN_REPOS specifies whether a BASE_NODE representation for exists
1328  * for this node. In that case the on disk type is compared to EXPECTED_KIND.
1329  *
1330  * If a tree conflict reason was found for the incoming action, the resulting
1331  * tree conflict info is returned in *PCONFLICT. PCONFLICT must be non-NULL,
1332  * while *PCONFLICT is always overwritten.
1333  *
1334  * The tree conflict is allocated in RESULT_POOL. Temporary allocations use
1335  * SCRATCH_POOL.
1336  */
1337 static svn_error_t *
check_tree_conflict(svn_skel_t ** pconflict,struct edit_baton * eb,const char * local_abspath,svn_wc__db_status_t working_status,svn_boolean_t exists_in_repos,svn_node_kind_t expected_kind,svn_wc_conflict_action_t action,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1338 check_tree_conflict(svn_skel_t **pconflict,
1339                     struct edit_baton *eb,
1340                     const char *local_abspath,
1341                     svn_wc__db_status_t working_status,
1342                     svn_boolean_t exists_in_repos,
1343                     svn_node_kind_t expected_kind,
1344                     svn_wc_conflict_action_t action,
1345                     apr_pool_t *result_pool,
1346                     apr_pool_t *scratch_pool)
1347 {
1348   svn_wc_conflict_reason_t reason = SVN_WC_CONFLICT_REASON_NONE;
1349   svn_boolean_t modified = FALSE;
1350   const char *move_src_op_root_abspath = NULL;
1351   const char *move_dst_op_root_abspath = NULL;
1352 
1353   *pconflict = NULL;
1354 
1355   /* Find out if there are any local changes to this node that may
1356    * be the "reason" of a tree-conflict with the incoming "action". */
1357   switch (working_status)
1358     {
1359       case svn_wc__db_status_added:
1360       case svn_wc__db_status_moved_here:
1361       case svn_wc__db_status_copied:
1362         if (!exists_in_repos)
1363           {
1364             /* The node is locally added, and it did not exist before.  This
1365              * is an 'update', so the local add can only conflict with an
1366              * incoming 'add'.  In fact, if we receive anything else than an
1367              * svn_wc_conflict_action_add (which includes 'added',
1368              * 'copied-here' and 'moved-here') during update on a node that
1369              * did not exist before, then something is very wrong.
1370              * Note that if there was no action on the node, this code
1371              * would not have been called in the first place. */
1372             SVN_ERR_ASSERT(action == svn_wc_conflict_action_add);
1373 
1374             /* Scan the addition in case our caller didn't. */
1375             if (working_status == svn_wc__db_status_added)
1376               SVN_ERR(svn_wc__db_scan_addition(&working_status, NULL, NULL,
1377                                                NULL, NULL, NULL, NULL,
1378                                                NULL, NULL,
1379                                                eb->db, local_abspath,
1380                                                scratch_pool, scratch_pool));
1381 
1382             if (working_status == svn_wc__db_status_moved_here)
1383               reason = svn_wc_conflict_reason_moved_here;
1384             else
1385               reason = svn_wc_conflict_reason_added;
1386           }
1387         else
1388           {
1389             /* The node is locally replaced but could also be moved-away,
1390                but we can't report that it is moved away and replaced.
1391 
1392                And we wouldn't be able to store that each of a dozen
1393                descendants was moved to other locations...
1394 
1395                Replaced is what actually happened... */
1396 
1397             reason = svn_wc_conflict_reason_replaced;
1398           }
1399         break;
1400 
1401 
1402       case svn_wc__db_status_deleted:
1403         {
1404           SVN_ERR(svn_wc__db_base_moved_to(NULL, &move_dst_op_root_abspath,
1405                                            NULL, &move_src_op_root_abspath,
1406                                            eb->db, local_abspath,
1407                                            scratch_pool, scratch_pool));
1408           if (move_src_op_root_abspath)
1409             reason = svn_wc_conflict_reason_moved_away;
1410           else
1411             reason = svn_wc_conflict_reason_deleted;
1412         }
1413         break;
1414 
1415       case svn_wc__db_status_incomplete:
1416         /* We used svn_wc__db_read_info(), so 'incomplete' means
1417          * - there is no node in the WORKING tree
1418          * - a BASE node is known to exist
1419          * So the node exists and is essentially 'normal'. We still need to
1420          * check prop and text mods, and those checks will retrieve the
1421          * missing information (hopefully). */
1422       case svn_wc__db_status_normal:
1423         if (action == svn_wc_conflict_action_edit)
1424           {
1425             /* An edit onto a local edit or onto *no* local changes is no
1426              * tree-conflict. (It's possibly a text- or prop-conflict,
1427              * but we don't handle those here.)
1428              *
1429              * Except when there is a local obstruction
1430              */
1431             if (exists_in_repos)
1432               {
1433                 svn_node_kind_t disk_kind;
1434 
1435                 SVN_ERR(svn_io_check_path(local_abspath, &disk_kind,
1436                                           scratch_pool));
1437 
1438                 if (disk_kind != expected_kind && disk_kind != svn_node_none)
1439                   {
1440                     reason = svn_wc_conflict_reason_obstructed;
1441                     break;
1442                   }
1443 
1444               }
1445             return SVN_NO_ERROR;
1446           }
1447 
1448         /* Replace is handled as delete and then specifically in
1449            add_directory() and add_file(), so we only expect deletes here */
1450         SVN_ERR_ASSERT(action == svn_wc_conflict_action_delete);
1451 
1452         /* Check if the update wants to delete or replace a locally
1453          * modified node. */
1454 
1455 
1456         /* Do a deep tree detection of local changes. The update editor will
1457          * not visit the subdirectories of a directory that it wants to delete.
1458          * Therefore, we need to start a separate crawl here. */
1459 
1460         SVN_ERR(svn_wc__node_has_local_mods(&modified, NULL,
1461                                             eb->db, local_abspath, TRUE,
1462                                             eb->cancel_func, eb->cancel_baton,
1463                                             scratch_pool));
1464 
1465         if (modified)
1466           {
1467             if (working_status == svn_wc__db_status_deleted)
1468               reason = svn_wc_conflict_reason_deleted;
1469             else
1470               reason = svn_wc_conflict_reason_edited;
1471           }
1472         break;
1473 
1474       case svn_wc__db_status_server_excluded:
1475         /* Not allowed to view the node. Not allowed to report tree
1476          * conflicts. */
1477       case svn_wc__db_status_excluded:
1478         /* Locally marked as excluded. No conflicts wanted. */
1479       case svn_wc__db_status_not_present:
1480         /* A committed delete (but parent not updated). The delete is
1481            committed, so no conflict possible during update. */
1482         return SVN_NO_ERROR;
1483 
1484       case svn_wc__db_status_base_deleted:
1485         /* An internal status. Should never show up here. */
1486         SVN_ERR_MALFUNCTION();
1487         break;
1488 
1489     }
1490 
1491   if (reason == SVN_WC_CONFLICT_REASON_NONE)
1492     /* No conflict with the current action. */
1493     return SVN_NO_ERROR;
1494 
1495 
1496   /* Sanity checks. Note that if there was no action on the node, this function
1497    * would not have been called in the first place.*/
1498   if (reason == svn_wc_conflict_reason_edited
1499       || reason == svn_wc_conflict_reason_obstructed
1500       || reason == svn_wc_conflict_reason_deleted
1501       || reason == svn_wc_conflict_reason_moved_away
1502       || reason == svn_wc_conflict_reason_replaced)
1503     {
1504       /* When the node existed before (it was locally deleted, replaced or
1505        * edited), then 'update' cannot add it "again". So it can only send
1506        * _action_edit, _delete or _replace. */
1507     if (action != svn_wc_conflict_action_edit
1508         && action != svn_wc_conflict_action_delete
1509         && action != svn_wc_conflict_action_replace)
1510       return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
1511                _("Unexpected attempt to add a node at path '%s'"),
1512                svn_dirent_local_style(local_abspath, scratch_pool));
1513     }
1514   else if (reason == svn_wc_conflict_reason_added ||
1515            reason == svn_wc_conflict_reason_moved_here)
1516     {
1517       /* When the node did not exist before (it was locally added),
1518        * then 'update' cannot want to modify it in any way.
1519        * It can only send _action_add. */
1520       if (action != svn_wc_conflict_action_add)
1521         return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
1522                  _("Unexpected attempt to edit, delete, or replace "
1523                    "a node at path '%s'"),
1524                  svn_dirent_local_style(local_abspath, scratch_pool));
1525 
1526     }
1527 
1528 
1529   /* A conflict was detected. Create a conflict skel to record it. */
1530   *pconflict = svn_wc__conflict_skel_create(result_pool);
1531 
1532   SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(*pconflict,
1533                                                   eb->db, local_abspath,
1534                                                   reason,
1535                                                   action,
1536                                                   move_src_op_root_abspath,
1537                                                   move_dst_op_root_abspath,
1538                                                   result_pool, scratch_pool));
1539 
1540   return SVN_NO_ERROR;
1541 }
1542 
1543 
1544 /* If LOCAL_ABSPATH is inside a conflicted tree and the conflict is
1545  * not a moved-away-edit conflict, set *CONFLICTED to TRUE.  Otherwise
1546  * set *CONFLICTED to FALSE.
1547  */
1548 static svn_error_t *
already_in_a_tree_conflict(svn_boolean_t * conflicted,svn_boolean_t * ignored,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)1549 already_in_a_tree_conflict(svn_boolean_t *conflicted,
1550                            svn_boolean_t *ignored,
1551                            svn_wc__db_t *db,
1552                            const char *local_abspath,
1553                            apr_pool_t *scratch_pool)
1554 {
1555   const char *ancestor_abspath = local_abspath;
1556   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1557 
1558   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1559 
1560   *conflicted = *ignored = FALSE;
1561 
1562   while (TRUE)
1563     {
1564       svn_boolean_t is_wc_root;
1565 
1566       svn_pool_clear(iterpool);
1567 
1568       SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, ignored, db,
1569                                               ancestor_abspath, TRUE,
1570                                               scratch_pool));
1571       if (*conflicted || *ignored)
1572         break;
1573 
1574       SVN_ERR(svn_wc__db_is_wcroot(&is_wc_root, db, ancestor_abspath,
1575                                    iterpool));
1576       if (is_wc_root)
1577         break;
1578 
1579       ancestor_abspath = svn_dirent_dirname(ancestor_abspath, scratch_pool);
1580     }
1581 
1582   svn_pool_destroy(iterpool);
1583 
1584   return SVN_NO_ERROR;
1585 }
1586 
1587 /* Temporary helper until the new conflict handling is in place */
1588 static svn_error_t *
node_already_conflicted(svn_boolean_t * conflicted,svn_boolean_t * conflict_ignored,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)1589 node_already_conflicted(svn_boolean_t *conflicted,
1590                         svn_boolean_t *conflict_ignored,
1591                         svn_wc__db_t *db,
1592                         const char *local_abspath,
1593                         apr_pool_t *scratch_pool)
1594 {
1595   SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, conflict_ignored, db,
1596                                           local_abspath, FALSE,
1597                                           scratch_pool));
1598 
1599   return SVN_NO_ERROR;
1600 }
1601 
1602 
1603 /* An svn_delta_editor_t function. */
1604 static svn_error_t *
delete_entry(const char * path,svn_revnum_t revision,void * parent_baton,apr_pool_t * pool)1605 delete_entry(const char *path,
1606              svn_revnum_t revision,
1607              void *parent_baton,
1608              apr_pool_t *pool)
1609 {
1610   struct dir_baton *pb = parent_baton;
1611   struct edit_baton *eb = pb->edit_baton;
1612   const char *base = svn_relpath_basename(path, NULL);
1613   const char *local_abspath;
1614   const char *repos_relpath;
1615   const char *deleted_repos_relpath;
1616   svn_node_kind_t kind;
1617   svn_revnum_t old_revision;
1618   svn_boolean_t conflicted;
1619   svn_boolean_t have_work;
1620   svn_skel_t *tree_conflict = NULL;
1621   svn_wc__db_status_t status;
1622   svn_wc__db_status_t base_status;
1623   apr_pool_t *scratch_pool;
1624   svn_boolean_t deleting_target;
1625   svn_boolean_t deleting_switched;
1626 
1627   if (pb->skip_this)
1628     return SVN_NO_ERROR;
1629 
1630   scratch_pool = svn_pool_create(pb->pool);
1631 
1632   SVN_ERR(mark_directory_edited(pb, scratch_pool));
1633 
1634   SVN_ERR(path_join_under_root(&local_abspath, pb->local_abspath, base,
1635                                scratch_pool));
1636 
1637   deleting_target =  (strcmp(local_abspath, eb->target_abspath) == 0);
1638 
1639   /* Detect obstructing working copies */
1640   {
1641     svn_boolean_t is_root;
1642 
1643 
1644     SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, local_abspath,
1645                                  scratch_pool));
1646 
1647     if (is_root)
1648       {
1649         /* Just skip this node; a future update will handle it */
1650         SVN_ERR(remember_skipped_tree(eb, local_abspath, pool));
1651         do_notification(eb, local_abspath, svn_node_unknown,
1652                         svn_wc_notify_update_skip_obstruction, scratch_pool);
1653 
1654         svn_pool_destroy(scratch_pool);
1655 
1656         return SVN_NO_ERROR;
1657       }
1658   }
1659 
1660   SVN_ERR(svn_wc__db_read_info(&status, &kind, &old_revision, &repos_relpath,
1661                                NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1662                                NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1663                                &conflicted, NULL, NULL, NULL,
1664                                NULL, NULL, &have_work,
1665                                eb->db, local_abspath,
1666                                scratch_pool, scratch_pool));
1667 
1668   if (!have_work)
1669     {
1670       base_status = status;
1671     }
1672   else
1673     SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &old_revision,
1674                                      &repos_relpath,
1675                                      NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1676                                      NULL, NULL, NULL, NULL, NULL,
1677                                      eb->db, local_abspath,
1678                                      scratch_pool, scratch_pool));
1679 
1680   if (pb->old_repos_relpath && repos_relpath)
1681     {
1682       const char *expected_name;
1683 
1684       expected_name = svn_relpath_skip_ancestor(pb->old_repos_relpath,
1685                                                 repos_relpath);
1686 
1687       deleting_switched = (!expected_name || strcmp(expected_name, base) != 0);
1688     }
1689   else
1690     deleting_switched = FALSE;
1691 
1692   /* Is this path a conflict victim? */
1693   if (pb->shadowed)
1694     conflicted = FALSE; /* Conflict applies to WORKING */
1695   else if (conflicted)
1696     SVN_ERR(node_already_conflicted(&conflicted, NULL,
1697                                     eb->db, local_abspath, scratch_pool));
1698   if (conflicted)
1699     {
1700       SVN_ERR(remember_skipped_tree(eb, local_abspath, scratch_pool));
1701 
1702       do_notification(eb, local_abspath, svn_node_unknown,
1703                       svn_wc_notify_skip_conflicted,
1704                       scratch_pool);
1705 
1706       svn_pool_destroy(scratch_pool);
1707 
1708       return SVN_NO_ERROR;
1709     }
1710 
1711 
1712   /* Receive the remote removal of excluded/server-excluded/not present node.
1713      Do not notify, but perform the change even when the node is shadowed */
1714   if (base_status == svn_wc__db_status_not_present
1715       || base_status == svn_wc__db_status_excluded
1716       || base_status == svn_wc__db_status_server_excluded)
1717     {
1718       SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, TRUE,
1719                                      deleting_target, FALSE,
1720                                      *eb->target_revision,
1721                                      NULL, NULL,
1722                                      scratch_pool));
1723 
1724       if (deleting_target)
1725         eb->target_deleted = TRUE;
1726 
1727       svn_pool_destroy(scratch_pool);
1728 
1729       return SVN_NO_ERROR;
1730     }
1731 
1732   /* Is this path the victim of a newly-discovered tree conflict?  If so,
1733    * remember it and notify the client. Then (if it was existing and
1734    * modified), re-schedule the node to be added back again, as a (modified)
1735    * copy of the previous base version.  */
1736 
1737   /* Check for conflicts only when we haven't already recorded
1738    * a tree-conflict on a parent node. */
1739   if (!pb->shadowed && !pb->edit_obstructed)
1740     {
1741       SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath,
1742                                   status, TRUE,
1743                                   kind,
1744                                   svn_wc_conflict_action_delete,
1745                                   pb->pool, scratch_pool));
1746     }
1747 
1748   if (tree_conflict != NULL)
1749     {
1750       /* When we raise a tree conflict on a node, we don't want to mark the
1751        * node as skipped, to allow a replacement to continue doing at least
1752        * a bit of its work (possibly adding a not present node, for the
1753        * next update) */
1754       if (!pb->deletion_conflicts)
1755         pb->deletion_conflicts = apr_hash_make(pb->pool);
1756 
1757       svn_hash_sets(pb->deletion_conflicts, apr_pstrdup(pb->pool, base),
1758                     tree_conflict);
1759 
1760       /* Whatever the kind of conflict, we can just clear BASE
1761          by turning whatever is there into a copy */
1762     }
1763 
1764   /* Calculate the repository-relative path of the entry which was
1765    * deleted. For updates it's the same as REPOS_RELPATH but for
1766    * switches it is within the switch target. */
1767   SVN_ERR(calculate_repos_relpath(&deleted_repos_relpath, local_abspath,
1768                                   repos_relpath, eb, pb, scratch_pool,
1769                                   scratch_pool));
1770   SVN_ERR(complete_conflict(tree_conflict, eb, local_abspath, repos_relpath,
1771                             old_revision, deleted_repos_relpath,
1772                             kind, svn_node_none, NULL,
1773                             pb->pool, scratch_pool));
1774 
1775   /* Issue a wq operation to delete the BASE_NODE data and to delete actual
1776      nodes based on that from disk, but leave any WORKING_NODEs on disk.
1777 
1778      Local modifications are already turned into copies at this point.
1779 
1780      If the thing being deleted is the *target* of this update, then
1781      we need to recreate a 'deleted' entry, so that the parent can give
1782      accurate reports about itself in the future. */
1783   if (! deleting_target && ! deleting_switched)
1784     {
1785       /* Delete, and do not leave a not-present node.  */
1786       SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
1787                                      (tree_conflict != NULL),
1788                                      FALSE, FALSE,
1789                                      SVN_INVALID_REVNUM /* not_present_rev */,
1790                                      tree_conflict, NULL,
1791                                      scratch_pool));
1792     }
1793   else
1794     {
1795       /* Delete, leaving a not-present node.  */
1796       SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
1797                                      (tree_conflict != NULL),
1798                                      TRUE, FALSE,
1799                                      *eb->target_revision,
1800                                      tree_conflict, NULL,
1801                                      scratch_pool));
1802       if (deleting_target)
1803         eb->target_deleted = TRUE;
1804       else
1805         {
1806           /* Don't remove the not-present marker at the final bump */
1807           SVN_ERR(remember_skipped_tree(eb, local_abspath, pool));
1808         }
1809     }
1810 
1811   SVN_ERR(svn_wc__wq_run(eb->db, pb->local_abspath,
1812                          eb->cancel_func, eb->cancel_baton,
1813                          scratch_pool));
1814 
1815   /* Notify. */
1816   if (tree_conflict)
1817     {
1818       if (eb->conflict_func)
1819         SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath,
1820                                                  kind,
1821                                                  tree_conflict,
1822                                                  NULL /* merge_options */,
1823                                                  eb->conflict_func,
1824                                                  eb->conflict_baton,
1825                                                  eb->cancel_func,
1826                                                  eb->cancel_baton,
1827                                                  scratch_pool));
1828       do_notification(eb, local_abspath, kind,
1829                       svn_wc_notify_tree_conflict, scratch_pool);
1830     }
1831   else
1832     {
1833       svn_wc_notify_action_t action = svn_wc_notify_update_delete;
1834 
1835       if (pb->shadowed || pb->edit_obstructed)
1836         action = svn_wc_notify_update_shadowed_delete;
1837 
1838       do_notification(eb, local_abspath, kind, action, scratch_pool);
1839     }
1840 
1841   svn_pool_destroy(scratch_pool);
1842 
1843   return SVN_NO_ERROR;
1844 }
1845 
1846 /* An svn_delta_editor_t function. */
1847 static svn_error_t *
add_directory(const char * path,void * parent_baton,const char * copyfrom_path,svn_revnum_t copyfrom_rev,apr_pool_t * pool,void ** child_baton)1848 add_directory(const char *path,
1849               void *parent_baton,
1850               const char *copyfrom_path,
1851               svn_revnum_t copyfrom_rev,
1852               apr_pool_t *pool,
1853               void **child_baton)
1854 {
1855   struct dir_baton *pb = parent_baton;
1856   struct edit_baton *eb = pb->edit_baton;
1857   struct dir_baton *db;
1858   apr_pool_t *scratch_pool = svn_pool_create(pool);
1859   svn_node_kind_t kind;
1860   svn_wc__db_status_t status;
1861   svn_node_kind_t wc_kind;
1862   svn_boolean_t conflicted;
1863   svn_boolean_t conflict_ignored = FALSE;
1864   svn_boolean_t versioned_locally_and_present;
1865   svn_skel_t *tree_conflict = NULL;
1866   svn_error_t *err;
1867 
1868   SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev)));
1869 
1870   SVN_ERR(make_dir_baton(&db, path, eb, pb, TRUE, pool));
1871   *child_baton = db;
1872 
1873   if (db->skip_this)
1874     return SVN_NO_ERROR;
1875 
1876   SVN_ERR(calculate_repos_relpath(&db->new_repos_relpath, db->local_abspath,
1877                                   NULL, eb, pb, db->pool, scratch_pool));
1878 
1879   SVN_ERR(mark_directory_edited(db, pool));
1880 
1881   if (strcmp(eb->target_abspath, db->local_abspath) == 0)
1882     {
1883       /* The target of the edit is being added, give it the requested
1884          depth of the edit (but convert svn_depth_unknown to
1885          svn_depth_infinity). */
1886       db->ambient_depth = (eb->requested_depth == svn_depth_unknown)
1887         ? svn_depth_infinity : eb->requested_depth;
1888     }
1889   else if (eb->requested_depth == svn_depth_immediates
1890            || (eb->requested_depth == svn_depth_unknown
1891                && pb->ambient_depth == svn_depth_immediates))
1892     {
1893       db->ambient_depth = svn_depth_empty;
1894     }
1895   else
1896     {
1897       db->ambient_depth = svn_depth_infinity;
1898     }
1899 
1900   /* It may not be named the same as the administrative directory. */
1901   if (svn_wc_is_adm_dir(db->name, pool))
1902     return svn_error_createf(
1903        SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
1904        _("Failed to add directory '%s': object of the same name as the "
1905          "administrative directory"),
1906        svn_dirent_local_style(db->local_abspath, pool));
1907 
1908   if (!eb->clean_checkout)
1909     {
1910       SVN_ERR(svn_io_check_path(db->local_abspath, &kind, db->pool));
1911 
1912       err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL,
1913                                 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1914                                 NULL, NULL, NULL, NULL, NULL,
1915                                 &conflicted, NULL, NULL, NULL, NULL, NULL, NULL,
1916                                 eb->db, db->local_abspath,
1917                                 scratch_pool, scratch_pool);
1918     }
1919   else
1920     {
1921       kind = svn_node_none;
1922       status = svn_wc__db_status_not_present;
1923       wc_kind = svn_node_unknown;
1924       conflicted = FALSE;
1925       err = NULL;
1926     }
1927 
1928   if (err)
1929     {
1930       if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1931         return svn_error_trace(err);
1932 
1933       svn_error_clear(err);
1934       wc_kind = svn_node_unknown;
1935       status = svn_wc__db_status_normal;
1936       conflicted = FALSE;
1937 
1938       versioned_locally_and_present = FALSE;
1939     }
1940   else if (status == svn_wc__db_status_normal && wc_kind == svn_node_unknown)
1941     {
1942       SVN_ERR_ASSERT(conflicted);
1943       versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */
1944     }
1945   else if (status == svn_wc__db_status_normal
1946            || status == svn_wc__db_status_incomplete)
1947     {
1948       svn_boolean_t root;
1949 
1950       SVN_ERR(svn_wc__db_is_wcroot(&root, eb->db, db->local_abspath,
1951                                    scratch_pool));
1952 
1953       if (root)
1954         {
1955           /* !! We found the root of a working copy obstructing the wc !!
1956 
1957              If the directory would be part of our own working copy then
1958              we wouldn't have been called as an add_directory().
1959 
1960              The only thing we can do is add a not-present node, to allow
1961              a future update to bring in the new files when the problem is
1962              resolved.  Note that svn_wc__db_base_add_not_present_node()
1963              explicitly adds the node into the parent's node database. */
1964 
1965           svn_hash_sets(pb->not_present_nodes,
1966                         apr_pstrdup(pb->pool, db->name),
1967                         svn_node_kind_to_word(svn_node_dir));
1968         }
1969       else if (wc_kind == svn_node_dir)
1970         {
1971           /* We have an editor violation. Github sometimes does this
1972              in its subversion compatibility code, when changing the
1973              depth of a working copy, or on updates from incomplete */
1974         }
1975       else
1976         {
1977           /* We found a file external occupating the place we need in BASE.
1978 
1979             We can't add a not-present node in this case as that would overwrite
1980             the file external. Luckily the file external itself stops us from
1981             forgetting a child of this parent directory like an obstructing
1982             working copy would.
1983 
1984             The reason we get here is that the adm crawler doesn't report
1985             file externals.
1986           */
1987           SVN_ERR_ASSERT(wc_kind == svn_node_file
1988                          || wc_kind == svn_node_symlink);
1989         }
1990 
1991       SVN_ERR(remember_skipped_tree(eb, db->local_abspath, scratch_pool));
1992       db->skip_this = TRUE;
1993       db->already_notified = TRUE;
1994 
1995       do_notification(eb, db->local_abspath, wc_kind,
1996                       svn_wc_notify_update_skip_obstruction, scratch_pool);
1997 
1998       svn_pool_destroy(scratch_pool);
1999 
2000       return SVN_NO_ERROR;
2001     }
2002   else
2003     versioned_locally_and_present = IS_NODE_PRESENT(status);
2004 
2005   /* Is this path a conflict victim? */
2006   if (conflicted)
2007     {
2008       if (pb->deletion_conflicts)
2009         tree_conflict = svn_hash_gets(pb->deletion_conflicts, db->name);
2010 
2011       if (tree_conflict)
2012         {
2013           svn_wc_conflict_reason_t reason;
2014           const char *move_src_op_root_abspath;
2015           const char *move_dst_op_root_abspath;
2016           /* So this deletion wasn't just a deletion, it is actually a
2017              replacement. Let's install a better tree conflict. */
2018 
2019           SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
2020                                                       &move_src_op_root_abspath,
2021                                                       &move_dst_op_root_abspath,
2022                                                       eb->db,
2023                                                       db->local_abspath,
2024                                                       tree_conflict,
2025                                                       db->pool, scratch_pool));
2026 
2027           tree_conflict = svn_wc__conflict_skel_create(db->pool);
2028 
2029           SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
2030                                         tree_conflict,
2031                                         eb->db, db->local_abspath,
2032                                         reason, svn_wc_conflict_action_replace,
2033                                         move_src_op_root_abspath,
2034                                         move_dst_op_root_abspath,
2035                                         db->pool, scratch_pool));
2036 
2037           /* And now stop checking for conflicts here and just perform
2038              a shadowed update */
2039           db->edit_conflict = tree_conflict; /* Cache for close_directory */
2040           tree_conflict = NULL; /* No direct notification */
2041           db->shadowed = TRUE; /* Just continue */
2042           conflicted = FALSE; /* No skip */
2043         }
2044       else
2045         SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
2046                                         eb->db, db->local_abspath,
2047                                         scratch_pool));
2048     }
2049 
2050   /* Now the "usual" behaviour if already conflicted. Skip it. */
2051   if (conflicted)
2052     {
2053       /* Record this conflict so that its descendants are skipped silently. */
2054       SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2055 
2056       db->skip_this = TRUE;
2057       db->already_notified = TRUE;
2058 
2059       /* We skip this node, but once the update completes the parent node will
2060          be updated to the new revision. So a future recursive update of the
2061          parent will not bring in this new node as the revision of the parent
2062          describes to the repository that all children are available.
2063 
2064          To resolve this problem, we add a not-present node to allow bringing
2065          the node in once this conflict is resolved.
2066 
2067          Note that we can safely assume that no present base node exists,
2068          because then we would not have received an add_directory.
2069        */
2070       svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, db->name),
2071                     svn_node_kind_to_word(svn_node_dir));
2072 
2073       do_notification(eb, db->local_abspath, svn_node_dir,
2074                       svn_wc_notify_skip_conflicted, scratch_pool);
2075 
2076       svn_pool_destroy(scratch_pool);
2077       return SVN_NO_ERROR;
2078     }
2079   else if (conflict_ignored)
2080     {
2081       db->shadowed = TRUE;
2082     }
2083 
2084   if (db->shadowed)
2085     {
2086       /* Nothing to check; does not and will not exist in working copy */
2087     }
2088   else if (versioned_locally_and_present)
2089     {
2090       /* What to do with a versioned or schedule-add dir:
2091 
2092          A dir already added without history is OK.  Set add_existed
2093          so that user notification is delayed until after any prop
2094          conflicts have been found.
2095 
2096          An existing versioned dir is an error.  In the future we may
2097          relax this restriction and simply update such dirs.
2098 
2099          A dir added with history is a tree conflict. */
2100 
2101       svn_boolean_t local_is_non_dir;
2102       svn_wc__db_status_t add_status = svn_wc__db_status_normal;
2103 
2104       /* Is the local add a copy? */
2105       if (status == svn_wc__db_status_added)
2106         SVN_ERR(svn_wc__db_scan_addition(&add_status, NULL, NULL, NULL, NULL,
2107                                          NULL, NULL, NULL, NULL,
2108                                          eb->db, db->local_abspath,
2109                                          scratch_pool, scratch_pool));
2110 
2111 
2112       /* Is there *something* that is not a dir? */
2113       local_is_non_dir = (wc_kind != svn_node_dir
2114                           && status != svn_wc__db_status_deleted);
2115 
2116       /* Do tree conflict checking if
2117        *  - if there is a local copy.
2118        *  - if this is a switch operation
2119        *  - the node kinds mismatch
2120        *
2121        * During switch, local adds at the same path as incoming adds get
2122        * "lost" in that switching back to the original will no longer have the
2123        * local add. So switch always alerts the user with a tree conflict. */
2124       if (!eb->adds_as_modification
2125           || local_is_non_dir
2126           || add_status != svn_wc__db_status_added)
2127         {
2128           SVN_ERR(check_tree_conflict(&tree_conflict, eb,
2129                                       db->local_abspath,
2130                                       status, FALSE, svn_node_none,
2131                                       svn_wc_conflict_action_add,
2132                                       db->pool, scratch_pool));
2133         }
2134 
2135       if (tree_conflict == NULL)
2136         db->add_existed = TRUE; /* Take over WORKING */
2137       else
2138         db->shadowed = TRUE; /* Only update BASE */
2139     }
2140   else if (kind != svn_node_none)
2141     {
2142       /* There's an unversioned node at this path. */
2143       db->obstruction_found = TRUE;
2144 
2145       /* Unversioned, obstructing dirs are handled by prop merge/conflict,
2146        * if unversioned obstructions are allowed. */
2147       if (! (kind == svn_node_dir && eb->allow_unver_obstructions))
2148         {
2149           /* Bring in the node as deleted */ /* ### Obstructed Conflict */
2150           db->shadowed = TRUE;
2151 
2152           /* Mark a conflict */
2153           tree_conflict = svn_wc__conflict_skel_create(db->pool);
2154 
2155           SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
2156                                         tree_conflict,
2157                                         eb->db, db->local_abspath,
2158                                         svn_wc_conflict_reason_unversioned,
2159                                         svn_wc_conflict_action_add,
2160                                         NULL, NULL, db->pool, scratch_pool));
2161           db->edit_conflict = tree_conflict;
2162         }
2163     }
2164 
2165   if (tree_conflict)
2166     SVN_ERR(complete_conflict(tree_conflict, eb, db->local_abspath,
2167                               db->old_repos_relpath, db->old_revision,
2168                               db->new_repos_relpath,
2169                               wc_kind, svn_node_dir,
2170                               pb->deletion_conflicts
2171                                 ? svn_hash_gets(pb->deletion_conflicts,
2172                                                 db->name)
2173                                 : NULL,
2174                               db->pool, scratch_pool));
2175 
2176   SVN_ERR(svn_wc__db_base_add_incomplete_directory(
2177                                      eb->db, db->local_abspath,
2178                                      db->new_repos_relpath,
2179                                      eb->repos_root,
2180                                      eb->repos_uuid,
2181                                      *eb->target_revision,
2182                                      db->ambient_depth,
2183                                      (db->shadowed && db->obstruction_found),
2184                                      (! db->shadowed
2185                                       && status == svn_wc__db_status_added),
2186                                      tree_conflict, NULL,
2187                                      scratch_pool));
2188 
2189   /* Make sure there is a real directory at LOCAL_ABSPATH, unless we are just
2190      updating the DB */
2191   if (!db->shadowed)
2192     SVN_ERR(svn_wc__ensure_directory(db->local_abspath, scratch_pool));
2193 
2194   if (tree_conflict != NULL)
2195     {
2196       db->edit_conflict = tree_conflict;
2197 
2198       db->already_notified = TRUE;
2199       do_notification(eb, db->local_abspath, svn_node_dir,
2200                       svn_wc_notify_tree_conflict, scratch_pool);
2201     }
2202 
2203 
2204   /* If this add was obstructed by dir scheduled for addition without
2205      history let close_directory() handle the notification because there
2206      might be properties to deal with.  If PATH was added inside a locally
2207      deleted tree, then suppress notification, a tree conflict was already
2208      issued. */
2209   if (eb->notify_func && !db->already_notified && !db->add_existed)
2210     {
2211       svn_wc_notify_action_t action;
2212 
2213       if (db->shadowed)
2214         action = svn_wc_notify_update_shadowed_add;
2215       else if (db->obstruction_found || db->add_existed)
2216         action = svn_wc_notify_exists;
2217       else
2218         action = svn_wc_notify_update_add;
2219 
2220       db->already_notified = TRUE;
2221 
2222       do_notification(eb, db->local_abspath, svn_node_dir, action,
2223                       scratch_pool);
2224     }
2225 
2226   svn_pool_destroy(scratch_pool);
2227 
2228   return SVN_NO_ERROR;
2229 }
2230 
2231 /* An svn_delta_editor_t function. */
2232 static svn_error_t *
open_directory(const char * path,void * parent_baton,svn_revnum_t base_revision,apr_pool_t * pool,void ** child_baton)2233 open_directory(const char *path,
2234                void *parent_baton,
2235                svn_revnum_t base_revision,
2236                apr_pool_t *pool,
2237                void **child_baton)
2238 {
2239   struct dir_baton *db, *pb = parent_baton;
2240   struct edit_baton *eb = pb->edit_baton;
2241   svn_boolean_t have_work;
2242   svn_boolean_t conflicted;
2243   svn_boolean_t conflict_ignored = FALSE;
2244   svn_skel_t *tree_conflict = NULL;
2245   svn_wc__db_status_t status, base_status;
2246   svn_node_kind_t wc_kind;
2247 
2248   SVN_ERR(make_dir_baton(&db, path, eb, pb, FALSE, pool));
2249   *child_baton = db;
2250 
2251   if (db->skip_this)
2252     return SVN_NO_ERROR;
2253 
2254   /* Detect obstructing working copies */
2255   {
2256     svn_boolean_t is_root;
2257 
2258     SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, db->local_abspath,
2259                                  pool));
2260 
2261     if (is_root)
2262       {
2263         /* Just skip this node; a future update will handle it */
2264         SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2265         db->skip_this = TRUE;
2266         db->already_notified = TRUE;
2267 
2268         do_notification(eb, db->local_abspath, svn_node_dir,
2269                         svn_wc_notify_update_skip_obstruction, pool);
2270 
2271         return SVN_NO_ERROR;
2272       }
2273   }
2274 
2275   /* We should have a write lock on every directory touched.  */
2276   SVN_ERR(svn_wc__write_check(eb->db, db->local_abspath, pool));
2277 
2278   SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &db->old_revision,
2279                                &db->old_repos_relpath, NULL, NULL,
2280                                &db->changed_rev, &db->changed_date,
2281                                &db->changed_author, &db->ambient_depth,
2282                                NULL, NULL, NULL, NULL,
2283                                NULL, NULL, NULL, NULL, NULL, NULL,
2284                                &conflicted, NULL, NULL, NULL,
2285                                NULL, NULL, &have_work,
2286                                eb->db, db->local_abspath,
2287                                db->pool, pool));
2288 
2289   if (!have_work)
2290     base_status = status;
2291   else
2292     SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db->old_revision,
2293                                      &db->old_repos_relpath, NULL, NULL,
2294                                      &db->changed_rev, &db->changed_date,
2295                                      &db->changed_author, &db->ambient_depth,
2296                                      NULL, NULL, NULL, NULL, NULL, NULL,
2297                                      eb->db, db->local_abspath,
2298                                      db->pool, pool));
2299 
2300   db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
2301 
2302   SVN_ERR(calculate_repos_relpath(&db->new_repos_relpath, db->local_abspath,
2303                                   db->old_repos_relpath, eb, pb,
2304                                   db->pool, pool));
2305 
2306   /* Is this path a conflict victim? */
2307   if (db->shadowed)
2308     conflicted = FALSE; /* Conflict applies to WORKING */
2309   else if (conflicted)
2310     SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
2311                                     eb->db, db->local_abspath, pool));
2312   if (conflicted)
2313     {
2314       SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2315 
2316       db->skip_this = TRUE;
2317       db->already_notified = TRUE;
2318 
2319       do_notification(eb, db->local_abspath, svn_node_unknown,
2320                       svn_wc_notify_skip_conflicted, pool);
2321 
2322       return SVN_NO_ERROR;
2323     }
2324   else if (conflict_ignored)
2325     {
2326       db->shadowed = TRUE;
2327     }
2328 
2329   /* Is this path a fresh tree conflict victim?  If so, skip the tree
2330      with one notification. */
2331 
2332   /* Check for conflicts only when we haven't already recorded
2333    * a tree-conflict on a parent node. */
2334   if (!db->shadowed)
2335     SVN_ERR(check_tree_conflict(&tree_conflict, eb, db->local_abspath,
2336                                 status, TRUE, svn_node_dir,
2337                                 svn_wc_conflict_action_edit,
2338                                 db->pool, pool));
2339 
2340   /* Remember the roots of any locally deleted trees. */
2341   if (tree_conflict != NULL)
2342     {
2343       svn_wc_conflict_reason_t reason;
2344       db->edit_conflict = tree_conflict;
2345       /* Other modifications wouldn't be a tree conflict */
2346 
2347       SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL, NULL,
2348                                                   eb->db, db->local_abspath,
2349                                                   tree_conflict,
2350                                                   db->pool, db->pool));
2351       SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
2352                      || reason == svn_wc_conflict_reason_moved_away
2353                      || reason == svn_wc_conflict_reason_replaced
2354                      || reason == svn_wc_conflict_reason_obstructed);
2355 
2356       /* Continue updating BASE */
2357       if (reason == svn_wc_conflict_reason_obstructed)
2358         db->edit_obstructed = TRUE;
2359       else
2360         db->shadowed = TRUE;
2361     }
2362 
2363   /* Mark directory as being at target_revision and URL, but incomplete. */
2364   SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db, db->local_abspath,
2365                                                     db->new_repos_relpath,
2366                                                     *eb->target_revision,
2367                                                     pool));
2368 
2369   return SVN_NO_ERROR;
2370 }
2371 
2372 
2373 /* An svn_delta_editor_t function. */
2374 static svn_error_t *
change_dir_prop(void * dir_baton,const char * name,const svn_string_t * value,apr_pool_t * pool)2375 change_dir_prop(void *dir_baton,
2376                 const char *name,
2377                 const svn_string_t *value,
2378                 apr_pool_t *pool)
2379 {
2380   svn_prop_t *propchange;
2381   struct dir_baton *db = dir_baton;
2382 
2383   if (db->skip_this)
2384     return SVN_NO_ERROR;
2385 
2386   propchange = apr_array_push(db->propchanges);
2387   propchange->name = apr_pstrdup(db->pool, name);
2388   propchange->value = svn_string_dup(value, db->pool);
2389 
2390   if (!db->edited && svn_property_kind2(name) == svn_prop_regular_kind)
2391     SVN_ERR(mark_directory_edited(db, pool));
2392 
2393   return SVN_NO_ERROR;
2394 }
2395 
2396 /* If any of the svn_prop_t objects in PROPCHANGES represents a change
2397    to the SVN_PROP_EXTERNALS property, return that change, else return
2398    null.  If PROPCHANGES contains more than one such change, return
2399    the first. */
2400 static const svn_prop_t *
externals_prop_changed(const apr_array_header_t * propchanges)2401 externals_prop_changed(const apr_array_header_t *propchanges)
2402 {
2403   int i;
2404 
2405   for (i = 0; i < propchanges->nelts; i++)
2406     {
2407       const svn_prop_t *p = &(APR_ARRAY_IDX(propchanges, i, svn_prop_t));
2408       if (strcmp(p->name, SVN_PROP_EXTERNALS) == 0)
2409         return p;
2410     }
2411 
2412   return NULL;
2413 }
2414 
2415 
2416 
2417 /* An svn_delta_editor_t function. */
2418 static svn_error_t *
close_directory(void * dir_baton,apr_pool_t * pool)2419 close_directory(void *dir_baton,
2420                 apr_pool_t *pool)
2421 {
2422   struct dir_baton *db = dir_baton;
2423   struct edit_baton *eb = db->edit_baton;
2424   svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
2425   apr_array_header_t *entry_prop_changes;
2426   apr_array_header_t *dav_prop_changes;
2427   apr_array_header_t *regular_prop_changes;
2428   apr_hash_t *base_props;
2429   apr_hash_t *actual_props;
2430   apr_hash_t *new_base_props = NULL;
2431   apr_hash_t *new_actual_props = NULL;
2432   svn_revnum_t new_changed_rev = SVN_INVALID_REVNUM;
2433   apr_time_t new_changed_date = 0;
2434   const char *new_changed_author = NULL;
2435   apr_pool_t *scratch_pool = db->pool;
2436   svn_skel_t *all_work_items = NULL;
2437   svn_skel_t *conflict_skel = NULL;
2438 
2439   /* Skip if we're in a conflicted tree. */
2440   if (db->skip_this)
2441     {
2442       /* Allow the parent to complete its update. */
2443       SVN_ERR(maybe_release_dir_info(db));
2444 
2445       return SVN_NO_ERROR;
2446     }
2447 
2448   if (db->edited)
2449     conflict_skel = db->edit_conflict;
2450 
2451   SVN_ERR(svn_categorize_props(db->propchanges, &entry_prop_changes,
2452                                &dav_prop_changes, &regular_prop_changes, pool));
2453 
2454   /* Fetch the existing properties.  */
2455   if ((!db->adding_dir || db->add_existed)
2456       && !db->shadowed)
2457     {
2458       SVN_ERR(svn_wc__get_actual_props(&actual_props,
2459                                        eb->db, db->local_abspath,
2460                                        scratch_pool, scratch_pool));
2461     }
2462   else
2463     actual_props = apr_hash_make(pool);
2464 
2465   if (db->add_existed)
2466     {
2467       /* This node already exists. Grab the current pristine properties. */
2468       SVN_ERR(svn_wc__db_read_pristine_props(&base_props,
2469                                              eb->db, db->local_abspath,
2470                                              scratch_pool, scratch_pool));
2471     }
2472   else if (!db->adding_dir)
2473     {
2474       /* Get the BASE properties for proper merging. */
2475       SVN_ERR(svn_wc__db_base_get_props(&base_props,
2476                                         eb->db, db->local_abspath,
2477                                         scratch_pool, scratch_pool));
2478     }
2479   else
2480     base_props = apr_hash_make(pool);
2481 
2482   /* An incomplete directory might have props which were supposed to be
2483      deleted but weren't.  Because the server sent us all the props we're
2484      supposed to have, any previous base props not in this list must be
2485      deleted (issue #1672). */
2486   if (db->was_incomplete)
2487     {
2488       int i;
2489       apr_hash_t *props_to_delete;
2490       apr_hash_index_t *hi;
2491 
2492       /* In a copy of the BASE props, remove every property that we see an
2493          incoming change for. The remaining unmentioned properties are those
2494          which need to be deleted.  */
2495       props_to_delete = apr_hash_copy(pool, base_props);
2496       for (i = 0; i < regular_prop_changes->nelts; i++)
2497         {
2498           const svn_prop_t *prop;
2499           prop = &APR_ARRAY_IDX(regular_prop_changes, i, svn_prop_t);
2500           svn_hash_sets(props_to_delete, prop->name, NULL);
2501         }
2502 
2503       /* Add these props to the incoming propchanges (in
2504        * regular_prop_changes).  */
2505       for (hi = apr_hash_first(pool, props_to_delete);
2506            hi != NULL;
2507            hi = apr_hash_next(hi))
2508         {
2509           const char *propname = apr_hash_this_key(hi);
2510           svn_prop_t *prop = apr_array_push(regular_prop_changes);
2511 
2512           /* Record a deletion for PROPNAME.  */
2513           prop->name = propname;
2514           prop->value = NULL;
2515         }
2516     }
2517 
2518   /* If this directory has property changes stored up, now is the time
2519      to deal with them. */
2520   if (regular_prop_changes->nelts)
2521     {
2522       /* If recording traversal info, then see if the
2523          SVN_PROP_EXTERNALS property on this directory changed,
2524          and record before and after for the change. */
2525       if (eb->external_func)
2526         {
2527           const svn_prop_t *change
2528             = externals_prop_changed(regular_prop_changes);
2529 
2530           if (change)
2531             {
2532               const svn_string_t *new_val_s = change->value;
2533               const svn_string_t *old_val_s;
2534 
2535               old_val_s = svn_hash_gets(base_props, SVN_PROP_EXTERNALS);
2536 
2537               if ((new_val_s == NULL) && (old_val_s == NULL))
2538                 ; /* No value before, no value after... so do nothing. */
2539               else if (new_val_s && old_val_s
2540                        && (svn_string_compare(old_val_s, new_val_s)))
2541                 ; /* Value did not change... so do nothing. */
2542               else if (old_val_s || new_val_s)
2543                 /* something changed, record the change */
2544                 {
2545                   SVN_ERR((eb->external_func)(
2546                                        eb->external_baton,
2547                                        db->local_abspath,
2548                                        old_val_s,
2549                                        new_val_s,
2550                                        db->ambient_depth,
2551                                        db->pool));
2552                 }
2553             }
2554         }
2555 
2556       if (db->shadowed)
2557         {
2558           /* We don't have a relevant actual row, but we need actual properties
2559              to allow property merging without conflicts. */
2560           if (db->adding_dir)
2561             actual_props = apr_hash_make(scratch_pool);
2562           else
2563             actual_props = base_props;
2564         }
2565 
2566       /* Merge pending properties. */
2567       new_base_props = svn_prop__patch(base_props, regular_prop_changes,
2568                                        db->pool);
2569       SVN_ERR_W(svn_wc__merge_props(&conflict_skel,
2570                                     &prop_state,
2571                                     &new_actual_props,
2572                                     eb->db,
2573                                     db->local_abspath,
2574                                     NULL /* use baseprops */,
2575                                     base_props,
2576                                     actual_props,
2577                                     regular_prop_changes,
2578                                     db->pool,
2579                                     scratch_pool),
2580                 _("Couldn't do property merge"));
2581       /* After a (not-dry-run) merge, we ALWAYS have props to save.  */
2582       SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
2583     }
2584 
2585   SVN_ERR(accumulate_last_change(&new_changed_rev, &new_changed_date,
2586                                  &new_changed_author, entry_prop_changes,
2587                                  scratch_pool, scratch_pool));
2588 
2589   /* Check if we should add some not-present markers before marking the
2590      directory complete (Issue #3569) */
2591   {
2592     apr_hash_t *new_children = svn_hash_gets(eb->dir_dirents,
2593                                              db->new_repos_relpath);
2594 
2595     if (new_children != NULL)
2596       {
2597         apr_hash_index_t *hi;
2598         apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2599 
2600         for (hi = apr_hash_first(scratch_pool, new_children);
2601              hi;
2602              hi = apr_hash_next(hi))
2603           {
2604             const char *child_name;
2605             const char *child_abspath;
2606             const char *child_relpath;
2607             const svn_dirent_t *dirent;
2608             svn_wc__db_status_t status;
2609             svn_node_kind_t child_kind;
2610             svn_error_t *err;
2611 
2612             svn_pool_clear(iterpool);
2613 
2614             child_name = apr_hash_this_key(hi);
2615             child_abspath = svn_dirent_join(db->local_abspath, child_name,
2616                                             iterpool);
2617 
2618             dirent = apr_hash_this_val(hi);
2619             child_kind = (dirent->kind == svn_node_dir)
2620                                         ? svn_node_dir
2621                                         : svn_node_file;
2622 
2623             if (db->ambient_depth < svn_depth_immediates
2624                 && child_kind == svn_node_dir)
2625               continue; /* We don't need the subdirs */
2626 
2627             /* ### We just check if there is some node in BASE at this path */
2628             err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL,
2629                                            NULL, NULL, NULL, NULL, NULL, NULL,
2630                                            NULL, NULL, NULL, NULL, NULL,
2631                                            eb->db, child_abspath,
2632                                            iterpool, iterpool);
2633 
2634             if (!err)
2635               {
2636                 svn_boolean_t is_wcroot;
2637                 SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, eb->db, child_abspath,
2638                                              iterpool));
2639 
2640                 if (!is_wcroot)
2641                   continue; /* Everything ok... Nothing to do here */
2642                 /* Fall through to allow recovering later */
2643               }
2644             else if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2645               return svn_error_trace(err);
2646 
2647             svn_error_clear(err);
2648 
2649             child_relpath = svn_relpath_join(db->new_repos_relpath, child_name,
2650                                              iterpool);
2651 
2652             SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db,
2653                                                          child_abspath,
2654                                                          child_relpath,
2655                                                          eb->repos_root,
2656                                                          eb->repos_uuid,
2657                                                          *eb->target_revision,
2658                                                          child_kind,
2659                                                          NULL, NULL,
2660                                                          iterpool));
2661           }
2662 
2663         svn_pool_destroy(iterpool);
2664       }
2665   }
2666 
2667   if (apr_hash_count(db->not_present_nodes))
2668     {
2669       apr_hash_index_t *hi;
2670       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2671 
2672       /* This should call some new function (which could also be used
2673          for new_children above) to add all the names in single
2674          transaction, but I can't even trigger it.  I've tried
2675          ra_local, ra_svn, ra_neon, ra_serf and they all call
2676          close_file before close_dir. */
2677       for (hi = apr_hash_first(scratch_pool, db->not_present_nodes);
2678            hi;
2679            hi = apr_hash_next(hi))
2680         {
2681           const char *child = apr_hash_this_key(hi);
2682           const char *child_abspath, *child_relpath;
2683           svn_node_kind_t kind = svn_node_kind_from_word(apr_hash_this_val(hi));
2684 
2685           svn_pool_clear(iterpool);
2686 
2687           child_abspath = svn_dirent_join(db->local_abspath, child, iterpool);
2688           child_relpath = svn_dirent_join(db->new_repos_relpath, child, iterpool);
2689 
2690           SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db,
2691                                                        child_abspath,
2692                                                        child_relpath,
2693                                                        eb->repos_root,
2694                                                        eb->repos_uuid,
2695                                                        *eb->target_revision,
2696                                                        kind,
2697                                                        NULL, NULL,
2698                                                        iterpool));
2699         }
2700       svn_pool_destroy(iterpool);
2701     }
2702 
2703   /* If this directory is merely an anchor for a targeted child, then we
2704      should not be updating the node at all.  */
2705   if (db->parent_baton == NULL
2706       && *eb->target_basename != '\0')
2707     {
2708       /* And we should not have received any changes!  */
2709       SVN_ERR_ASSERT(db->propchanges->nelts == 0);
2710       /* ... which also implies NEW_CHANGED_* are not set,
2711          and NEW_BASE_PROPS == NULL.  */
2712     }
2713   else
2714     {
2715       apr_hash_t *props;
2716       apr_array_header_t *iprops = NULL;
2717 
2718       /* ### we know a base node already exists. it was created in
2719          ### open_directory or add_directory.  let's just preserve the
2720          ### existing DEPTH value, and possibly CHANGED_*.  */
2721       /* If we received any changed_* values, then use them.  */
2722       if (SVN_IS_VALID_REVNUM(new_changed_rev))
2723         db->changed_rev = new_changed_rev;
2724       if (new_changed_date != 0)
2725         db->changed_date = new_changed_date;
2726       if (new_changed_author != NULL)
2727         db->changed_author = new_changed_author;
2728 
2729       /* If no depth is set yet, set to infinity. */
2730       if (db->ambient_depth == svn_depth_unknown)
2731         db->ambient_depth = svn_depth_infinity;
2732 
2733       if (eb->depth_is_sticky
2734           && db->ambient_depth != eb->requested_depth)
2735         {
2736           /* After a depth upgrade the entry must reflect the new depth.
2737              Upgrading to infinity changes the depth of *all* directories,
2738              upgrading to something else only changes the target. */
2739 
2740           if (eb->requested_depth == svn_depth_infinity
2741               || (strcmp(db->local_abspath, eb->target_abspath) == 0
2742                   && eb->requested_depth > db->ambient_depth))
2743             {
2744               db->ambient_depth = eb->requested_depth;
2745             }
2746         }
2747 
2748       /* Do we have new properties to install? Or shall we simply retain
2749          the prior set of properties? If we're installing new properties,
2750          then we also want to write them to an old-style props file.  */
2751       props = new_base_props;
2752       if (props == NULL)
2753         props = base_props;
2754 
2755       if (conflict_skel)
2756         {
2757           svn_skel_t *work_item;
2758 
2759           SVN_ERR(complete_conflict(conflict_skel,
2760                                     db->edit_baton,
2761                                     db->local_abspath,
2762                                     db->old_repos_relpath,
2763                                     db->old_revision,
2764                                     db->new_repos_relpath,
2765                                     svn_node_dir, svn_node_dir,
2766                                     (db->parent_baton
2767                                      && db->parent_baton->deletion_conflicts)
2768                                       ? svn_hash_gets(
2769                                             db->parent_baton->deletion_conflicts,
2770                                             db->name)
2771                                       : NULL,
2772                                     db->pool, scratch_pool));
2773 
2774           SVN_ERR(svn_wc__conflict_create_markers(&work_item,
2775                                                   eb->db, db->local_abspath,
2776                                                   conflict_skel,
2777                                                   scratch_pool, scratch_pool));
2778 
2779           all_work_items = svn_wc__wq_merge(all_work_items, work_item,
2780                                             scratch_pool);
2781         }
2782 
2783       /* Any inherited props to be set set for this base node? */
2784       if (eb->wcroot_iprops)
2785         {
2786           iprops = svn_hash_gets(eb->wcroot_iprops, db->local_abspath);
2787 
2788           /* close_edit may also update iprops for switched nodes, catching
2789              those for which close_directory is never called (e.g. a switch
2790              with no changes).  So as a minor optimization we remove any
2791              iprops from the hash so as not to set them again in
2792              close_edit. */
2793           if (iprops)
2794             svn_hash_sets(eb->wcroot_iprops, db->local_abspath, NULL);
2795         }
2796 
2797       /* Update the BASE data for the directory and mark the directory
2798          complete */
2799       SVN_ERR(svn_wc__db_base_add_directory(
2800                 eb->db, db->local_abspath,
2801                 eb->wcroot_abspath,
2802                 db->new_repos_relpath,
2803                 eb->repos_root, eb->repos_uuid,
2804                 *eb->target_revision,
2805                 props,
2806                 db->changed_rev, db->changed_date, db->changed_author,
2807                 NULL /* children */,
2808                 db->ambient_depth,
2809                 (dav_prop_changes->nelts > 0)
2810                     ? svn_prop_array_to_hash(dav_prop_changes, pool)
2811                     : NULL,
2812                 (! db->shadowed) && new_base_props != NULL,
2813                 new_actual_props, iprops,
2814                 conflict_skel, all_work_items,
2815                 scratch_pool));
2816     }
2817 
2818   /* Process all of the queued work items for this directory.  */
2819   SVN_ERR(svn_wc__wq_run(eb->db, db->local_abspath,
2820                          eb->cancel_func, eb->cancel_baton,
2821                          scratch_pool));
2822 
2823   if (db->parent_baton)
2824     svn_hash_sets(db->parent_baton->not_present_nodes, db->name, NULL);
2825 
2826   if (conflict_skel && eb->conflict_func)
2827     SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath,
2828                                              svn_node_dir,
2829                                              conflict_skel,
2830                                              NULL /* merge_options */,
2831                                              eb->conflict_func,
2832                                              eb->conflict_baton,
2833                                              eb->cancel_func,
2834                                              eb->cancel_baton,
2835                                              scratch_pool));
2836 
2837   /* Notify of any prop changes on this directory -- but do nothing if
2838      it's an added or skipped directory, because notification has already
2839      happened in that case - unless the add was obstructed by a dir
2840      scheduled for addition without history, in which case we handle
2841      notification here). */
2842   if (!db->already_notified && eb->notify_func && db->edited)
2843     {
2844       svn_wc_notify_t *notify;
2845       svn_wc_notify_action_t action;
2846 
2847       if (db->shadowed || db->edit_obstructed)
2848         action = svn_wc_notify_update_shadowed_update;
2849       else if (db->obstruction_found || db->add_existed)
2850         action = svn_wc_notify_exists;
2851       else
2852         action = svn_wc_notify_update_update;
2853 
2854       notify = svn_wc_create_notify(db->local_abspath, action, pool);
2855       notify->kind = svn_node_dir;
2856       notify->prop_state = prop_state;
2857       notify->revision = *eb->target_revision;
2858       notify->old_revision = db->old_revision;
2859 
2860       eb->notify_func(eb->notify_baton, notify, scratch_pool);
2861     }
2862 
2863   if (db->edited)
2864     eb->edited = db->edited;
2865 
2866   /* We're done with this directory, so remove one reference from the
2867      bump information. */
2868   SVN_ERR(maybe_release_dir_info(db));
2869 
2870   return SVN_NO_ERROR;
2871 }
2872 
2873 
2874 /* Common code for 'absent_file' and 'absent_directory'. */
2875 static svn_error_t *
absent_node(const char * path,svn_node_kind_t absent_kind,void * parent_baton,apr_pool_t * pool)2876 absent_node(const char *path,
2877             svn_node_kind_t absent_kind,
2878             void *parent_baton,
2879             apr_pool_t *pool)
2880 {
2881   struct dir_baton *pb = parent_baton;
2882   struct edit_baton *eb = pb->edit_baton;
2883   apr_pool_t *scratch_pool = svn_pool_create(pool);
2884   const char *name = svn_dirent_basename(path, NULL);
2885   const char *local_abspath;
2886   svn_error_t *err;
2887   svn_wc__db_status_t status;
2888   svn_node_kind_t kind;
2889   svn_skel_t *tree_conflict = NULL;
2890 
2891   if (pb->skip_this)
2892     return SVN_NO_ERROR;
2893 
2894   local_abspath = svn_dirent_join(pb->local_abspath, name, scratch_pool);
2895   /* If an item by this name is scheduled for addition that's a
2896      genuine tree-conflict.  */
2897   err = svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
2898                              NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2899                              NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2900                              NULL, NULL, NULL, NULL,
2901                              eb->db, local_abspath,
2902                              scratch_pool, scratch_pool);
2903 
2904   if (err)
2905     {
2906       if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2907         return svn_error_trace(err);
2908 
2909       svn_error_clear(err);
2910       status = svn_wc__db_status_not_present;
2911       kind = svn_node_unknown;
2912     }
2913 
2914   if (status != svn_wc__db_status_server_excluded)
2915     SVN_ERR(mark_directory_edited(pb, scratch_pool));
2916   /* Else fall through as we should update the revision anyway */
2917 
2918   if (status == svn_wc__db_status_normal)
2919     {
2920       svn_boolean_t wcroot;
2921       /* We found an obstructing working copy or a file external! */
2922 
2923       SVN_ERR(svn_wc__db_is_wcroot(&wcroot, eb->db, local_abspath,
2924                                    scratch_pool));
2925 
2926       if (wcroot)
2927         {
2928           /*
2929              We have an obstructing working copy; possibly a directory external
2930 
2931              We can do two things now:
2932              1) notify the user, record a skip, etc.
2933              2) Just record the absent node in BASE in the parent
2934                 working copy.
2935 
2936              As option 2 happens to be exactly what we do anyway, fall through.
2937            */
2938         }
2939       else
2940         {
2941           svn_boolean_t file_external;
2942           svn_revnum_t revnum;
2943 
2944           SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &revnum, NULL, NULL,
2945                                            NULL, NULL, NULL, NULL, NULL, NULL,
2946                                            NULL, NULL, NULL, NULL,
2947                                            &file_external,
2948                                            eb->db, local_abspath,
2949                                            scratch_pool, scratch_pool));
2950 
2951           if (file_external)
2952             {
2953               /* The server asks us to replace a file external
2954                  (Existing BASE node; not reported by the working copy crawler
2955                   or there would have been a delete_entry() call.
2956 
2957                  There is no way we can store this state in the working copy as
2958                  the BASE layer is already filled.
2959                  We could error out, but that is not helping anybody; the user is not
2960                  even seeing with what the file external would be replaced, so let's
2961                  report a skip and continue the update.
2962                */
2963 
2964               if (eb->notify_func)
2965                 {
2966                   svn_wc_notify_t *notify;
2967                   notify = svn_wc_create_notify(
2968                                     local_abspath,
2969                                     svn_wc_notify_update_skip_obstruction,
2970                                     scratch_pool);
2971 
2972                   eb->notify_func(eb->notify_baton, notify, scratch_pool);
2973                 }
2974 
2975               svn_pool_destroy(scratch_pool);
2976               return SVN_NO_ERROR;
2977             }
2978           else
2979             {
2980               /* We have a normal local node that will now be hidden for the
2981                  user. Let's try to delete what is there. This may introduce
2982                  tree conflicts if there are local changes */
2983               SVN_ERR(delete_entry(path, revnum, pb, scratch_pool));
2984 
2985               /* delete_entry() promises that BASE is empty after the operation,
2986                  so we can just fall through now */
2987             }
2988         }
2989     }
2990   else if (status == svn_wc__db_status_not_present
2991            || status == svn_wc__db_status_server_excluded
2992            || status == svn_wc__db_status_excluded)
2993     {
2994       /* The BASE node is not actually there, so we can safely turn it into
2995          an absent node */
2996     }
2997   else
2998     {
2999       /* We have a local addition. If this would be a BASE node it would have
3000          been deleted before we get here. (Which might have turned it into
3001          a copy). */
3002       SVN_ERR_ASSERT(status != svn_wc__db_status_normal);
3003 
3004       if (!pb->shadowed && !pb->edit_obstructed)
3005         SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath,
3006                                     status, FALSE, svn_node_unknown,
3007                                     svn_wc_conflict_action_add,
3008                                     scratch_pool, scratch_pool));
3009 
3010     }
3011 
3012   {
3013     const char *repos_relpath;
3014     repos_relpath = svn_relpath_join(pb->new_repos_relpath, name, scratch_pool);
3015 
3016     if (tree_conflict)
3017       SVN_ERR(complete_conflict(tree_conflict, eb, local_abspath,
3018                                 NULL, SVN_INVALID_REVNUM, repos_relpath,
3019                                 kind, svn_node_unknown, NULL,
3020                                 scratch_pool, scratch_pool));
3021 
3022     /* Insert an excluded node below the parent node to note that this child
3023        is absent. (This puts it in the parent db if the child is obstructed) */
3024     SVN_ERR(svn_wc__db_base_add_excluded_node(eb->db, local_abspath,
3025                                               repos_relpath, eb->repos_root,
3026                                               eb->repos_uuid,
3027                                               *(eb->target_revision),
3028                                               absent_kind,
3029                                               svn_wc__db_status_server_excluded,
3030                                               tree_conflict, NULL,
3031                                               scratch_pool));
3032 
3033     if (tree_conflict)
3034       {
3035         if (eb->conflict_func)
3036           SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath,
3037                                                    kind,
3038                                                    tree_conflict,
3039                                                    NULL /* merge_options */,
3040                                                    eb->conflict_func,
3041                                                    eb->conflict_baton,
3042                                                    eb->cancel_func,
3043                                                    eb->cancel_baton,
3044                                                    scratch_pool));
3045         do_notification(eb, local_abspath, kind, svn_wc_notify_tree_conflict,
3046                         scratch_pool);
3047       }
3048   }
3049 
3050   svn_pool_destroy(scratch_pool);
3051 
3052   return SVN_NO_ERROR;
3053 }
3054 
3055 
3056 /* An svn_delta_editor_t function. */
3057 static svn_error_t *
absent_file(const char * path,void * parent_baton,apr_pool_t * pool)3058 absent_file(const char *path,
3059             void *parent_baton,
3060             apr_pool_t *pool)
3061 {
3062   return absent_node(path, svn_node_file, parent_baton, pool);
3063 }
3064 
3065 
3066 /* An svn_delta_editor_t function. */
3067 static svn_error_t *
absent_directory(const char * path,void * parent_baton,apr_pool_t * pool)3068 absent_directory(const char *path,
3069                  void *parent_baton,
3070                  apr_pool_t *pool)
3071 {
3072   return absent_node(path, svn_node_dir, parent_baton, pool);
3073 }
3074 
3075 
3076 /* An svn_delta_editor_t function. */
3077 static svn_error_t *
add_file(const char * path,void * parent_baton,const char * copyfrom_path,svn_revnum_t copyfrom_rev,apr_pool_t * pool,void ** file_baton)3078 add_file(const char *path,
3079          void *parent_baton,
3080          const char *copyfrom_path,
3081          svn_revnum_t copyfrom_rev,
3082          apr_pool_t *pool,
3083          void **file_baton)
3084 {
3085   struct dir_baton *pb = parent_baton;
3086   struct edit_baton *eb = pb->edit_baton;
3087   struct file_baton *fb;
3088   svn_node_kind_t kind;
3089   svn_node_kind_t wc_kind;
3090   svn_wc__db_status_t status;
3091   apr_pool_t *scratch_pool;
3092   svn_boolean_t conflicted;
3093   svn_boolean_t conflict_ignored = FALSE;
3094   svn_boolean_t versioned_locally_and_present;
3095   svn_skel_t *tree_conflict = NULL;
3096   svn_error_t *err = SVN_NO_ERROR;
3097 
3098   SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev)));
3099 
3100   SVN_ERR(make_file_baton(&fb, pb, path, TRUE, pool));
3101   *file_baton = fb;
3102 
3103   if (fb->skip_this)
3104     return SVN_NO_ERROR;
3105 
3106   SVN_ERR(calculate_repos_relpath(&fb->new_repos_relpath, fb->local_abspath,
3107                                   NULL, eb, pb, fb->pool, pool));
3108   SVN_ERR(mark_file_edited(fb, pool));
3109 
3110   /* The file_pool can stick around for a *long* time, so we want to
3111      use a subpool for any temporary allocations. */
3112   scratch_pool = svn_pool_create(pool);
3113 
3114 
3115   /* It may not be named the same as the administrative directory. */
3116   if (svn_wc_is_adm_dir(fb->name, pool))
3117     return svn_error_createf(
3118        SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
3119        _("Failed to add file '%s': object of the same name as the "
3120          "administrative directory"),
3121        svn_dirent_local_style(fb->local_abspath, pool));
3122 
3123   if (!eb->clean_checkout)
3124     {
3125       SVN_ERR(svn_io_check_path(fb->local_abspath, &kind, scratch_pool));
3126 
3127       err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL,
3128                                 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3129                                 NULL, NULL, NULL, NULL, NULL,
3130                                 &conflicted, NULL, NULL, NULL, NULL, NULL, NULL,
3131                                 eb->db, fb->local_abspath,
3132                                 scratch_pool, scratch_pool);
3133     }
3134   else
3135     {
3136       kind =  svn_node_none;
3137       status = svn_wc__db_status_not_present;
3138       wc_kind = svn_node_unknown;
3139       conflicted = FALSE;
3140     }
3141 
3142   if (err)
3143     {
3144       if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3145         return svn_error_trace(err);
3146 
3147       svn_error_clear(err);
3148       wc_kind = svn_node_unknown;
3149       conflicted = FALSE;
3150 
3151       versioned_locally_and_present = FALSE;
3152     }
3153   else if (status == svn_wc__db_status_normal && wc_kind == svn_node_unknown)
3154     {
3155       SVN_ERR_ASSERT(conflicted);
3156       versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */
3157     }
3158   else if (status == svn_wc__db_status_normal
3159            || status == svn_wc__db_status_incomplete)
3160     {
3161       svn_boolean_t root;
3162 
3163       SVN_ERR(svn_wc__db_is_wcroot(&root, eb->db, fb->local_abspath,
3164                                    scratch_pool));
3165 
3166       if (root)
3167         {
3168           /* !! We found the root of a working copy obstructing the wc !!
3169 
3170              If the directory would be part of our own working copy then
3171              we wouldn't have been called as an add_directory().
3172 
3173              The only thing we can do is add a not-present node, to allow
3174              a future update to bring in the new files when the problem is
3175              resolved.  Note that svn_wc__db_base_add_not_present_node()
3176              explicitly adds the node into the parent's node database. */
3177 
3178           svn_hash_sets(pb->not_present_nodes,
3179                         apr_pstrdup(pb->pool, fb->name),
3180                         svn_node_kind_to_word(svn_node_dir));
3181         }
3182       else if (wc_kind == svn_node_dir)
3183         {
3184           /* We have an editor violation. Github sometimes does this
3185              in its subversion compatibility code, when changing the
3186              depth of a working copy, or on updates from incomplete */
3187         }
3188       else
3189         {
3190           /* We found a file external occupating the place we need in BASE.
3191 
3192              We can't add a not-present node in this case as that would overwrite
3193              the file external. Luckily the file external itself stops us from
3194              forgetting a child of this parent directory like an obstructing
3195              working copy would.
3196 
3197              The reason we get here is that the adm crawler doesn't report
3198              file externals.
3199            */
3200           SVN_ERR_ASSERT(wc_kind == svn_node_file
3201                          || wc_kind == svn_node_symlink);
3202         }
3203 
3204       SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3205       fb->skip_this = TRUE;
3206       fb->already_notified = TRUE;
3207 
3208       do_notification(eb, fb->local_abspath, wc_kind,
3209                       svn_wc_notify_update_skip_obstruction, scratch_pool);
3210 
3211       svn_pool_destroy(scratch_pool);
3212 
3213       return SVN_NO_ERROR;
3214     }
3215   else
3216     versioned_locally_and_present = IS_NODE_PRESENT(status);
3217 
3218 
3219   /* Is this path a conflict victim? */
3220   if (fb->shadowed)
3221     conflicted = FALSE; /* Conflict applies to WORKING */
3222   else if (conflicted)
3223     {
3224       if (pb->deletion_conflicts)
3225         tree_conflict = svn_hash_gets(pb->deletion_conflicts, fb->name);
3226 
3227       if (tree_conflict)
3228         {
3229           svn_wc_conflict_reason_t reason;
3230           const char *move_src_op_root_abspath;
3231           const char *move_dst_op_root_abspath;
3232           /* So this deletion wasn't just a deletion, it is actually a
3233              replacement. Let's install a better tree conflict. */
3234 
3235           SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
3236                                                       &move_src_op_root_abspath,
3237                                                       &move_dst_op_root_abspath,
3238                                                       eb->db,
3239                                                       fb->local_abspath,
3240                                                       tree_conflict,
3241                                                       fb->pool, scratch_pool));
3242 
3243           tree_conflict = svn_wc__conflict_skel_create(fb->pool);
3244 
3245           SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3246                                         tree_conflict,
3247                                         eb->db, fb->local_abspath,
3248                                         reason, svn_wc_conflict_action_replace,
3249                                         move_src_op_root_abspath,
3250                                         move_dst_op_root_abspath,
3251                                         fb->pool, scratch_pool));
3252 
3253           /* And now stop checking for conflicts here and just perform
3254              a shadowed update */
3255           fb->edit_conflict = tree_conflict; /* Cache for close_file */
3256           tree_conflict = NULL; /* No direct notification */
3257           fb->shadowed = TRUE; /* Just continue */
3258           conflicted = FALSE; /* No skip */
3259         }
3260       else
3261         SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
3262                                         eb->db, fb->local_abspath, pool));
3263     }
3264 
3265   /* Now the usual conflict handling: skip. */
3266   if (conflicted)
3267     {
3268       SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3269 
3270       fb->skip_this = TRUE;
3271       fb->already_notified = TRUE;
3272 
3273       /* We skip this node, but once the update completes the parent node will
3274          be updated to the new revision. So a future recursive update of the
3275          parent will not bring in this new node as the revision of the parent
3276          describes to the repository that all children are available.
3277 
3278          To resolve this problem, we add a not-present node to allow bringing
3279          the node in once this conflict is resolved.
3280 
3281          Note that we can safely assume that no present base node exists,
3282          because then we would not have received an add_file.
3283        */
3284       svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, fb->name),
3285                     svn_node_kind_to_word(svn_node_file));
3286 
3287       do_notification(eb, fb->local_abspath, svn_node_file,
3288                       svn_wc_notify_skip_conflicted, scratch_pool);
3289 
3290       svn_pool_destroy(scratch_pool);
3291 
3292       return SVN_NO_ERROR;
3293     }
3294   else if (conflict_ignored)
3295     {
3296       fb->shadowed = TRUE;
3297     }
3298 
3299   if (fb->shadowed)
3300     {
3301       /* Nothing to check; does not and will not exist in working copy */
3302     }
3303   else if (versioned_locally_and_present)
3304     {
3305       /* What to do with a versioned or schedule-add file:
3306 
3307          If the UUID doesn't match the parent's, or the URL isn't a child of
3308          the parent dir's URL, it's an error.
3309 
3310          Set add_existed so that user notification is delayed until after any
3311          text or prop conflicts have been found.
3312 
3313          Whether the incoming add is a symlink or a file will only be known in
3314          close_file(), when the props are known. So with a locally added file
3315          or symlink, let close_file() check for a tree conflict.
3316 
3317          We will never see missing files here, because these would be
3318          re-added during the crawler phase. */
3319       svn_boolean_t local_is_file;
3320 
3321       /* Is the local node a copy or move */
3322       if (status == svn_wc__db_status_added)
3323         SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL, NULL,
3324                                          NULL, NULL, NULL,
3325                                          eb->db, fb->local_abspath,
3326                                          scratch_pool, scratch_pool));
3327 
3328       /* Is there something that is a file? */
3329       local_is_file = (wc_kind == svn_node_file
3330                        || wc_kind == svn_node_symlink);
3331 
3332       /* Do tree conflict checking if
3333        *  - if there is a local copy.
3334        *  - if this is a switch operation
3335        *  - the node kinds mismatch
3336        *
3337        * During switch, local adds at the same path as incoming adds get
3338        * "lost" in that switching back to the original will no longer have the
3339        * local add. So switch always alerts the user with a tree conflict. */
3340       if (!eb->adds_as_modification
3341           || !local_is_file
3342           || status != svn_wc__db_status_added)
3343         {
3344           SVN_ERR(check_tree_conflict(&tree_conflict, eb,
3345                                       fb->local_abspath,
3346                                       status, FALSE, svn_node_none,
3347                                       svn_wc_conflict_action_add,
3348                                       fb->pool, scratch_pool));
3349         }
3350 
3351       if (tree_conflict == NULL)
3352         fb->add_existed = TRUE; /* Take over WORKING */
3353       else
3354         fb->shadowed = TRUE; /* Only update BASE */
3355 
3356     }
3357   else if (kind != svn_node_none)
3358     {
3359       /* There's an unversioned node at this path. */
3360       fb->obstruction_found = TRUE;
3361 
3362       /* Unversioned, obstructing files are handled by text merge/conflict,
3363        * if unversioned obstructions are allowed. */
3364       if (! (kind == svn_node_file && eb->allow_unver_obstructions))
3365         {
3366           /* Bring in the node as deleted */ /* ### Obstructed Conflict */
3367           fb->shadowed = TRUE;
3368 
3369           /* Mark a conflict */
3370           tree_conflict = svn_wc__conflict_skel_create(fb->pool);
3371 
3372           SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3373                                         tree_conflict,
3374                                         eb->db, fb->local_abspath,
3375                                         svn_wc_conflict_reason_unversioned,
3376                                         svn_wc_conflict_action_add,
3377                                         NULL, NULL,
3378                                         fb->pool, scratch_pool));
3379         }
3380     }
3381 
3382   /* When this is not the update target add a not-present BASE node now,
3383      to allow marking the parent directory complete in its close_edit() call.
3384      This resolves issues when that occurs before the close_file(). */
3385   if (pb->parent_baton
3386       || *eb->target_basename == '\0'
3387       || (strcmp(fb->local_abspath, eb->target_abspath) != 0))
3388     {
3389       svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, fb->name),
3390                     svn_node_kind_to_word(svn_node_file));
3391     }
3392 
3393   if (tree_conflict != NULL)
3394     {
3395       SVN_ERR(complete_conflict(tree_conflict,
3396                                 fb->edit_baton,
3397                                 fb->local_abspath,
3398                                 fb->old_repos_relpath,
3399                                 fb->old_revision,
3400                                 fb->new_repos_relpath,
3401                                 wc_kind, svn_node_file,
3402                                 pb->deletion_conflicts
3403                                   ? svn_hash_gets(pb->deletion_conflicts,
3404                                                   fb->name)
3405                                   : NULL,
3406                                 fb->pool, scratch_pool));
3407 
3408       SVN_ERR(svn_wc__db_op_mark_conflict(eb->db,
3409                                           fb->local_abspath,
3410                                           tree_conflict, NULL,
3411                                           scratch_pool));
3412 
3413       fb->edit_conflict = tree_conflict;
3414 
3415       fb->already_notified = TRUE;
3416       do_notification(eb, fb->local_abspath, svn_node_file,
3417                       svn_wc_notify_tree_conflict, scratch_pool);
3418     }
3419 
3420   svn_pool_destroy(scratch_pool);
3421 
3422   return SVN_NO_ERROR;
3423 }
3424 
3425 
3426 /* An svn_delta_editor_t function. */
3427 static svn_error_t *
open_file(const char * path,void * parent_baton,svn_revnum_t base_revision,apr_pool_t * pool,void ** file_baton)3428 open_file(const char *path,
3429           void *parent_baton,
3430           svn_revnum_t base_revision,
3431           apr_pool_t *pool,
3432           void **file_baton)
3433 {
3434   struct dir_baton *pb = parent_baton;
3435   struct edit_baton *eb = pb->edit_baton;
3436   struct file_baton *fb;
3437   svn_boolean_t conflicted;
3438   svn_boolean_t conflict_ignored = FALSE;
3439   svn_boolean_t have_work;
3440   svn_wc__db_status_t status;
3441   svn_node_kind_t wc_kind;
3442   svn_skel_t *tree_conflict = NULL;
3443 
3444   /* the file_pool can stick around for a *long* time, so we want to use
3445      a subpool for any temporary allocations. */
3446   apr_pool_t *scratch_pool = svn_pool_create(pool);
3447 
3448   SVN_ERR(make_file_baton(&fb, pb, path, FALSE, pool));
3449   *file_baton = fb;
3450 
3451   if (fb->skip_this)
3452     return SVN_NO_ERROR;
3453 
3454   /* Detect obstructing working copies */
3455   {
3456     svn_boolean_t is_root;
3457 
3458     SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, fb->local_abspath,
3459                                  pool));
3460 
3461     if (is_root)
3462       {
3463         /* Just skip this node; a future update will handle it */
3464         SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3465         fb->skip_this = TRUE;
3466         fb->already_notified = TRUE;
3467 
3468         do_notification(eb, fb->local_abspath, svn_node_file,
3469                         svn_wc_notify_update_skip_obstruction, pool);
3470 
3471         return SVN_NO_ERROR;
3472       }
3473   }
3474 
3475   /* Sanity check. */
3476 
3477   SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &fb->old_revision,
3478                                &fb->old_repos_relpath, NULL, NULL,
3479                                &fb->changed_rev, &fb->changed_date,
3480                                &fb->changed_author, NULL,
3481                                &fb->original_checksum, NULL, NULL, NULL,
3482                                NULL, NULL, NULL, NULL, NULL, NULL,
3483                                &conflicted, NULL, NULL, &fb->local_prop_mods,
3484                                NULL, NULL, &have_work,
3485                                eb->db, fb->local_abspath,
3486                                fb->pool, scratch_pool));
3487 
3488   if (have_work)
3489     SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &fb->old_revision,
3490                                      &fb->old_repos_relpath, NULL, NULL,
3491                                      &fb->changed_rev, &fb->changed_date,
3492                                      &fb->changed_author, NULL,
3493                                      &fb->original_checksum, NULL, NULL,
3494                                      NULL, NULL, NULL,
3495                                      eb->db, fb->local_abspath,
3496                                      fb->pool, scratch_pool));
3497 
3498   SVN_ERR(calculate_repos_relpath(&fb->new_repos_relpath, fb->local_abspath,
3499                                   fb->old_repos_relpath, eb, pb,
3500                                   fb->pool, scratch_pool));
3501 
3502   /* Is this path a conflict victim? */
3503   if (fb->shadowed)
3504     conflicted = FALSE; /* Conflict applies to WORKING */
3505   else if (conflicted)
3506     SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
3507                                     eb->db, fb->local_abspath, pool));
3508   if (conflicted)
3509     {
3510       SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3511 
3512       fb->skip_this = TRUE;
3513       fb->already_notified = TRUE;
3514 
3515       do_notification(eb, fb->local_abspath, svn_node_unknown,
3516                       svn_wc_notify_skip_conflicted, scratch_pool);
3517 
3518       svn_pool_destroy(scratch_pool);
3519 
3520       return SVN_NO_ERROR;
3521     }
3522   else if (conflict_ignored)
3523     {
3524       fb->shadowed = TRUE;
3525     }
3526 
3527   /* Check for conflicts only when we haven't already recorded
3528    * a tree-conflict on a parent node. */
3529   if (!fb->shadowed)
3530     SVN_ERR(check_tree_conflict(&tree_conflict, eb, fb->local_abspath,
3531                                 status, TRUE, svn_node_file,
3532                                 svn_wc_conflict_action_edit,
3533                                 fb->pool, scratch_pool));
3534 
3535   /* Is this path the victim of a newly-discovered tree conflict? */
3536   if (tree_conflict != NULL)
3537     {
3538       svn_wc_conflict_reason_t reason;
3539       fb->edit_conflict = tree_conflict;
3540       /* Other modifications wouldn't be a tree conflict */
3541 
3542       SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL, NULL,
3543                                                   eb->db, fb->local_abspath,
3544                                                   tree_conflict,
3545                                                   scratch_pool, scratch_pool));
3546       SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
3547                      || reason == svn_wc_conflict_reason_moved_away
3548                      || reason == svn_wc_conflict_reason_replaced
3549                      || reason == svn_wc_conflict_reason_obstructed);
3550 
3551       /* Continue updating BASE */
3552       if (reason == svn_wc_conflict_reason_obstructed)
3553         fb->edit_obstructed = TRUE;
3554       else
3555         fb->shadowed = TRUE;
3556     }
3557 
3558   svn_pool_destroy(scratch_pool);
3559 
3560   return SVN_NO_ERROR;
3561 }
3562 
3563 /* Implements svn_stream_lazyopen_func_t. */
3564 static svn_error_t *
lazy_open_source(svn_stream_t ** stream,void * baton,apr_pool_t * result_pool,apr_pool_t * scratch_pool)3565 lazy_open_source(svn_stream_t **stream,
3566                  void *baton,
3567                  apr_pool_t *result_pool,
3568                  apr_pool_t *scratch_pool)
3569 {
3570   struct file_baton *fb = baton;
3571 
3572   SVN_ERR(svn_wc__db_pristine_read(stream, NULL, fb->edit_baton->db,
3573                                    fb->local_abspath,
3574                                    fb->original_checksum,
3575                                    result_pool, scratch_pool));
3576 
3577 
3578   return SVN_NO_ERROR;
3579 }
3580 
3581 /* Implements svn_stream_lazyopen_func_t. */
3582 static svn_error_t *
lazy_open_target(svn_stream_t ** stream,void * baton,apr_pool_t * result_pool,apr_pool_t * scratch_pool)3583 lazy_open_target(svn_stream_t **stream,
3584                  void *baton,
3585                  apr_pool_t *result_pool,
3586                  apr_pool_t *scratch_pool)
3587 {
3588   struct handler_baton *hb = baton;
3589   svn_wc__db_install_data_t *install_data;
3590 
3591   /* By convention return value is undefined on error, but we rely
3592      on HB->INSTALL_DATA value in window_handler() and abort
3593      INSTALL_STREAM if is not NULL on error.
3594      So we store INSTALL_DATA to local variable first, to leave
3595      HB->INSTALL_DATA unchanged on error. */
3596   SVN_ERR(svn_wc__db_pristine_prepare_install(stream,
3597                                               &install_data,
3598                                               &hb->new_text_base_sha1_checksum,
3599                                               NULL,
3600                                               hb->fb->edit_baton->db,
3601                                               hb->fb->dir_baton->local_abspath,
3602                                               result_pool, scratch_pool));
3603 
3604   hb->install_data = install_data;
3605 
3606   return SVN_NO_ERROR;
3607 }
3608 
3609 /* An svn_delta_editor_t function. */
3610 static svn_error_t *
apply_textdelta(void * file_baton,const char * expected_checksum,apr_pool_t * pool,svn_txdelta_window_handler_t * handler,void ** handler_baton)3611 apply_textdelta(void *file_baton,
3612                 const char *expected_checksum,
3613                 apr_pool_t *pool,
3614                 svn_txdelta_window_handler_t *handler,
3615                 void **handler_baton)
3616 {
3617   struct file_baton *fb = file_baton;
3618   apr_pool_t *handler_pool = svn_pool_create(fb->pool);
3619   struct handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb));
3620   struct edit_baton *eb = fb->edit_baton;
3621   const svn_checksum_t *recorded_base_checksum;
3622   svn_checksum_t *expected_base_checksum;
3623   svn_stream_t *source;
3624   svn_stream_t *target;
3625 
3626   if (fb->skip_this)
3627     {
3628       *handler = svn_delta_noop_window_handler;
3629       *handler_baton = NULL;
3630       return SVN_NO_ERROR;
3631     }
3632 
3633   SVN_ERR(mark_file_edited(fb, pool));
3634 
3635   /* Parse checksum or sets expected_base_checksum to NULL */
3636   SVN_ERR(svn_checksum_parse_hex(&expected_base_checksum, svn_checksum_md5,
3637                                  expected_checksum, pool));
3638 
3639   /* Before applying incoming svndiff data to text base, make sure
3640      text base hasn't been corrupted, and that its checksum
3641      matches the expected base checksum. */
3642 
3643   /* The incoming delta is targeted against EXPECTED_BASE_CHECKSUM. Find and
3644      check our RECORDED_BASE_CHECKSUM.  (In WC-1, we could not do this test
3645      for replaced nodes because we didn't store the checksum of the "revert
3646      base".  In WC-NG, we do and we can.) */
3647   recorded_base_checksum = fb->original_checksum;
3648 
3649   /* If we have a checksum that we want to compare to a MD5 checksum,
3650      ensure that it is a MD5 checksum */
3651   if (recorded_base_checksum
3652       && expected_base_checksum
3653       && recorded_base_checksum->kind != svn_checksum_md5)
3654     SVN_ERR(svn_wc__db_pristine_get_md5(&recorded_base_checksum,
3655                                         eb->db, eb->wcroot_abspath,
3656                                         recorded_base_checksum, pool, pool));
3657 
3658 
3659   if (!svn_checksum_match(expected_base_checksum, recorded_base_checksum))
3660       return svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
3661                      _("Checksum mismatch for '%s':\n"
3662                        "   expected:  %s\n"
3663                        "   recorded:  %s\n"),
3664                      svn_dirent_local_style(fb->local_abspath, pool),
3665                      svn_checksum_to_cstring_display(expected_base_checksum,
3666                                                      pool),
3667                      svn_checksum_to_cstring_display(recorded_base_checksum,
3668                                                      pool));
3669 
3670   /* Open the text base for reading, unless this is an added file. */
3671 
3672   /*
3673      kff todo: what we really need to do here is:
3674 
3675      1. See if there's a file or dir by this name already here.
3676      2. See if it's under revision control.
3677      3. If both are true, open text-base.
3678      4. If only 1 is true, bail, because we can't go destroying user's
3679         files (or as an alternative to bailing, move it to some tmp
3680         name and somehow tell the user, but communicating with the
3681         user without erroring is a whole callback system we haven't
3682         finished inventing yet.)
3683   */
3684 
3685   if (! fb->adding_file)
3686     {
3687       SVN_ERR_ASSERT(!fb->original_checksum
3688                      || fb->original_checksum->kind == svn_checksum_sha1);
3689 
3690       source = svn_stream_lazyopen_create(lazy_open_source, fb, FALSE,
3691                                           handler_pool);
3692     }
3693   else
3694     {
3695       source = svn_stream_empty(handler_pool);
3696     }
3697 
3698   /* If we don't have a recorded checksum, use the ra provided checksum */
3699   if (!recorded_base_checksum)
3700     recorded_base_checksum = expected_base_checksum;
3701 
3702   /* Checksum the text base while applying deltas */
3703   if (recorded_base_checksum)
3704     {
3705       hb->expected_source_checksum = svn_checksum_dup(recorded_base_checksum,
3706                                                       handler_pool);
3707 
3708       /* Wrap stream and store reference to allow calculating the
3709          checksum. */
3710       source = svn_stream_checksummed2(source,
3711                                        &hb->actual_source_checksum,
3712                                        NULL, recorded_base_checksum->kind,
3713                                        TRUE, handler_pool);
3714       hb->source_checksum_stream = source;
3715     }
3716 
3717   target = svn_stream_lazyopen_create(lazy_open_target, hb, TRUE, handler_pool);
3718 
3719   /* Prepare to apply the delta.  */
3720   svn_txdelta_apply(source, target,
3721                     hb->new_text_base_md5_digest,
3722                     fb->local_abspath /* error_info */,
3723                     handler_pool,
3724                     &hb->apply_handler, &hb->apply_baton);
3725 
3726   hb->pool = handler_pool;
3727   hb->fb = fb;
3728 
3729   /* We're all set.  */
3730   *handler_baton = hb;
3731   *handler = window_handler;
3732 
3733   return SVN_NO_ERROR;
3734 }
3735 
3736 
3737 /* An svn_delta_editor_t function. */
3738 static svn_error_t *
change_file_prop(void * file_baton,const char * name,const svn_string_t * value,apr_pool_t * scratch_pool)3739 change_file_prop(void *file_baton,
3740                  const char *name,
3741                  const svn_string_t *value,
3742                  apr_pool_t *scratch_pool)
3743 {
3744   struct file_baton *fb = file_baton;
3745   svn_prop_t *propchange;
3746 
3747   if (fb->skip_this)
3748     return SVN_NO_ERROR;
3749 
3750   /* Push a new propchange to the file baton's array of propchanges */
3751   propchange = apr_array_push(fb->propchanges);
3752   propchange->name = apr_pstrdup(fb->pool, name);
3753   propchange->value = svn_string_dup(value, fb->pool);
3754 
3755   if (!fb->edited && svn_property_kind2(name) == svn_prop_regular_kind)
3756     SVN_ERR(mark_file_edited(fb, scratch_pool));
3757 
3758   if (! fb->shadowed
3759       && strcmp(name, SVN_PROP_SPECIAL) == 0)
3760     {
3761       struct edit_baton *eb = fb->edit_baton;
3762       svn_boolean_t modified = FALSE;
3763       svn_boolean_t becomes_symlink;
3764       svn_boolean_t was_symlink;
3765 
3766       /* Let's see if we have a change as in some scenarios servers report
3767          non-changes of properties. */
3768       becomes_symlink = (value != NULL);
3769 
3770       if (fb->adding_file)
3771         was_symlink = becomes_symlink; /* No change */
3772       else
3773         {
3774           apr_hash_t *props;
3775 
3776           /* We read the server-props, not the ACTUAL props here as we just
3777              want to see if this is really an incoming prop change. */
3778           SVN_ERR(svn_wc__db_base_get_props(&props, eb->db,
3779                                             fb->local_abspath,
3780                                             scratch_pool, scratch_pool));
3781 
3782           was_symlink = ((props
3783                               && svn_hash_gets(props, SVN_PROP_SPECIAL) != NULL)
3784                               ? svn_tristate_true
3785                               : svn_tristate_false);
3786         }
3787 
3788       if (was_symlink != becomes_symlink)
3789         {
3790           /* If the local node was not modified, we continue as usual, if
3791              modified we want a tree conflict just like how we would handle
3792              it when receiving a delete + add (aka "replace") */
3793           if (fb->local_prop_mods)
3794             modified = TRUE;
3795           else
3796             SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db,
3797                                                      fb->local_abspath,
3798                                                      FALSE, scratch_pool));
3799         }
3800 
3801       if (modified)
3802         {
3803           if (!fb->edit_conflict)
3804             fb->edit_conflict = svn_wc__conflict_skel_create(fb->pool);
3805 
3806           SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3807                                      fb->edit_conflict,
3808                                      eb->db, fb->local_abspath,
3809                                      svn_wc_conflict_reason_edited,
3810                                      svn_wc_conflict_action_replace,
3811                                      NULL, NULL,
3812                                      fb->pool, scratch_pool));
3813 
3814           SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton,
3815                                     fb->local_abspath, fb->old_repos_relpath,
3816                                     fb->old_revision, fb->new_repos_relpath,
3817                                     svn_node_file, svn_node_file,
3818                                     NULL, fb->pool, scratch_pool));
3819 
3820           /* Create a copy of the existing (pre update) BASE node in WORKING,
3821              mark a tree conflict and handle the rest of the update as
3822              shadowed */
3823           SVN_ERR(svn_wc__db_op_make_copy(eb->db, fb->local_abspath,
3824                                           fb->edit_conflict, NULL,
3825                                           scratch_pool));
3826 
3827           do_notification(eb, fb->local_abspath, svn_node_file,
3828                           svn_wc_notify_tree_conflict, scratch_pool);
3829 
3830           /* Ok, we introduced a replacement, so we can now handle the rest
3831              as a normal shadowed update */
3832           fb->shadowed = TRUE;
3833           fb->add_existed = FALSE;
3834           fb->already_notified = TRUE;
3835       }
3836     }
3837 
3838   return SVN_NO_ERROR;
3839 }
3840 
3841 /* Perform the actual merge of file changes between an original file,
3842    identified by ORIGINAL_CHECKSUM (an empty file if NULL) to a new file
3843    identified by NEW_CHECKSUM.
3844 
3845    Merge the result into LOCAL_ABSPATH, which is part of the working copy
3846    identified by WRI_ABSPATH. Use OLD_REVISION and TARGET_REVISION for naming
3847    the intermediate files.
3848 
3849    The rest of the arguments are passed to svn_wc__internal_merge().
3850  */
3851 svn_error_t *
svn_wc__perform_file_merge(svn_skel_t ** work_items,svn_skel_t ** conflict_skel,svn_boolean_t * found_conflict,svn_wc__db_t * db,const char * local_abspath,const char * wri_abspath,const svn_checksum_t * new_checksum,const svn_checksum_t * original_checksum,apr_hash_t * old_actual_props,const apr_array_header_t * ext_patterns,svn_revnum_t old_revision,svn_revnum_t target_revision,const apr_array_header_t * propchanges,const char * diff3_cmd,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * result_pool,apr_pool_t * scratch_pool)3852 svn_wc__perform_file_merge(svn_skel_t **work_items,
3853                            svn_skel_t **conflict_skel,
3854                            svn_boolean_t *found_conflict,
3855                            svn_wc__db_t *db,
3856                            const char *local_abspath,
3857                            const char *wri_abspath,
3858                            const svn_checksum_t *new_checksum,
3859                            const svn_checksum_t *original_checksum,
3860                            apr_hash_t *old_actual_props,
3861                            const apr_array_header_t *ext_patterns,
3862                            svn_revnum_t old_revision,
3863                            svn_revnum_t target_revision,
3864                            const apr_array_header_t *propchanges,
3865                            const char *diff3_cmd,
3866                            svn_cancel_func_t cancel_func,
3867                            void *cancel_baton,
3868                            apr_pool_t *result_pool,
3869                            apr_pool_t *scratch_pool)
3870 {
3871   /* Actual file exists and has local mods:
3872      Now we need to let loose svn_wc__internal_merge() to merge
3873      the textual changes into the working file. */
3874   const char *oldrev_str, *newrev_str, *mine_str;
3875   const char *merge_left;
3876   svn_boolean_t delete_left = FALSE;
3877   const char *path_ext = "";
3878   const char *new_pristine_abspath;
3879   enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged;
3880   svn_skel_t *work_item;
3881 
3882   *work_items = NULL;
3883 
3884   SVN_ERR(svn_wc__db_pristine_get_path(&new_pristine_abspath,
3885                                        db, wri_abspath, new_checksum,
3886                                        scratch_pool, scratch_pool));
3887 
3888   /* If we have any file extensions we're supposed to
3889      preserve in generated conflict file names, then find
3890      this path's extension.  But then, if it isn't one of
3891      the ones we want to keep in conflict filenames,
3892      pretend it doesn't have an extension at all. */
3893   if (ext_patterns && ext_patterns->nelts)
3894     {
3895       svn_path_splitext(NULL, &path_ext, local_abspath, scratch_pool);
3896       if (! (*path_ext && svn_cstring_match_glob_list(path_ext, ext_patterns)))
3897         path_ext = "";
3898     }
3899 
3900   /* old_revision can be invalid when the conflict is against a
3901      local addition */
3902   if (!SVN_IS_VALID_REVNUM(old_revision))
3903     old_revision = 0;
3904 
3905   oldrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s",
3906                             old_revision,
3907                             *path_ext ? "." : "",
3908                             *path_ext ? path_ext : "");
3909 
3910   newrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s",
3911                             target_revision,
3912                             *path_ext ? "." : "",
3913                             *path_ext ? path_ext : "");
3914   mine_str = apr_psprintf(scratch_pool, ".mine%s%s",
3915                           *path_ext ? "." : "",
3916                           *path_ext ? path_ext : "");
3917 
3918   if (! original_checksum)
3919     {
3920       SVN_ERR(get_empty_tmp_file(&merge_left, db, wri_abspath,
3921                                  result_pool, scratch_pool));
3922       delete_left = TRUE;
3923     }
3924   else
3925     SVN_ERR(svn_wc__db_pristine_get_path(&merge_left, db, wri_abspath,
3926                                          original_checksum,
3927                                          result_pool, scratch_pool));
3928 
3929   /* Merge the changes from the old textbase to the new
3930      textbase into the file we're updating.
3931      Remember that this function wants full paths! */
3932   SVN_ERR(svn_wc__internal_merge(&work_item,
3933                                  conflict_skel,
3934                                  &merge_outcome,
3935                                  db,
3936                                  merge_left,
3937                                  new_pristine_abspath,
3938                                  local_abspath,
3939                                  wri_abspath,
3940                                  oldrev_str, newrev_str, mine_str,
3941                                  old_actual_props,
3942                                  FALSE /* dry_run */,
3943                                  diff3_cmd, NULL, propchanges,
3944                                  cancel_func, cancel_baton,
3945                                  result_pool, scratch_pool));
3946 
3947   *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
3948   *found_conflict = (merge_outcome == svn_wc_merge_conflict);
3949 
3950   /* If we created a temporary left merge file, get rid of it. */
3951   if (delete_left)
3952     {
3953       SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, wri_abspath,
3954                                            merge_left,
3955                                            result_pool, scratch_pool));
3956       *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
3957     }
3958 
3959   return SVN_NO_ERROR;
3960 }
3961 
3962 /* This is the small planet.  It has the complex responsibility of
3963  * "integrating" a new revision of a file into a working copy.
3964  *
3965  * Given a file_baton FB for a file either already under version control, or
3966  * prepared (see below) to join version control, fully install a
3967  * new revision of the file.
3968  *
3969  * ### transitional: installation of the working file will be handled
3970  * ### by the *INSTALL_PRISTINE flag.
3971  *
3972  * By "install", we mean: create a new text-base and prop-base, merge
3973  * any textual and property changes into the working file, and finally
3974  * update all metadata so that the working copy believes it has a new
3975  * working revision of the file.  All of this work includes being
3976  * sensitive to eol translation, keyword substitution, and performing
3977  * all actions accumulated the parent directory's work queue.
3978  *
3979  * Set *CONTENT_STATE to the state of the contents after the
3980  * installation.
3981  *
3982  * Return values are allocated in RESULT_POOL and temporary allocations
3983  * are performed in SCRATCH_POOL.
3984  */
3985 static svn_error_t *
merge_file(svn_skel_t ** work_items,svn_skel_t ** conflict_skel,svn_boolean_t * install_pristine,const char ** install_from,svn_wc_notify_state_t * content_state,struct file_baton * fb,apr_hash_t * actual_props,apr_time_t last_changed_date,apr_pool_t * result_pool,apr_pool_t * scratch_pool)3986 merge_file(svn_skel_t **work_items,
3987            svn_skel_t **conflict_skel,
3988            svn_boolean_t *install_pristine,
3989            const char **install_from,
3990            svn_wc_notify_state_t *content_state,
3991            struct file_baton *fb,
3992            apr_hash_t *actual_props,
3993            apr_time_t last_changed_date,
3994            apr_pool_t *result_pool,
3995            apr_pool_t *scratch_pool)
3996 {
3997   struct edit_baton *eb = fb->edit_baton;
3998   struct dir_baton *pb = fb->dir_baton;
3999   svn_boolean_t is_locally_modified;
4000   svn_boolean_t found_text_conflict = FALSE;
4001 
4002   SVN_ERR_ASSERT(! fb->shadowed
4003                  && ! fb->obstruction_found
4004                  && ! fb->edit_obstructed);
4005 
4006   /*
4007      When this function is called on file F, we assume the following
4008      things are true:
4009 
4010          - The new pristine text of F is present in the pristine store
4011            iff FB->NEW_TEXT_BASE_SHA1_CHECKSUM is not NULL.
4012 
4013          - The WC metadata still reflects the old version of F.
4014            (We can still access the old pristine base text of F.)
4015 
4016      The goal is to update the local working copy of F to reflect
4017      the changes received from the repository, preserving any local
4018      modifications.
4019   */
4020 
4021   *work_items = NULL;
4022   *install_pristine = FALSE;
4023   *install_from = NULL;
4024 
4025   /* Start by splitting the file path, getting an access baton for the parent,
4026      and an entry for the file if any. */
4027 
4028   /* Has the user made local mods to the working file?
4029      Note that this compares to the current pristine file, which is
4030      different from fb->old_text_base_path if we have a replaced-with-history
4031      file.  However, in the case we had an obstruction, we check against the
4032      new text base.
4033    */
4034   if (fb->adding_file && !fb->add_existed)
4035     {
4036       is_locally_modified = FALSE; /* There is no file: Don't check */
4037     }
4038   else
4039     {
4040       /* The working file is not an obstruction.
4041          So: is the file modified, relative to its ORIGINAL pristine?
4042 
4043          This function sets is_locally_modified to FALSE for
4044          files that do not exist and for directories. */
4045 
4046       SVN_ERR(svn_wc__internal_file_modified_p(&is_locally_modified,
4047                                                eb->db, fb->local_abspath,
4048                                                FALSE /* exact_comparison */,
4049                                                scratch_pool));
4050     }
4051 
4052   /* For 'textual' merging, we use the following system:
4053 
4054      When a file is modified and we have a new BASE:
4055       - For text files
4056           * svn_wc_merge uses diff3
4057           * possibly makes backups and marks files as conflicted.
4058 
4059       - For binary files
4060           * svn_wc_merge makes backups and marks files as conflicted.
4061 
4062      If a file is not modified and we have a new BASE:
4063        * Install from pristine.
4064 
4065      If we have property changes related to magic properties or if the
4066      svn:keywords property is set:
4067        * Retranslate from the working file.
4068    */
4069   if (! is_locally_modified
4070       && fb->new_text_base_sha1_checksum)
4071     {
4072           /* If there are no local mods, who cares whether it's a text
4073              or binary file!  Just write a log command to overwrite
4074              any working file with the new text-base.  If newline
4075              conversion or keyword substitution is activated, this
4076              will happen as well during the copy.
4077              For replaced files, though, we want to merge in the changes
4078              even if the file is not modified compared to the (non-revert)
4079              text-base. */
4080 
4081       *install_pristine = TRUE;
4082     }
4083   else if (fb->new_text_base_sha1_checksum)
4084     {
4085       /* Actual file exists and has local mods:
4086          Now we need to let loose svn_wc__merge_internal() to merge
4087          the textual changes into the working file. */
4088       SVN_ERR(svn_wc__perform_file_merge(work_items,
4089                                          conflict_skel,
4090                                          &found_text_conflict,
4091                                          eb->db,
4092                                          fb->local_abspath,
4093                                          pb->local_abspath,
4094                                          fb->new_text_base_sha1_checksum,
4095                                          fb->add_existed
4096                                                   ? NULL
4097                                                   : fb->original_checksum,
4098                                          actual_props,
4099                                          eb->ext_patterns,
4100                                          fb->old_revision,
4101                                          *eb->target_revision,
4102                                          fb->propchanges,
4103                                          eb->diff3_cmd,
4104                                          eb->cancel_func, eb->cancel_baton,
4105                                          result_pool, scratch_pool));
4106     } /* end: working file exists and has mods */
4107   else
4108     {
4109       /* There is no new text base, but let's see if the working file needs
4110          to be updated for any other reason. */
4111 
4112       apr_hash_t *keywords;
4113 
4114       /* Determine if any of the propchanges are the "magic" ones that
4115          might require changing the working file. */
4116       svn_boolean_t magic_props_changed;
4117 
4118       magic_props_changed = svn_wc__has_magic_property(fb->propchanges);
4119 
4120       SVN_ERR(svn_wc__get_translate_info(NULL, NULL,
4121                                          &keywords,
4122                                          NULL,
4123                                          eb->db, fb->local_abspath,
4124                                          actual_props, TRUE,
4125                                          scratch_pool, scratch_pool));
4126       if (magic_props_changed || keywords)
4127         {
4128           /* Special edge-case: it's possible that this file installation
4129              only involves propchanges, but that some of those props still
4130              require a retranslation of the working file.
4131 
4132              OR that the file doesn't involve propchanges which by themselves
4133              require retranslation, but receiving a change bumps the revision
4134              number which requires re-expansion of keywords... */
4135 
4136           if (is_locally_modified)
4137             {
4138               const char *tmptext;
4139 
4140               /* Copy and DEtranslate the working file to a temp text-base.
4141                  Note that detranslation is done according to the old props. */
4142               SVN_ERR(svn_wc__internal_translated_file(
4143                         &tmptext, fb->local_abspath, eb->db, fb->local_abspath,
4144                         SVN_WC_TRANSLATE_TO_NF
4145                           | SVN_WC_TRANSLATE_NO_OUTPUT_CLEANUP,
4146                         eb->cancel_func, eb->cancel_baton,
4147                         result_pool, scratch_pool));
4148 
4149               /* We always want to reinstall the working file if the magic
4150                  properties have changed, or there are any keywords present.
4151                  Note that TMPTEXT might actually refer to the working file
4152                  itself (the above function skips a detranslate when not
4153                  required). This is acceptable, as we will (re)translate
4154                  according to the new properties into a temporary file (from
4155                  the working file), and then rename the temp into place. Magic!
4156                */
4157               *install_pristine = TRUE;
4158               *install_from = tmptext;
4159             }
4160           else
4161             {
4162               /* Use our existing 'copy' from the pristine store instead
4163                  of making a new copy. This way we can use the standard code
4164                  to update the recorded size and modification time.
4165                  (Issue #3842) */
4166               *install_pristine = TRUE;
4167             }
4168         }
4169     }
4170 
4171   /* Set the returned content state. */
4172 
4173   if (found_text_conflict)
4174     *content_state = svn_wc_notify_state_conflicted;
4175   else if (fb->new_text_base_sha1_checksum)
4176     {
4177       if (is_locally_modified)
4178         *content_state = svn_wc_notify_state_merged;
4179       else
4180         *content_state = svn_wc_notify_state_changed;
4181     }
4182   else
4183     *content_state = svn_wc_notify_state_unchanged;
4184 
4185   return SVN_NO_ERROR;
4186 }
4187 
4188 
4189 /* An svn_delta_editor_t function. */
4190 /* Mostly a wrapper around merge_file. */
4191 static svn_error_t *
close_file(void * file_baton,const char * expected_md5_digest,apr_pool_t * pool)4192 close_file(void *file_baton,
4193            const char *expected_md5_digest,
4194            apr_pool_t *pool)
4195 {
4196   struct file_baton *fb = file_baton;
4197   struct dir_baton *pdb = fb->dir_baton;
4198   struct edit_baton *eb = fb->edit_baton;
4199   svn_wc_notify_state_t content_state, prop_state;
4200   svn_wc_notify_lock_state_t lock_state;
4201   svn_checksum_t *expected_md5_checksum = NULL;
4202   apr_hash_t *new_base_props = NULL;
4203   apr_hash_t *new_actual_props = NULL;
4204   apr_array_header_t *entry_prop_changes;
4205   apr_array_header_t *dav_prop_changes;
4206   apr_array_header_t *regular_prop_changes;
4207   apr_hash_t *current_base_props = NULL;
4208   apr_hash_t *current_actual_props = NULL;
4209   apr_hash_t *local_actual_props = NULL;
4210   svn_skel_t *all_work_items = NULL;
4211   svn_skel_t *conflict_skel = NULL;
4212   svn_skel_t *work_item;
4213   apr_pool_t *scratch_pool = fb->pool; /* Destroyed at function exit */
4214   svn_boolean_t keep_recorded_info = FALSE;
4215   const svn_checksum_t *new_checksum;
4216   apr_array_header_t *iprops = NULL;
4217 
4218   if (fb->skip_this)
4219     {
4220       svn_pool_destroy(fb->pool);
4221       SVN_ERR(maybe_release_dir_info(pdb));
4222       return SVN_NO_ERROR;
4223     }
4224 
4225   if (fb->edited)
4226     conflict_skel = fb->edit_conflict;
4227 
4228   if (expected_md5_digest)
4229     SVN_ERR(svn_checksum_parse_hex(&expected_md5_checksum, svn_checksum_md5,
4230                                    expected_md5_digest, scratch_pool));
4231 
4232   if (fb->new_text_base_md5_checksum && expected_md5_checksum
4233       && !svn_checksum_match(expected_md5_checksum,
4234                              fb->new_text_base_md5_checksum))
4235     return svn_error_trace(
4236                 svn_checksum_mismatch_err(expected_md5_checksum,
4237                                           fb->new_text_base_md5_checksum,
4238                                           scratch_pool,
4239                                           _("Checksum mismatch for '%s'"),
4240                                           svn_dirent_local_style(
4241                                                 fb->local_abspath, pool)));
4242 
4243   /* Gather the changes for each kind of property.  */
4244   SVN_ERR(svn_categorize_props(fb->propchanges, &entry_prop_changes,
4245                                &dav_prop_changes, &regular_prop_changes,
4246                                scratch_pool));
4247 
4248   /* Extract the changed_* and lock state information.  */
4249   {
4250     svn_revnum_t new_changed_rev;
4251     apr_time_t new_changed_date;
4252     const char *new_changed_author;
4253 
4254     SVN_ERR(accumulate_last_change(&new_changed_rev,
4255                                    &new_changed_date,
4256                                    &new_changed_author,
4257                                    entry_prop_changes,
4258                                    scratch_pool, scratch_pool));
4259 
4260     if (SVN_IS_VALID_REVNUM(new_changed_rev))
4261       fb->changed_rev = new_changed_rev;
4262     if (new_changed_date != 0)
4263       fb->changed_date = new_changed_date;
4264     if (new_changed_author != NULL)
4265       fb->changed_author = new_changed_author;
4266   }
4267 
4268   /* Determine whether the file has become unlocked.  */
4269   {
4270     int i;
4271 
4272     lock_state = svn_wc_notify_lock_state_unchanged;
4273 
4274     for (i = 0; i < entry_prop_changes->nelts; ++i)
4275       {
4276         const svn_prop_t *prop
4277           = &APR_ARRAY_IDX(entry_prop_changes, i, svn_prop_t);
4278 
4279         /* If we see a change to the LOCK_TOKEN entry prop, then the only
4280            possible change is its REMOVAL. Thus, the lock has been removed,
4281            and we should likewise remove our cached copy of it.  */
4282         if (! strcmp(prop->name, SVN_PROP_ENTRY_LOCK_TOKEN))
4283           {
4284             /* If we lose the lock, but not because we are switching to
4285                another url, remove the state lock from the wc */
4286             if (! eb->switch_repos_relpath
4287                 || strcmp(fb->new_repos_relpath, fb->old_repos_relpath) == 0)
4288               {
4289                 SVN_ERR_ASSERT(prop->value == NULL);
4290                 SVN_ERR(svn_wc__db_lock_remove(eb->db, fb->local_abspath, NULL,
4291                                                scratch_pool));
4292 
4293                 lock_state = svn_wc_notify_lock_state_unlocked;
4294               }
4295             break;
4296           }
4297       }
4298   }
4299 
4300   /* Install all kinds of properties.  It is important to do this before
4301      any file content merging, since that process might expand keywords, in
4302      which case we want the new entryprops to be in place. */
4303 
4304   /* Write log commands to merge REGULAR_PROPS into the existing
4305      properties of FB->LOCAL_ABSPATH.  Update *PROP_STATE to reflect
4306      the result of the regular prop merge.
4307 
4308      BASE_PROPS and WORKING_PROPS are hashes of the base and
4309      working props of the file; if NULL they are read from the wc.  */
4310 
4311   /* ### some of this feels like voodoo... */
4312 
4313   if ((!fb->adding_file || fb->add_existed)
4314       && !fb->shadowed)
4315     SVN_ERR(svn_wc__get_actual_props(&local_actual_props,
4316                                      eb->db, fb->local_abspath,
4317                                      scratch_pool, scratch_pool));
4318   if (local_actual_props == NULL)
4319     local_actual_props = apr_hash_make(scratch_pool);
4320 
4321   if (fb->add_existed)
4322     {
4323       /* This node already exists. Grab the current pristine properties. */
4324       SVN_ERR(svn_wc__db_read_pristine_props(&current_base_props,
4325                                              eb->db, fb->local_abspath,
4326                                              scratch_pool, scratch_pool));
4327       current_actual_props = local_actual_props;
4328     }
4329   else if (!fb->adding_file)
4330     {
4331       /* Get the BASE properties for proper merging. */
4332       SVN_ERR(svn_wc__db_base_get_props(&current_base_props,
4333                                         eb->db, fb->local_abspath,
4334                                         scratch_pool, scratch_pool));
4335       current_actual_props = local_actual_props;
4336     }
4337 
4338   /* Note: even if the node existed before, it may not have
4339      pristine props (e.g a local-add)  */
4340   if (current_base_props == NULL)
4341     current_base_props = apr_hash_make(scratch_pool);
4342 
4343   /* And new nodes need an empty set of ACTUAL props.  */
4344   if (current_actual_props == NULL)
4345     current_actual_props = apr_hash_make(scratch_pool);
4346 
4347   prop_state = svn_wc_notify_state_unknown;
4348 
4349   if (! fb->shadowed)
4350     {
4351       svn_boolean_t install_pristine;
4352       const char *install_from = NULL;
4353 
4354       /* Merge the 'regular' props into the existing working proplist. */
4355       /* This will merge the old and new props into a new prop db, and
4356          write <cp> commands to the logfile to install the merged
4357          props.  */
4358       new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
4359                                        scratch_pool);
4360       SVN_ERR(svn_wc__merge_props(&conflict_skel,
4361                                   &prop_state,
4362                                   &new_actual_props,
4363                                   eb->db,
4364                                   fb->local_abspath,
4365                                   NULL /* server_baseprops (update, not merge)  */,
4366                                   current_base_props,
4367                                   current_actual_props,
4368                                   regular_prop_changes, /* propchanges */
4369                                   scratch_pool,
4370                                   scratch_pool));
4371       /* We will ALWAYS have properties to save (after a not-dry-run merge). */
4372       SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
4373 
4374       /* Merge the text. This will queue some additional work.  */
4375       if (!fb->obstruction_found && !fb->edit_obstructed)
4376         {
4377           svn_error_t *err;
4378           err = merge_file(&work_item, &conflict_skel,
4379                            &install_pristine, &install_from,
4380                            &content_state, fb, current_actual_props,
4381                            fb->changed_date, scratch_pool, scratch_pool);
4382 
4383           if (err && err->apr_err == SVN_ERR_WC_PATH_ACCESS_DENIED)
4384             {
4385               if (eb->notify_func)
4386                 {
4387                   svn_wc_notify_t *notify =svn_wc_create_notify(
4388                                 fb->local_abspath,
4389                                 svn_wc_notify_update_skip_access_denied,
4390                                 scratch_pool);
4391 
4392                   notify->kind = svn_node_file;
4393                   notify->err = err;
4394 
4395                   eb->notify_func(eb->notify_baton, notify, scratch_pool);
4396                 }
4397               svn_error_clear(err);
4398 
4399               SVN_ERR(remember_skipped_tree(eb, fb->local_abspath,
4400                                             scratch_pool));
4401               fb->skip_this = TRUE;
4402 
4403               svn_pool_destroy(fb->pool);
4404               SVN_ERR(maybe_release_dir_info(pdb));
4405               return SVN_NO_ERROR;
4406             }
4407           else
4408             SVN_ERR(err);
4409 
4410           all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4411                                             scratch_pool);
4412         }
4413       else
4414         {
4415           install_pristine = FALSE;
4416           if (fb->new_text_base_sha1_checksum)
4417             content_state = svn_wc_notify_state_changed;
4418           else
4419             content_state = svn_wc_notify_state_unchanged;
4420         }
4421 
4422       if (install_pristine)
4423         {
4424           svn_boolean_t record_fileinfo;
4425 
4426           /* If we are installing from the pristine contents, then go ahead and
4427              record the fileinfo. That will be the "proper" values. Installing
4428              from some random file means the fileinfo does NOT correspond to
4429              the pristine (in which case, the fileinfo will be cleared for
4430              safety's sake).  */
4431           record_fileinfo = (install_from == NULL);
4432 
4433           SVN_ERR(svn_wc__wq_build_file_install(&work_item,
4434                                                 eb->db,
4435                                                 fb->local_abspath,
4436                                                 install_from,
4437                                                 eb->use_commit_times,
4438                                                 record_fileinfo,
4439                                                 scratch_pool, scratch_pool));
4440           all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4441                                             scratch_pool);
4442         }
4443       else if (lock_state == svn_wc_notify_lock_state_unlocked
4444                && !fb->obstruction_found)
4445         {
4446           /* If a lock was removed and we didn't update the text contents, we
4447              might need to set the file read-only.
4448 
4449              Note: this will also update the executable flag, but ... meh.  */
4450           SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, eb->db,
4451                                                    fb->local_abspath,
4452                                                    scratch_pool, scratch_pool));
4453           all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4454                                             scratch_pool);
4455         }
4456 
4457       if (! install_pristine
4458           && (content_state == svn_wc_notify_state_unchanged))
4459         {
4460           /* It is safe to keep the current recorded timestamp and size */
4461           keep_recorded_info = TRUE;
4462         }
4463 
4464       /* Clean up any temporary files.  */
4465 
4466       /* Remove the INSTALL_FROM file, as long as it doesn't refer to the
4467          working file.  */
4468       if (install_from != NULL
4469           && strcmp(install_from, fb->local_abspath) != 0)
4470         {
4471           SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db,
4472                                                fb->local_abspath, install_from,
4473                                                scratch_pool, scratch_pool));
4474           all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4475                                             scratch_pool);
4476         }
4477     }
4478   else
4479     {
4480       /* Adding or updating a BASE node under a locally added node. */
4481       apr_hash_t *fake_actual_props;
4482 
4483       if (fb->adding_file)
4484         fake_actual_props = apr_hash_make(scratch_pool);
4485       else
4486         fake_actual_props = current_base_props;
4487 
4488       /* Store the incoming props (sent as propchanges) in new_base_props
4489          and create a set of new actual props to use for notifications */
4490       new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
4491                                        scratch_pool);
4492       SVN_ERR(svn_wc__merge_props(&conflict_skel,
4493                                   &prop_state,
4494                                   &new_actual_props,
4495                                   eb->db,
4496                                   fb->local_abspath,
4497                                   NULL /* server_baseprops (not merging) */,
4498                                   current_base_props /* pristine_props */,
4499                                   fake_actual_props /* actual_props */,
4500                                   regular_prop_changes, /* propchanges */
4501                                   scratch_pool,
4502                                   scratch_pool));
4503 
4504       if (fb->new_text_base_sha1_checksum)
4505         content_state = svn_wc_notify_state_changed;
4506       else
4507         content_state = svn_wc_notify_state_unchanged;
4508     }
4509 
4510   /* Insert/replace the BASE node with all of the new metadata.  */
4511 
4512   /* Set the 'checksum' column of the file's BASE_NODE row to
4513    * NEW_TEXT_BASE_SHA1_CHECKSUM.  The pristine text identified by that
4514    * checksum is already in the pristine store. */
4515   new_checksum = fb->new_text_base_sha1_checksum;
4516 
4517   /* If we don't have a NEW checksum, then the base must not have changed.
4518      Just carry over the old checksum.  */
4519   if (new_checksum == NULL)
4520     new_checksum = fb->original_checksum;
4521 
4522   if (conflict_skel)
4523     {
4524       SVN_ERR(complete_conflict(conflict_skel,
4525                                 fb->edit_baton,
4526                                 fb->local_abspath,
4527                                 fb->old_repos_relpath,
4528                                 fb->old_revision,
4529                                 fb->new_repos_relpath,
4530                                 svn_node_file, svn_node_file,
4531                                 fb->dir_baton->deletion_conflicts
4532                                   ? svn_hash_gets(
4533                                         fb->dir_baton->deletion_conflicts,
4534                                         fb->name)
4535                                   : NULL,
4536                                 fb->pool, scratch_pool));
4537 
4538       SVN_ERR(svn_wc__conflict_create_markers(&work_item,
4539                                               eb->db, fb->local_abspath,
4540                                               conflict_skel,
4541                                               scratch_pool, scratch_pool));
4542 
4543       all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4544                                         scratch_pool);
4545     }
4546 
4547   /* Any inherited props to be set set for this base node? */
4548   if (eb->wcroot_iprops)
4549     {
4550       iprops = svn_hash_gets(eb->wcroot_iprops, fb->local_abspath);
4551 
4552       /* close_edit may also update iprops for switched nodes, catching
4553          those for which close_directory is never called (e.g. a switch
4554          with no changes).  So as a minor optimization we remove any
4555          iprops from the hash so as not to set them again in
4556          close_edit. */
4557       if (iprops)
4558         svn_hash_sets(eb->wcroot_iprops, fb->local_abspath, NULL);
4559     }
4560 
4561   SVN_ERR(svn_wc__db_base_add_file(eb->db, fb->local_abspath,
4562                                    eb->wcroot_abspath,
4563                                    fb->new_repos_relpath,
4564                                    eb->repos_root, eb->repos_uuid,
4565                                    *eb->target_revision,
4566                                    new_base_props,
4567                                    fb->changed_rev,
4568                                    fb->changed_date,
4569                                    fb->changed_author,
4570                                    new_checksum,
4571                                    (dav_prop_changes->nelts > 0)
4572                                      ? svn_prop_array_to_hash(
4573                                                       dav_prop_changes,
4574                                                       scratch_pool)
4575                                      : NULL,
4576                                    (fb->add_existed && fb->adding_file),
4577                                    (! fb->shadowed) && new_base_props,
4578                                    new_actual_props,
4579                                    iprops,
4580                                    keep_recorded_info,
4581                                    (fb->shadowed && fb->obstruction_found),
4582                                    conflict_skel,
4583                                    all_work_items,
4584                                    scratch_pool));
4585 
4586   if (conflict_skel && eb->conflict_func)
4587     SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath,
4588                                              svn_node_file,
4589                                              conflict_skel,
4590                                              NULL /* merge_options */,
4591                                              eb->conflict_func,
4592                                              eb->conflict_baton,
4593                                              eb->cancel_func,
4594                                              eb->cancel_baton,
4595                                              scratch_pool));
4596 
4597   /* Deal with the WORKING tree, based on updates to the BASE tree.  */
4598 
4599   svn_hash_sets(fb->dir_baton->not_present_nodes, fb->name, NULL);
4600 
4601   /* Send a notification to the callback function.  (Skip notifications
4602      about files which were already notified for another reason.) */
4603   if (eb->notify_func && !fb->already_notified
4604       && (fb->edited || lock_state == svn_wc_notify_lock_state_unlocked))
4605     {
4606       svn_wc_notify_t *notify;
4607       svn_wc_notify_action_t action = svn_wc_notify_update_update;
4608 
4609       if (fb->edited)
4610         {
4611           if (fb->shadowed || fb->edit_obstructed)
4612             action = fb->adding_file
4613                             ? svn_wc_notify_update_shadowed_add
4614                             : svn_wc_notify_update_shadowed_update;
4615           else if (fb->obstruction_found || fb->add_existed)
4616             {
4617               if (content_state != svn_wc_notify_state_conflicted)
4618                 action = svn_wc_notify_exists;
4619             }
4620           else if (fb->adding_file)
4621             {
4622               action = svn_wc_notify_update_add;
4623             }
4624         }
4625       else
4626         {
4627           SVN_ERR_ASSERT(lock_state == svn_wc_notify_lock_state_unlocked);
4628           action = svn_wc_notify_update_broken_lock;
4629         }
4630 
4631       /* If the file was moved-away, notify for the moved-away node.
4632        * The original location only had its BASE info changed and
4633        * we don't usually notify about such changes. */
4634       notify = svn_wc_create_notify(fb->local_abspath, action, scratch_pool);
4635       notify->kind = svn_node_file;
4636       notify->content_state = content_state;
4637       notify->prop_state = prop_state;
4638       notify->lock_state = lock_state;
4639       notify->revision = *eb->target_revision;
4640       notify->old_revision = fb->old_revision;
4641 
4642       /* Fetch the mimetype from the actual properties */
4643       notify->mime_type = svn_prop_get_value(new_actual_props,
4644                                              SVN_PROP_MIME_TYPE);
4645 
4646       eb->notify_func(eb->notify_baton, notify, scratch_pool);
4647     }
4648 
4649   svn_pool_destroy(fb->pool); /* Destroy scratch_pool */
4650 
4651   /* We have one less referrer to the directory */
4652   SVN_ERR(maybe_release_dir_info(pdb));
4653 
4654   return SVN_NO_ERROR;
4655 }
4656 
4657 
4658 /* Implements svn_wc__proplist_receiver_t.
4659  * Check for the presence of an svn:keywords property and queues an install_file
4660  * work queue item if present. Thus, when the work queue is run to complete the
4661  * switch operation, all files with keywords will go through the translation
4662  * process so URLs etc are updated. */
4663 static svn_error_t *
update_keywords_after_switch_cb(void * baton,const char * local_abspath,apr_hash_t * props,apr_pool_t * scratch_pool)4664 update_keywords_after_switch_cb(void *baton,
4665                                 const char *local_abspath,
4666                                 apr_hash_t *props,
4667                                 apr_pool_t *scratch_pool)
4668 {
4669   struct edit_baton *eb = baton;
4670   svn_string_t *propval;
4671   svn_boolean_t modified;
4672   svn_boolean_t record_fileinfo;
4673   svn_skel_t *work_items;
4674   const char *install_from;
4675 
4676   propval = svn_hash_gets(props, SVN_PROP_KEYWORDS);
4677   if (!propval)
4678     return SVN_NO_ERROR;
4679 
4680   SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db,
4681                                            local_abspath, FALSE,
4682                                            scratch_pool));
4683   if (modified)
4684     {
4685       const char *temp_dir_abspath;
4686       svn_stream_t *working_stream;
4687       svn_stream_t *install_from_stream;
4688 
4689       SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, eb->db,
4690                                              local_abspath, scratch_pool,
4691                                              scratch_pool));
4692       SVN_ERR(svn_stream_open_readonly(&working_stream, local_abspath,
4693                                        scratch_pool, scratch_pool));
4694       SVN_ERR(svn_stream_open_unique(&install_from_stream, &install_from,
4695                                      temp_dir_abspath, svn_io_file_del_none,
4696                                      scratch_pool, scratch_pool));
4697       SVN_ERR(svn_stream_copy3(working_stream, install_from_stream,
4698                                eb->cancel_func, eb->cancel_baton,
4699                                scratch_pool));
4700       record_fileinfo = FALSE;
4701     }
4702   else
4703     {
4704       install_from = NULL;
4705       record_fileinfo = TRUE;
4706     }
4707 
4708   SVN_ERR(svn_wc__wq_build_file_install(&work_items, eb->db, local_abspath,
4709                                         install_from,
4710                                         eb->use_commit_times,
4711                                         record_fileinfo,
4712                                         scratch_pool, scratch_pool));
4713   if (install_from)
4714     {
4715       svn_skel_t *work_item;
4716 
4717       SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db,
4718                                            local_abspath, install_from,
4719                                            scratch_pool, scratch_pool));
4720       work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
4721     }
4722 
4723   SVN_ERR(svn_wc__db_wq_add(eb->db, local_abspath, work_items,
4724                             scratch_pool));
4725 
4726   return SVN_NO_ERROR;
4727 }
4728 
4729 
4730 /* An svn_delta_editor_t function. */
4731 static svn_error_t *
close_edit(void * edit_baton,apr_pool_t * pool)4732 close_edit(void *edit_baton,
4733            apr_pool_t *pool)
4734 {
4735   struct edit_baton *eb = edit_baton;
4736   apr_pool_t *scratch_pool = eb->pool;
4737 
4738   /* The editor didn't even open the root; we have to take care of
4739      some cleanup stuffs. */
4740   if (! eb->root_opened
4741       && *eb->target_basename == '\0')
4742     {
4743       /* We need to "un-incomplete" the root directory. */
4744       SVN_ERR(svn_wc__db_temp_op_end_directory_update(eb->db,
4745                                                       eb->anchor_abspath,
4746                                                       scratch_pool));
4747     }
4748 
4749   /* By definition, anybody "driving" this editor for update or switch
4750      purposes at a *minimum* must have called set_target_revision() at
4751      the outset, and close_edit() at the end -- even if it turned out
4752      that no changes ever had to be made, and open_root() was never
4753      called.  That's fine.  But regardless, when the edit is over,
4754      this editor needs to make sure that *all* paths have had their
4755      revisions bumped to the new target revision. */
4756 
4757   /* Make sure our update target now has the new working revision.
4758      Also, if this was an 'svn switch', then rewrite the target's
4759      url.  All of this tweaking might happen recursively!  Note
4760      that if eb->target is NULL, that's okay (albeit "sneaky",
4761      some might say).  */
4762 
4763   /* Extra check: if the update did nothing but make its target
4764      'deleted', then do *not* run cleanup on the target, as it
4765      will only remove the deleted entry!  */
4766   if (! eb->target_deleted)
4767     {
4768       SVN_ERR(svn_wc__db_op_bump_revisions_post_update(eb->db,
4769                                                        eb->target_abspath,
4770                                                        eb->requested_depth,
4771                                                        eb->switch_repos_relpath,
4772                                                        eb->repos_root,
4773                                                        eb->repos_uuid,
4774                                                        *(eb->target_revision),
4775                                                        eb->skipped_trees,
4776                                                        eb->wcroot_iprops,
4777                                                        ! eb->edited,
4778                                                        eb->notify_func,
4779                                                        eb->notify_baton,
4780                                                        eb->pool));
4781 
4782       if (*eb->target_basename != '\0')
4783         {
4784           svn_wc__db_status_t status;
4785           svn_error_t *err;
4786 
4787           /* Note: we are fetching information about the *target*, not anchor.
4788              There is no guarantee that the target has a BASE node.
4789              For example:
4790 
4791                The node was not present in BASE, but locally-added, and the
4792                update did not create a new BASE node "under" the local-add.
4793 
4794              If there is no BASE node for the target, then we certainly don't
4795              have to worry about removing it. */
4796           err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, NULL,
4797                                          NULL, NULL, NULL, NULL, NULL, NULL,
4798                                          NULL, NULL, NULL, NULL,
4799                                          eb->db, eb->target_abspath,
4800                                          scratch_pool, scratch_pool);
4801           if (err)
4802             {
4803               if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
4804                 return svn_error_trace(err);
4805 
4806               svn_error_clear(err);
4807             }
4808           else if (status == svn_wc__db_status_excluded)
4809             {
4810               /* There is a small chance that the explicit target of an update/
4811                  switch is gone in the repository, in that specific case the
4812                  node hasn't been re-added to the BASE tree by this update.
4813 
4814                  If so, we should get rid of this excluded node now. */
4815 
4816               SVN_ERR(svn_wc__db_base_remove(eb->db, eb->target_abspath,
4817                                              TRUE, FALSE, FALSE,
4818                                              SVN_INVALID_REVNUM,
4819                                              NULL, NULL, scratch_pool));
4820             }
4821         }
4822     }
4823 
4824   /* Update keywords in switched files.
4825      GOTO #1975 (the year of the Altair 8800). */
4826   if (eb->switch_repos_relpath)
4827     {
4828       svn_depth_t depth;
4829 
4830       if (eb->requested_depth > svn_depth_empty)
4831         depth = eb->requested_depth;
4832       else
4833         depth = svn_depth_infinity;
4834 
4835       SVN_ERR(svn_wc__db_read_props_streamily(eb->db,
4836                                               eb->target_abspath,
4837                                               depth,
4838                                               FALSE, /* pristine */
4839                                               NULL, /* changelists */
4840                                               update_keywords_after_switch_cb,
4841                                               eb,
4842                                               eb->cancel_func,
4843                                               eb->cancel_baton,
4844                                               scratch_pool));
4845     }
4846 
4847   /* The edit is over: run the wq with proper cancel support,
4848      but first kill the handler that would run it on the pool
4849      cleanup at the end of this function. */
4850   apr_pool_cleanup_kill(eb->pool, eb, cleanup_edit_baton);
4851 
4852   SVN_ERR(svn_wc__wq_run(eb->db, eb->wcroot_abspath,
4853                          eb->cancel_func, eb->cancel_baton,
4854                          eb->pool));
4855 
4856   /* The edit is over, free its pool.
4857      ### No, this is wrong.  Who says this editor/baton won't be used
4858      again?  But the change is not merely to remove this call.  We
4859      should also make eb->pool not be a subpool (see make_editor),
4860      and change callers of svn_client_{checkout,update,switch} to do
4861      better pool management. ### */
4862 
4863   svn_pool_destroy(eb->pool);
4864 
4865   return SVN_NO_ERROR;
4866 }
4867 
4868 
4869 /*** Returning editors. ***/
4870 
4871 /* Helper for the three public editor-supplying functions. */
4872 static svn_error_t *
make_editor(svn_revnum_t * target_revision,svn_wc__db_t * db,const char * anchor_abspath,const char * target_basename,apr_hash_t * wcroot_iprops,svn_boolean_t use_commit_times,const char * switch_url,svn_depth_t depth,svn_boolean_t depth_is_sticky,svn_boolean_t allow_unver_obstructions,svn_boolean_t adds_as_modification,svn_boolean_t server_performs_filtering,svn_boolean_t clean_checkout,svn_wc_notify_func2_t notify_func,void * notify_baton,svn_cancel_func_t cancel_func,void * cancel_baton,svn_wc_dirents_func_t fetch_dirents_func,void * fetch_dirents_baton,svn_wc_conflict_resolver_func2_t conflict_func,void * conflict_baton,svn_wc_external_update_t external_func,void * external_baton,const char * diff3_cmd,const apr_array_header_t * preserved_exts,const svn_delta_editor_t ** editor,void ** edit_baton,apr_pool_t * result_pool,apr_pool_t * scratch_pool)4873 make_editor(svn_revnum_t *target_revision,
4874             svn_wc__db_t *db,
4875             const char *anchor_abspath,
4876             const char *target_basename,
4877             apr_hash_t *wcroot_iprops,
4878             svn_boolean_t use_commit_times,
4879             const char *switch_url,
4880             svn_depth_t depth,
4881             svn_boolean_t depth_is_sticky,
4882             svn_boolean_t allow_unver_obstructions,
4883             svn_boolean_t adds_as_modification,
4884             svn_boolean_t server_performs_filtering,
4885             svn_boolean_t clean_checkout,
4886             svn_wc_notify_func2_t notify_func,
4887             void *notify_baton,
4888             svn_cancel_func_t cancel_func,
4889             void *cancel_baton,
4890             svn_wc_dirents_func_t fetch_dirents_func,
4891             void *fetch_dirents_baton,
4892             svn_wc_conflict_resolver_func2_t conflict_func,
4893             void *conflict_baton,
4894             svn_wc_external_update_t external_func,
4895             void *external_baton,
4896             const char *diff3_cmd,
4897             const apr_array_header_t *preserved_exts,
4898             const svn_delta_editor_t **editor,
4899             void **edit_baton,
4900             apr_pool_t *result_pool,
4901             apr_pool_t *scratch_pool)
4902 {
4903   struct edit_baton *eb;
4904   void *inner_baton;
4905   apr_pool_t *edit_pool = svn_pool_create(result_pool);
4906   svn_delta_editor_t *tree_editor = svn_delta_default_editor(edit_pool);
4907   const svn_delta_editor_t *inner_editor;
4908   const char *repos_root, *repos_uuid;
4909   struct svn_wc__shim_fetch_baton_t *sfb;
4910   svn_delta_shim_callbacks_t *shim_callbacks =
4911                                 svn_delta_shim_callbacks_default(edit_pool);
4912 
4913   /* An unknown depth can't be sticky. */
4914   if (depth == svn_depth_unknown)
4915     depth_is_sticky = FALSE;
4916 
4917   /* Get the anchor's repository root and uuid. The anchor must already exist
4918      in BASE. */
4919   SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, &repos_root,
4920                                    &repos_uuid, NULL, NULL, NULL, NULL,
4921                                    NULL, NULL, NULL, NULL, NULL, NULL,
4922                                    db, anchor_abspath,
4923                                    result_pool, scratch_pool));
4924 
4925   /* With WC-NG we need a valid repository root */
4926   SVN_ERR_ASSERT(repos_root != NULL && repos_uuid != NULL);
4927 
4928   /* Disallow a switch operation to change the repository root of the target,
4929      if that is known. */
4930   if (switch_url && !svn_uri__is_ancestor(repos_root, switch_url))
4931     return svn_error_createf(SVN_ERR_WC_INVALID_SWITCH, NULL,
4932                              _("'%s'\nis not the same repository as\n'%s'"),
4933                              switch_url, repos_root);
4934 
4935   /* Construct an edit baton. */
4936   eb = apr_pcalloc(edit_pool, sizeof(*eb));
4937   eb->pool                     = edit_pool;
4938   eb->use_commit_times         = use_commit_times;
4939   eb->target_revision          = target_revision;
4940   eb->repos_root               = repos_root;
4941   eb->repos_uuid               = repos_uuid;
4942   eb->db                       = db;
4943   eb->target_basename          = target_basename;
4944   eb->anchor_abspath           = anchor_abspath;
4945   eb->wcroot_iprops            = wcroot_iprops;
4946 
4947   SVN_ERR(svn_wc__db_get_wcroot(&eb->wcroot_abspath, db, anchor_abspath,
4948                                 edit_pool, scratch_pool));
4949 
4950   if (switch_url)
4951     eb->switch_repos_relpath =
4952       svn_uri_skip_ancestor(repos_root, switch_url, scratch_pool);
4953   else
4954     eb->switch_repos_relpath = NULL;
4955 
4956   if (svn_path_is_empty(target_basename))
4957     eb->target_abspath = eb->anchor_abspath;
4958   else
4959     eb->target_abspath = svn_dirent_join(eb->anchor_abspath, target_basename,
4960                                          edit_pool);
4961 
4962   eb->requested_depth          = depth;
4963   eb->depth_is_sticky          = depth_is_sticky;
4964   eb->notify_func              = notify_func;
4965   eb->notify_baton             = notify_baton;
4966   eb->external_func            = external_func;
4967   eb->external_baton           = external_baton;
4968   eb->diff3_cmd                = diff3_cmd;
4969   eb->cancel_func              = cancel_func;
4970   eb->cancel_baton             = cancel_baton;
4971   eb->conflict_func            = conflict_func;
4972   eb->conflict_baton           = conflict_baton;
4973   eb->allow_unver_obstructions = allow_unver_obstructions;
4974   eb->adds_as_modification     = adds_as_modification;
4975   eb->clean_checkout           = clean_checkout;
4976   eb->skipped_trees            = apr_hash_make(edit_pool);
4977   eb->dir_dirents              = apr_hash_make(edit_pool);
4978   eb->ext_patterns             = preserved_exts;
4979 
4980   apr_pool_cleanup_register(edit_pool, eb, cleanup_edit_baton,
4981                             apr_pool_cleanup_null);
4982 
4983   /* Construct an editor. */
4984   tree_editor->set_target_revision = set_target_revision;
4985   tree_editor->open_root = open_root;
4986   tree_editor->delete_entry = delete_entry;
4987   tree_editor->add_directory = add_directory;
4988   tree_editor->open_directory = open_directory;
4989   tree_editor->change_dir_prop = change_dir_prop;
4990   tree_editor->close_directory = close_directory;
4991   tree_editor->absent_directory = absent_directory;
4992   tree_editor->add_file = add_file;
4993   tree_editor->open_file = open_file;
4994   tree_editor->apply_textdelta = apply_textdelta;
4995   tree_editor->change_file_prop = change_file_prop;
4996   tree_editor->close_file = close_file;
4997   tree_editor->absent_file = absent_file;
4998   tree_editor->close_edit = close_edit;
4999 
5000   /* Fiddle with the type system. */
5001   inner_editor = tree_editor;
5002   inner_baton = eb;
5003 
5004   if (!depth_is_sticky
5005       && depth != svn_depth_unknown
5006       && svn_depth_empty <= depth && depth < svn_depth_infinity
5007       && fetch_dirents_func)
5008     {
5009       /* We are asked to perform an update at a depth less than the ambient
5010          depth. In this case the update won't describe additions that would
5011          have been reported if we updated at the ambient depth. */
5012       svn_error_t *err;
5013       svn_node_kind_t dir_kind;
5014       svn_wc__db_status_t dir_status;
5015       const char *dir_repos_relpath;
5016       svn_depth_t dir_depth;
5017 
5018       /* we have to do this on the target of the update, not the anchor */
5019       err = svn_wc__db_base_get_info(&dir_status, &dir_kind, NULL,
5020                                      &dir_repos_relpath, NULL, NULL, NULL,
5021                                      NULL, NULL, &dir_depth, NULL, NULL, NULL,
5022                                      NULL, NULL, NULL,
5023                                      db, eb->target_abspath,
5024                                      scratch_pool, scratch_pool);
5025 
5026       if (!err
5027           && dir_kind == svn_node_dir
5028           && dir_status == svn_wc__db_status_normal)
5029         {
5030           if (dir_depth > depth)
5031             {
5032               apr_hash_t *dirents;
5033 
5034               /* If we switch, we should look at the new relpath */
5035               if (eb->switch_repos_relpath)
5036                 dir_repos_relpath = eb->switch_repos_relpath;
5037 
5038               SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents,
5039                                          repos_root, dir_repos_relpath,
5040                                          edit_pool, scratch_pool));
5041 
5042               if (dirents != NULL && apr_hash_count(dirents))
5043                 svn_hash_sets(eb->dir_dirents,
5044                               apr_pstrdup(edit_pool, dir_repos_relpath),
5045                               dirents);
5046             }
5047 
5048           if (depth == svn_depth_immediates)
5049             {
5050               /* Worst case scenario of issue #3569 fix: We have to do the
5051                  same for all existing subdirs, but then we check for
5052                  svn_depth_empty. */
5053               const apr_array_header_t *children;
5054               apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5055               int i;
5056               SVN_ERR(svn_wc__db_base_get_children(&children, db,
5057                                                    eb->target_abspath,
5058                                                    scratch_pool,
5059                                                    iterpool));
5060 
5061               for (i = 0; i < children->nelts; i++)
5062                 {
5063                   const char *child_abspath;
5064                   const char *child_name;
5065 
5066                   svn_pool_clear(iterpool);
5067 
5068                   child_name = APR_ARRAY_IDX(children, i, const char *);
5069 
5070                   child_abspath = svn_dirent_join(eb->target_abspath,
5071                                                   child_name, iterpool);
5072 
5073                   SVN_ERR(svn_wc__db_base_get_info(&dir_status, &dir_kind,
5074                                                    NULL, &dir_repos_relpath,
5075                                                    NULL, NULL, NULL, NULL,
5076                                                    NULL, &dir_depth, NULL,
5077                                                    NULL, NULL, NULL, NULL,
5078                                                    NULL,
5079                                                    db, child_abspath,
5080                                                    iterpool, iterpool));
5081 
5082                   if (dir_kind == svn_node_dir
5083                       && dir_status == svn_wc__db_status_normal
5084                       && dir_depth > svn_depth_empty)
5085                     {
5086                       apr_hash_t *dirents;
5087 
5088                       /* If we switch, we should look at the new relpath */
5089                       if (eb->switch_repos_relpath)
5090                         dir_repos_relpath = svn_relpath_join(
5091                                                 eb->switch_repos_relpath,
5092                                                 child_name, iterpool);
5093 
5094                       SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents,
5095                                                  repos_root, dir_repos_relpath,
5096                                                  edit_pool, iterpool));
5097 
5098                       if (dirents != NULL && apr_hash_count(dirents))
5099                         svn_hash_sets(eb->dir_dirents,
5100                                       apr_pstrdup(edit_pool,
5101                                                   dir_repos_relpath),
5102                                       dirents);
5103                     }
5104                 }
5105             }
5106         }
5107       else if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
5108         svn_error_clear(err);
5109       else
5110         SVN_ERR(err);
5111     }
5112 
5113   /* We need to limit the scope of our operation to the ambient depths
5114      present in the working copy already, but only if the requested
5115      depth is not sticky. If a depth was explicitly requested,
5116      libsvn_delta/depth_filter_editor.c will ensure that we never see
5117      editor calls that extend beyond the scope of the requested depth.
5118      But even what we do so might extend beyond the scope of our
5119      ambient depth.  So we use another filtering editor to avoid
5120      modifying the ambient working copy depth when not asked to do so.
5121      (This can also be skipped if the server understands depth.) */
5122   if (!server_performs_filtering
5123       && !depth_is_sticky)
5124     SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor,
5125                                                 &inner_baton,
5126                                                 db,
5127                                                 anchor_abspath,
5128                                                 target_basename,
5129                                                 inner_editor,
5130                                                 inner_baton,
5131                                                 result_pool));
5132 
5133   SVN_ERR(svn_delta_get_cancellation_editor(cancel_func,
5134                                             cancel_baton,
5135                                             inner_editor,
5136                                             inner_baton,
5137                                             editor,
5138                                             edit_baton,
5139                                             result_pool));
5140 
5141   sfb = apr_palloc(result_pool, sizeof(*sfb));
5142   sfb->db = db;
5143   sfb->base_abspath = eb->anchor_abspath;
5144   sfb->fetch_base = TRUE;
5145 
5146   shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func;
5147   shim_callbacks->fetch_props_func = svn_wc__fetch_props_func;
5148   shim_callbacks->fetch_base_func = svn_wc__fetch_base_func;
5149   shim_callbacks->fetch_baton = sfb;
5150 
5151   SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
5152                                    NULL, NULL, shim_callbacks,
5153                                    result_pool, scratch_pool));
5154 
5155   return SVN_NO_ERROR;
5156 }
5157 
5158 
5159 svn_error_t *
svn_wc__get_update_editor(const svn_delta_editor_t ** editor,void ** edit_baton,svn_revnum_t * target_revision,svn_wc_context_t * wc_ctx,const char * anchor_abspath,const char * target_basename,apr_hash_t * wcroot_iprops,svn_boolean_t use_commit_times,svn_depth_t depth,svn_boolean_t depth_is_sticky,svn_boolean_t allow_unver_obstructions,svn_boolean_t adds_as_modification,svn_boolean_t server_performs_filtering,svn_boolean_t clean_checkout,const char * diff3_cmd,const apr_array_header_t * preserved_exts,svn_wc_dirents_func_t fetch_dirents_func,void * fetch_dirents_baton,svn_wc_conflict_resolver_func2_t conflict_func,void * conflict_baton,svn_wc_external_update_t external_func,void * external_baton,svn_cancel_func_t cancel_func,void * cancel_baton,svn_wc_notify_func2_t notify_func,void * notify_baton,apr_pool_t * result_pool,apr_pool_t * scratch_pool)5160 svn_wc__get_update_editor(const svn_delta_editor_t **editor,
5161                           void **edit_baton,
5162                           svn_revnum_t *target_revision,
5163                           svn_wc_context_t *wc_ctx,
5164                           const char *anchor_abspath,
5165                           const char *target_basename,
5166                           apr_hash_t *wcroot_iprops,
5167                           svn_boolean_t use_commit_times,
5168                           svn_depth_t depth,
5169                           svn_boolean_t depth_is_sticky,
5170                           svn_boolean_t allow_unver_obstructions,
5171                           svn_boolean_t adds_as_modification,
5172                           svn_boolean_t server_performs_filtering,
5173                           svn_boolean_t clean_checkout,
5174                           const char *diff3_cmd,
5175                           const apr_array_header_t *preserved_exts,
5176                           svn_wc_dirents_func_t fetch_dirents_func,
5177                           void *fetch_dirents_baton,
5178                           svn_wc_conflict_resolver_func2_t conflict_func,
5179                           void *conflict_baton,
5180                           svn_wc_external_update_t external_func,
5181                           void *external_baton,
5182                           svn_cancel_func_t cancel_func,
5183                           void *cancel_baton,
5184                           svn_wc_notify_func2_t notify_func,
5185                           void *notify_baton,
5186                           apr_pool_t *result_pool,
5187                           apr_pool_t *scratch_pool)
5188 {
5189   return make_editor(target_revision, wc_ctx->db, anchor_abspath,
5190                      target_basename, wcroot_iprops, use_commit_times,
5191                      NULL, depth, depth_is_sticky, allow_unver_obstructions,
5192                      adds_as_modification, server_performs_filtering,
5193                      clean_checkout,
5194                      notify_func, notify_baton,
5195                      cancel_func, cancel_baton,
5196                      fetch_dirents_func, fetch_dirents_baton,
5197                      conflict_func, conflict_baton,
5198                      external_func, external_baton,
5199                      diff3_cmd, preserved_exts, editor, edit_baton,
5200                      result_pool, scratch_pool);
5201 }
5202 
5203 svn_error_t *
svn_wc__get_switch_editor(const svn_delta_editor_t ** editor,void ** edit_baton,svn_revnum_t * target_revision,svn_wc_context_t * wc_ctx,const char * anchor_abspath,const char * target_basename,const char * switch_url,apr_hash_t * wcroot_iprops,svn_boolean_t use_commit_times,svn_depth_t depth,svn_boolean_t depth_is_sticky,svn_boolean_t allow_unver_obstructions,svn_boolean_t server_performs_filtering,const char * diff3_cmd,const apr_array_header_t * preserved_exts,svn_wc_dirents_func_t fetch_dirents_func,void * fetch_dirents_baton,svn_wc_conflict_resolver_func2_t conflict_func,void * conflict_baton,svn_wc_external_update_t external_func,void * external_baton,svn_cancel_func_t cancel_func,void * cancel_baton,svn_wc_notify_func2_t notify_func,void * notify_baton,apr_pool_t * result_pool,apr_pool_t * scratch_pool)5204 svn_wc__get_switch_editor(const svn_delta_editor_t **editor,
5205                           void **edit_baton,
5206                           svn_revnum_t *target_revision,
5207                           svn_wc_context_t *wc_ctx,
5208                           const char *anchor_abspath,
5209                           const char *target_basename,
5210                           const char *switch_url,
5211                           apr_hash_t *wcroot_iprops,
5212                           svn_boolean_t use_commit_times,
5213                           svn_depth_t depth,
5214                           svn_boolean_t depth_is_sticky,
5215                           svn_boolean_t allow_unver_obstructions,
5216                           svn_boolean_t server_performs_filtering,
5217                           const char *diff3_cmd,
5218                           const apr_array_header_t *preserved_exts,
5219                           svn_wc_dirents_func_t fetch_dirents_func,
5220                           void *fetch_dirents_baton,
5221                           svn_wc_conflict_resolver_func2_t conflict_func,
5222                           void *conflict_baton,
5223                           svn_wc_external_update_t external_func,
5224                           void *external_baton,
5225                           svn_cancel_func_t cancel_func,
5226                           void *cancel_baton,
5227                           svn_wc_notify_func2_t notify_func,
5228                           void *notify_baton,
5229                           apr_pool_t *result_pool,
5230                           apr_pool_t *scratch_pool)
5231 {
5232   SVN_ERR_ASSERT(switch_url && svn_uri_is_canonical(switch_url, scratch_pool));
5233 
5234   return make_editor(target_revision, wc_ctx->db, anchor_abspath,
5235                      target_basename, wcroot_iprops, use_commit_times,
5236                      switch_url,
5237                      depth, depth_is_sticky, allow_unver_obstructions,
5238                      FALSE /* adds_as_modification */,
5239                      server_performs_filtering,
5240                      FALSE /* clean_checkout */,
5241                      notify_func, notify_baton,
5242                      cancel_func, cancel_baton,
5243                      fetch_dirents_func, fetch_dirents_baton,
5244                      conflict_func, conflict_baton,
5245                      external_func, external_baton,
5246                      diff3_cmd, preserved_exts,
5247                      editor, edit_baton,
5248                      result_pool, scratch_pool);
5249 }
5250 
5251 
5252 
5253 /* ### Note that this function is completely different from the rest of the
5254        update editor in what it updates. The update editor changes only BASE
5255        and ACTUAL and this function just changes WORKING and ACTUAL.
5256 
5257        In the entries world this function shared a lot of code with the
5258        update editor but in the wonderful new WC-NG world it will probably
5259        do more and more by itself and would be more logically grouped with
5260        the add/copy functionality in adm_ops.c and copy.c. */
5261 svn_error_t *
svn_wc_add_repos_file4(svn_wc_context_t * wc_ctx,const char * local_abspath,svn_stream_t * new_base_contents,svn_stream_t * new_contents,apr_hash_t * new_base_props,apr_hash_t * new_props,const char * copyfrom_url,svn_revnum_t copyfrom_rev,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)5262 svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx,
5263                        const char *local_abspath,
5264                        svn_stream_t *new_base_contents,
5265                        svn_stream_t *new_contents,
5266                        apr_hash_t *new_base_props,
5267                        apr_hash_t *new_props,
5268                        const char *copyfrom_url,
5269                        svn_revnum_t copyfrom_rev,
5270                        svn_cancel_func_t cancel_func,
5271                        void *cancel_baton,
5272                        apr_pool_t *scratch_pool)
5273 {
5274   svn_wc__db_t *db = wc_ctx->db;
5275   const char *dir_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
5276   svn_wc__db_status_t status;
5277   svn_node_kind_t kind;
5278   const char *tmp_text_base_abspath;
5279   svn_checksum_t *new_text_base_md5_checksum;
5280   svn_checksum_t *new_text_base_sha1_checksum;
5281   const char *source_abspath = NULL;
5282   svn_skel_t *all_work_items = NULL;
5283   svn_skel_t *work_item;
5284   const char *repos_root_url;
5285   const char *repos_uuid;
5286   const char *original_repos_relpath;
5287   svn_revnum_t changed_rev;
5288   apr_time_t changed_date;
5289   const char *changed_author;
5290   svn_stream_t *tmp_base_contents;
5291   svn_wc__db_install_data_t *install_data;
5292   svn_error_t *err;
5293   apr_pool_t *pool = scratch_pool;
5294 
5295   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5296   SVN_ERR_ASSERT(new_base_contents != NULL);
5297   SVN_ERR_ASSERT(new_base_props != NULL);
5298 
5299   /* We should have a write lock on this file's parent directory.  */
5300   SVN_ERR(svn_wc__write_check(db, dir_abspath, pool));
5301 
5302   err = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5303                              NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5304                              NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5305                              NULL, NULL, NULL,
5306                              db, local_abspath, scratch_pool, scratch_pool);
5307 
5308   if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
5309     return svn_error_trace(err);
5310   else if(err)
5311     svn_error_clear(err);
5312   else
5313     switch (status)
5314       {
5315         case svn_wc__db_status_not_present:
5316         case svn_wc__db_status_deleted:
5317           break;
5318         default:
5319           return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
5320                                    _("Node '%s' exists."),
5321                                    svn_dirent_local_style(local_abspath,
5322                                                           scratch_pool));
5323       }
5324 
5325   SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, &repos_root_url,
5326                                &repos_uuid, NULL, NULL, NULL, NULL, NULL, NULL,
5327                                NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5328                                NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5329                                db, dir_abspath, scratch_pool, scratch_pool));
5330 
5331   switch (status)
5332     {
5333       case svn_wc__db_status_normal:
5334       case svn_wc__db_status_added:
5335         break;
5336       case svn_wc__db_status_deleted:
5337         return
5338           svn_error_createf(SVN_ERR_WC_SCHEDULE_CONFLICT, NULL,
5339                             _("Can't add '%s' to a parent directory"
5340                               " scheduled for deletion"),
5341                             svn_dirent_local_style(local_abspath,
5342                                                    scratch_pool));
5343       default:
5344         return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, err,
5345                                  _("Can't find parent directory's node while"
5346                                    " trying to add '%s'"),
5347                                  svn_dirent_local_style(local_abspath,
5348                                                         scratch_pool));
5349     }
5350   if (kind != svn_node_dir)
5351     return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
5352                              _("Can't schedule an addition of '%s'"
5353                                " below a not-directory node"),
5354                              svn_dirent_local_style(local_abspath,
5355                                                     scratch_pool));
5356 
5357   /* Fabricate the anticipated new URL of the target and check the
5358      copyfrom URL to be in the same repository. */
5359   if (copyfrom_url != NULL)
5360     {
5361       /* Find the repository_root via the parent directory, which
5362          is always versioned before this function is called */
5363 
5364       if (!repos_root_url)
5365         {
5366           /* The parent is an addition, scan upwards to find the right info */
5367           SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL,
5368                                            &repos_root_url, &repos_uuid,
5369                                            NULL, NULL, NULL, NULL,
5370                                            wc_ctx->db, dir_abspath,
5371                                            scratch_pool, scratch_pool));
5372         }
5373       SVN_ERR_ASSERT(repos_root_url);
5374 
5375       original_repos_relpath =
5376           svn_uri_skip_ancestor(repos_root_url, copyfrom_url, scratch_pool);
5377 
5378       if (!original_repos_relpath)
5379         return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
5380                                  _("Copyfrom-url '%s' has different repository"
5381                                    " root than '%s'"),
5382                                  copyfrom_url, repos_root_url);
5383     }
5384   else
5385     {
5386       original_repos_relpath = NULL;
5387       copyfrom_rev = SVN_INVALID_REVNUM;  /* Just to be sure.  */
5388     }
5389 
5390   /* Set CHANGED_* to reflect the entry props in NEW_BASE_PROPS, and
5391      filter NEW_BASE_PROPS so it contains only regular props. */
5392   {
5393     apr_array_header_t *regular_props;
5394     apr_array_header_t *entry_props;
5395 
5396     SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_base_props, pool),
5397                                  &entry_props, NULL, &regular_props,
5398                                  pool));
5399 
5400     /* Put regular props back into a hash table. */
5401     new_base_props = svn_prop_array_to_hash(regular_props, pool);
5402 
5403     /* Get the change_* info from the entry props.  */
5404     SVN_ERR(accumulate_last_change(&changed_rev,
5405                                    &changed_date,
5406                                    &changed_author,
5407                                    entry_props, pool, pool));
5408   }
5409 
5410   /* Copy NEW_BASE_CONTENTS into a temporary file so our log can refer to
5411      it, and set TMP_TEXT_BASE_ABSPATH to its path.  Compute its
5412      NEW_TEXT_BASE_MD5_CHECKSUM and NEW_TEXT_BASE_SHA1_CHECKSUM as we copy. */
5413   if (copyfrom_url)
5414     {
5415       SVN_ERR(svn_wc__db_pristine_prepare_install(&tmp_base_contents,
5416                                                   &install_data,
5417                                                   &new_text_base_sha1_checksum,
5418                                                   &new_text_base_md5_checksum,
5419                                                   wc_ctx->db, local_abspath,
5420                                                   scratch_pool, scratch_pool));
5421     }
5422   else
5423     {
5424       const char *tmp_dir_abspath;
5425 
5426       /* We are not installing a PRISTINE file, but we use the same code to
5427          create whatever we want to install */
5428 
5429       SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmp_dir_abspath,
5430                                              db, dir_abspath,
5431                                              scratch_pool, scratch_pool));
5432 
5433       SVN_ERR(svn_stream_open_unique(&tmp_base_contents, &tmp_text_base_abspath,
5434                                      tmp_dir_abspath, svn_io_file_del_none,
5435                                      scratch_pool, scratch_pool));
5436 
5437       new_text_base_sha1_checksum = NULL;
5438       new_text_base_md5_checksum = NULL;
5439     }
5440   SVN_ERR(svn_stream_copy3(new_base_contents, tmp_base_contents,
5441                            cancel_func, cancel_baton, pool));
5442 
5443   /* If the caller gave us a new working file, copy it to a safe (temporary)
5444      location and set SOURCE_ABSPATH to that path. We'll then translate/copy
5445      that into place after the node's state has been created.  */
5446   if (new_contents)
5447     {
5448       const char *temp_dir_abspath;
5449       svn_stream_t *tmp_contents;
5450 
5451       SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db,
5452                                              local_abspath, pool, pool));
5453       SVN_ERR(svn_stream_open_unique(&tmp_contents, &source_abspath,
5454                                      temp_dir_abspath, svn_io_file_del_none,
5455                                      pool, pool));
5456       SVN_ERR(svn_stream_copy3(new_contents, tmp_contents,
5457                                cancel_func, cancel_baton, pool));
5458     }
5459 
5460   /* Install new text base for copied files. Added files do NOT have a
5461      text base.  */
5462   if (copyfrom_url != NULL)
5463     {
5464       SVN_ERR(svn_wc__db_pristine_install(install_data,
5465                                           new_text_base_sha1_checksum,
5466                                           new_text_base_md5_checksum, pool));
5467     }
5468   else
5469     {
5470       /* ### There's something wrong around here.  Sometimes (merge from a
5471          foreign repository, at least) we are called with copyfrom_url =
5472          NULL and an empty new_base_contents (and an empty set of
5473          new_base_props).  Why an empty "new base"?
5474 
5475          That happens in merge_tests.py 54,87,88,89,143.
5476 
5477          In that case, having been given this supposed "new base" file, we
5478          copy it and calculate its checksum but do not install it.  Why?
5479          That must be wrong.
5480 
5481          To crudely work around one issue with this, that we shouldn't
5482          record a checksum in the database if we haven't installed the
5483          corresponding pristine text, for now we'll just set the checksum
5484          to NULL.
5485 
5486          The proper solution is probably more like: the caller should pass
5487          NULL for the missing information, and this function should learn to
5488          handle that. */
5489 
5490       new_text_base_sha1_checksum = NULL;
5491       new_text_base_md5_checksum = NULL;
5492     }
5493 
5494   /* For added files without NEW_CONTENTS, then generate the working file
5495      from the provided "pristine" contents.  */
5496   if (new_contents == NULL && copyfrom_url == NULL)
5497     source_abspath = tmp_text_base_abspath;
5498 
5499   {
5500     svn_boolean_t record_fileinfo;
5501 
5502     /* If new contents were provided, then we do NOT want to record the
5503        file information. We assume the new contents do not match the
5504        "proper" values for RECORDED_SIZE and RECORDED_TIME.  */
5505     record_fileinfo = (new_contents == NULL);
5506 
5507     /* Install the working copy file (with appropriate translation) from
5508        the appropriate source. SOURCE_ABSPATH will be NULL, indicating an
5509        installation from the pristine (available for copied/moved files),
5510        or it will specify a temporary file where we placed a "pristine"
5511        (for an added file) or a detranslated local-mods file.  */
5512     SVN_ERR(svn_wc__wq_build_file_install(&work_item,
5513                                           db, local_abspath,
5514                                           source_abspath,
5515                                           FALSE /* use_commit_times */,
5516                                           record_fileinfo,
5517                                           pool, pool));
5518     all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
5519 
5520     /* If we installed from somewhere besides the official pristine, then
5521        it is a temporary file, which needs to be removed.  */
5522     if (source_abspath != NULL)
5523       {
5524         SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, local_abspath,
5525                                              source_abspath,
5526                                              pool, pool));
5527         all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
5528       }
5529   }
5530 
5531   SVN_ERR(svn_wc__db_op_copy_file(db, local_abspath,
5532                                   new_base_props,
5533                                   changed_rev,
5534                                   changed_date,
5535                                   changed_author,
5536                                   original_repos_relpath,
5537                                   original_repos_relpath ? repos_root_url
5538                                                          : NULL,
5539                                   original_repos_relpath ? repos_uuid : NULL,
5540                                   copyfrom_rev,
5541                                   new_text_base_sha1_checksum,
5542                                   TRUE,
5543                                   new_props,
5544                                   FALSE /* is_move */,
5545                                   NULL /* conflict */,
5546                                   all_work_items,
5547                                   pool));
5548 
5549   return svn_error_trace(svn_wc__wq_run(db, dir_abspath,
5550                                         cancel_func, cancel_baton,
5551                                         pool));
5552 }
5553 
5554 svn_error_t *
svn_wc__complete_directory_add(svn_wc_context_t * wc_ctx,const char * local_abspath,apr_hash_t * new_original_props,const char * copyfrom_url,svn_revnum_t copyfrom_rev,apr_pool_t * scratch_pool)5555 svn_wc__complete_directory_add(svn_wc_context_t *wc_ctx,
5556                                const char *local_abspath,
5557                                apr_hash_t *new_original_props,
5558                                const char *copyfrom_url,
5559                                svn_revnum_t copyfrom_rev,
5560                                apr_pool_t *scratch_pool)
5561 {
5562   svn_wc__db_status_t status;
5563   svn_node_kind_t kind;
5564   const char *original_repos_relpath;
5565   const char *original_root_url;
5566   const char *original_uuid;
5567   svn_boolean_t had_props;
5568   svn_boolean_t props_mod;
5569 
5570   svn_revnum_t original_revision;
5571   svn_revnum_t changed_rev;
5572   apr_time_t changed_date;
5573   const char *changed_author;
5574 
5575   SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
5576                                NULL, NULL, NULL, NULL, NULL,
5577                                &original_repos_relpath, &original_root_url,
5578                                &original_uuid, &original_revision, NULL, NULL,
5579                                NULL, NULL, NULL, NULL, &had_props, &props_mod,
5580                                NULL, NULL, NULL,
5581                                wc_ctx->db, local_abspath,
5582                                scratch_pool, scratch_pool));
5583 
5584   if (status != svn_wc__db_status_added
5585       || kind != svn_node_dir
5586       || had_props
5587       || props_mod
5588       || !original_repos_relpath)
5589     {
5590       return svn_error_createf(
5591                     SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5592                     _("'%s' is not an unmodified copied directory"),
5593                     svn_dirent_local_style(local_abspath, scratch_pool));
5594     }
5595   if (original_revision != copyfrom_rev
5596       || strcmp(copyfrom_url,
5597                  svn_path_url_add_component2(original_root_url,
5598                                              original_repos_relpath,
5599                                              scratch_pool)))
5600     {
5601       return svn_error_createf(
5602                     SVN_ERR_WC_COPYFROM_PATH_NOT_FOUND, NULL,
5603                     _("Copyfrom '%s' doesn't match original location of '%s'"),
5604                     copyfrom_url,
5605                     svn_dirent_local_style(local_abspath, scratch_pool));
5606     }
5607 
5608   {
5609     apr_array_header_t *regular_props;
5610     apr_array_header_t *entry_props;
5611 
5612     SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_original_props,
5613                                                         scratch_pool),
5614                                  &entry_props, NULL, &regular_props,
5615                                  scratch_pool));
5616 
5617     /* Put regular props back into a hash table. */
5618     new_original_props = svn_prop_array_to_hash(regular_props, scratch_pool);
5619 
5620     /* Get the change_* info from the entry props.  */
5621     SVN_ERR(accumulate_last_change(&changed_rev,
5622                                    &changed_date,
5623                                    &changed_author,
5624                                    entry_props, scratch_pool, scratch_pool));
5625   }
5626 
5627   return svn_error_trace(
5628             svn_wc__db_op_copy_dir(wc_ctx->db, local_abspath,
5629                                    new_original_props,
5630                                    changed_rev, changed_date, changed_author,
5631                                    original_repos_relpath, original_root_url,
5632                                    original_uuid, original_revision,
5633                                    NULL /* children */,
5634                                    svn_depth_infinity,
5635                                    FALSE /* is_move */,
5636                                    NULL /* conflict */,
5637                                    NULL /* work_items */,
5638                                    scratch_pool));
5639 }
5640