1 /*
2  * workqueue.c :  manipulating work queue items
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23 
24 #include <apr_pools.h>
25 
26 #include "svn_private_config.h"
27 #include "svn_types.h"
28 #include "svn_pools.h"
29 #include "svn_dirent_uri.h"
30 #include "svn_subst.h"
31 #include "svn_hash.h"
32 #include "svn_io.h"
33 
34 #include "wc.h"
35 #include "wc_db.h"
36 #include "workqueue.h"
37 #include "adm_files.h"
38 #include "conflicts.h"
39 #include "translate.h"
40 
41 #include "private/svn_io_private.h"
42 #include "private/svn_skel.h"
43 
44 
45 /* Workqueue operation names.  */
46 #define OP_FILE_COMMIT "file-commit"
47 #define OP_FILE_INSTALL "file-install"
48 #define OP_FILE_REMOVE "file-remove"
49 #define OP_FILE_MOVE "file-move"
50 #define OP_FILE_COPY_TRANSLATED "file-translate"
51 #define OP_SYNC_FILE_FLAGS "sync-file-flags"
52 #define OP_PREJ_INSTALL "prej-install"
53 #define OP_DIRECTORY_REMOVE "dir-remove"
54 #define OP_DIRECTORY_INSTALL "dir-install"
55 
56 #define OP_POSTUPGRADE "postupgrade"
57 
58 /* Legacy items */
59 #define OP_BASE_REMOVE "base-remove"
60 #define OP_RECORD_FILEINFO "record-fileinfo"
61 #define OP_TMP_SET_TEXT_CONFLICT_MARKERS "tmp-set-text-conflict-markers"
62 #define OP_TMP_SET_PROPERTY_CONFLICT_MARKER "tmp-set-property-conflict-marker"
63 
64 /* For work queue debugging. Generates output about its operation.  */
65 /* #define SVN_DEBUG_WORK_QUEUE */
66 
67 typedef struct work_item_baton_t work_item_baton_t;
68 
69 struct work_item_dispatch {
70   const char *name;
71   svn_error_t *(*func)(work_item_baton_t *wqb,
72                        svn_wc__db_t *db,
73                        const svn_skel_t *work_item,
74                        const char *wri_abspath,
75                        svn_cancel_func_t cancel_func,
76                        void *cancel_baton,
77                        apr_pool_t *scratch_pool);
78 };
79 
80 /* Forward definition */
81 static svn_error_t *
82 get_and_record_fileinfo(work_item_baton_t *wqb,
83                         const char *local_abspath,
84                         svn_boolean_t ignore_enoent,
85                         apr_pool_t *scratch_pool);
86 
87 /* ------------------------------------------------------------------------ */
88 /* OP_REMOVE_BASE  */
89 
90 /* Removes a BASE_NODE and all it's data, leaving any adds and copies as is.
91    Do this as a depth first traversal to make sure than any parent still exists
92    on error conditions.
93  */
94 
95 /* Process the OP_REMOVE_BASE work item WORK_ITEM.
96  * See svn_wc__wq_build_remove_base() which generates this work item.
97  * Implements (struct work_item_dispatch).func. */
98 static svn_error_t *
run_base_remove(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)99 run_base_remove(work_item_baton_t *wqb,
100                 svn_wc__db_t *db,
101                 const svn_skel_t *work_item,
102                 const char *wri_abspath,
103                 svn_cancel_func_t cancel_func,
104                 void *cancel_baton,
105                 apr_pool_t *scratch_pool)
106 {
107   const svn_skel_t *arg1 = work_item->children->next;
108   const char *local_relpath;
109   const char *local_abspath;
110   svn_revnum_t not_present_rev = SVN_INVALID_REVNUM;
111   apr_int64_t val;
112 
113   local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
114   SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
115                                   local_relpath, scratch_pool, scratch_pool));
116   SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
117 
118   if (arg1->next->next)
119     {
120       not_present_rev = (svn_revnum_t)val;
121 
122       SVN_ERR(svn_skel__parse_int(&val, arg1->next->next, scratch_pool));
123     }
124   else
125     {
126       svn_boolean_t keep_not_present;
127 
128       SVN_ERR_ASSERT(SVN_WC__VERSION <= 28); /* Case unused in later versions*/
129 
130       keep_not_present = (val != 0);
131 
132       if (keep_not_present)
133         {
134           SVN_ERR(svn_wc__db_base_get_info(NULL, NULL,
135                                            &not_present_rev, NULL,
136                                            NULL, NULL, NULL,
137                                            NULL, NULL, NULL, NULL, NULL, NULL,
138                                            NULL, NULL, NULL,
139                                            db, local_abspath,
140                                            scratch_pool, scratch_pool));
141         }
142     }
143 
144   SVN_ERR(svn_wc__db_base_remove(db, local_abspath,
145                                  FALSE /* keep_as_working */,
146                                  SVN_IS_VALID_REVNUM(not_present_rev), FALSE,
147                                  not_present_rev,
148                                  NULL, NULL, scratch_pool));
149 
150   return SVN_NO_ERROR;
151 }
152 
153 /* ------------------------------------------------------------------------ */
154 
155 /* ------------------------------------------------------------------------ */
156 
157 /* OP_FILE_COMMIT  */
158 
159 
160 /* FILE_ABSPATH is the new text base of the newly-committed versioned file,
161  * in repository-normal form (aka "detranslated" form).  Adjust the working
162  * file accordingly.
163  *
164  * If eol and/or keyword translation would cause the working file to
165  * change, then overwrite the working file with a translated copy of
166  * the new text base (but only if the translated copy differs from the
167  * current working file -- if they are the same, do nothing, to avoid
168  * clobbering timestamps unnecessarily).
169  *
170  * Set the working file's executability according to its svn:executable
171  * property.
172  *
173  * Set the working file's read-only attribute according to its properties
174  * and lock status (see svn_wc__maybe_set_read_only()).
175  *
176  * If the working file was re-translated or had its executability or
177  * read-only state changed,
178  * then set OVERWROTE_WORKING to TRUE.  If the working file isn't
179  * touched at all, then set to FALSE.
180  *
181  * Use SCRATCH_POOL for any temporary allocation.
182  */
183 static svn_error_t *
install_committed_file(svn_boolean_t * overwrote_working,svn_wc__db_t * db,const char * file_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)184 install_committed_file(svn_boolean_t *overwrote_working,
185                        svn_wc__db_t *db,
186                        const char *file_abspath,
187                        svn_cancel_func_t cancel_func,
188                        void *cancel_baton,
189                        apr_pool_t *scratch_pool)
190 {
191   svn_boolean_t same;
192   const char *tmp_wfile;
193   svn_boolean_t special;
194 
195   /* start off assuming that the working file isn't touched. */
196   *overwrote_working = FALSE;
197 
198   /* In the commit, newlines and keywords may have been
199    * canonicalized and/or contracted... Or they may not have
200    * been.  It's kind of hard to know.  Here's how we find out:
201    *
202    *    1. Make a translated tmp copy of the committed text base,
203    *       translated according to the versioned file's properties.
204    *       Or, if no committed text base exists (the commit must have
205    *       been a propchange only), make a translated tmp copy of the
206    *       working file.
207    *    2. Compare the translated tmpfile to the working file.
208    *    3. If different, copy the tmpfile over working file.
209    *
210    * This means we only rewrite the working file if we absolutely
211    * have to, which is good because it avoids changing the file's
212    * timestamp unless necessary, so editors aren't tempted to
213    * reread the file if they don't really need to.
214    */
215 
216   /* Copy and translate the new base-to-be file (if found, else the working
217    * file) from repository-normal form to working form, writing a new
218    * temporary file if any translation was actually done.  Set TMP_WFILE to
219    * the translated file's path, which may be the source file's path if no
220    * translation was done.  Set SAME to indicate whether the new working
221    * text is the same as the old working text (or TRUE if it's a special
222    * file). */
223   {
224     const char *tmp = file_abspath;
225 
226     /* Copy and translate, if necessary. The output file will be deleted at
227      * scratch_pool cleanup.
228      * ### That's not quite safe: we might rename the file and then maybe
229      * its path will get re-used for another temp file before pool clean-up.
230      * Instead, we should take responsibility for deleting it. */
231     SVN_ERR(svn_wc__internal_translated_file(&tmp_wfile, tmp, db,
232                                              file_abspath,
233                                              SVN_WC_TRANSLATE_FROM_NF,
234                                              cancel_func, cancel_baton,
235                                              scratch_pool, scratch_pool));
236 
237     /* If the translation is a no-op, the text base and the working copy
238      * file contain the same content, because we use the same props here
239      * as were used to detranslate from working file to text base.
240      *
241      * In that case: don't replace the working file, but make sure
242      * it has the right executable and read_write attributes set.
243      */
244 
245     SVN_ERR(svn_wc__get_translate_info(NULL, NULL,
246                                        NULL,
247                                        &special,
248                                        db, file_abspath, NULL, FALSE,
249                                        scratch_pool, scratch_pool));
250     /* Translated file returns the exact pointer if not translated. */
251     if (! special && tmp != tmp_wfile)
252       SVN_ERR(svn_io_files_contents_same_p(&same, tmp_wfile,
253                                            file_abspath, scratch_pool));
254     else
255       same = TRUE;
256   }
257 
258   if (! same)
259     {
260       SVN_ERR(svn_io_file_rename2(tmp_wfile, file_abspath, FALSE,
261                                   scratch_pool));
262       *overwrote_working = TRUE;
263     }
264 
265   /* ### should be using OP_SYNC_FILE_FLAGS, or an internal version of
266      ### that here. do we need to set *OVERWROTE_WORKING? */
267 
268   /* ### Re: OVERWROTE_WORKING, the following function is rather liberal
269      ### with setting that flag, so we should probably decide if we really
270      ### care about it when syncing flags. */
271   SVN_ERR(svn_wc__sync_flags_with_props(overwrote_working, db, file_abspath,
272                                         scratch_pool));
273 
274   return SVN_NO_ERROR;
275 }
276 
277 static svn_error_t *
process_commit_file_install(svn_wc__db_t * db,const char * local_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)278 process_commit_file_install(svn_wc__db_t *db,
279                        const char *local_abspath,
280                        svn_cancel_func_t cancel_func,
281                        void *cancel_baton,
282                        apr_pool_t *scratch_pool)
283 {
284   svn_boolean_t overwrote_working;
285 
286   /* Install the new file, which may involve expanding keywords.
287      A copy of this file should have been dropped into our `tmp/text-base'
288      directory during the commit process.  Part of this process
289      involves recording the textual timestamp for this entry.  We'd like
290      to just use the timestamp of the working file, but it is possible
291      that at some point during the commit, the real working file might
292      have changed again.
293    */
294 
295   SVN_ERR(install_committed_file(&overwrote_working, db,
296                                  local_abspath,
297                                  cancel_func, cancel_baton,
298                                  scratch_pool));
299 
300   /* We will compute and modify the size and timestamp */
301   if (overwrote_working)
302     {
303       apr_finfo_t finfo;
304 
305       SVN_ERR(svn_io_stat(&finfo, local_abspath,
306                           APR_FINFO_MIN | APR_FINFO_LINK, scratch_pool));
307       SVN_ERR(svn_wc__db_global_record_fileinfo(db, local_abspath,
308                                                 finfo.size, finfo.mtime,
309                                                 scratch_pool));
310     }
311   else
312     {
313       svn_boolean_t modified;
314 
315       /* The working copy file hasn't been overwritten.  We just
316          removed the recorded size and modification time from the nodes
317          record by calling svn_wc__db_global_commit().
318 
319          Now we have some file in our working copy that might be what
320          we just committed, but we are not certain at this point.
321 
322          We still have a write lock here, so we check if the file is
323          what we expect it to be and if it is the right file we update
324          the recorded information. (If it isn't we keep the null data).
325 
326          Instead of reimplementing all this here, we just call a function
327          that already does implement this when it notices that we have the
328          right kind of lock (and we ignore the result)
329        */
330       SVN_ERR(svn_wc__internal_file_modified_p(&modified,
331                                                db, local_abspath, FALSE,
332                                                scratch_pool));
333     }
334   return SVN_NO_ERROR;
335 }
336 
337 
338 static svn_error_t *
run_file_commit(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)339 run_file_commit(work_item_baton_t *wqb,
340                 svn_wc__db_t *db,
341                 const svn_skel_t *work_item,
342                 const char *wri_abspath,
343                 svn_cancel_func_t cancel_func,
344                 void *cancel_baton,
345                 apr_pool_t *scratch_pool)
346 {
347   const svn_skel_t *arg1 = work_item->children->next;
348   const char *local_relpath;
349   const char *local_abspath;
350 
351   local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
352   SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
353                                   local_relpath, scratch_pool, scratch_pool));
354 
355   /* We don't both parsing the other two values in the skel. */
356 
357   return svn_error_trace(
358                 process_commit_file_install(db, local_abspath,
359                                             cancel_func, cancel_baton,
360                                             scratch_pool));
361 }
362 
363 svn_error_t *
svn_wc__wq_build_file_commit(svn_skel_t ** work_item,svn_wc__db_t * db,const char * local_abspath,svn_boolean_t props_mod,apr_pool_t * result_pool,apr_pool_t * scratch_pool)364 svn_wc__wq_build_file_commit(svn_skel_t **work_item,
365                              svn_wc__db_t *db,
366                              const char *local_abspath,
367                              svn_boolean_t props_mod,
368                              apr_pool_t *result_pool,
369                              apr_pool_t *scratch_pool)
370 {
371   const char *local_relpath;
372   *work_item = svn_skel__make_empty_list(result_pool);
373 
374   SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
375                                 local_abspath, result_pool, scratch_pool));
376 
377   svn_skel__prepend_str(local_relpath, *work_item, result_pool);
378 
379   svn_skel__prepend_str(OP_FILE_COMMIT, *work_item, result_pool);
380 
381   return SVN_NO_ERROR;
382 }
383 
384 /* ------------------------------------------------------------------------ */
385 /* OP_POSTUPGRADE  */
386 
387 static svn_error_t *
run_postupgrade(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)388 run_postupgrade(work_item_baton_t *wqb,
389                 svn_wc__db_t *db,
390                 const svn_skel_t *work_item,
391                 const char *wri_abspath,
392                 svn_cancel_func_t cancel_func,
393                 void *cancel_baton,
394                 apr_pool_t *scratch_pool)
395 {
396   const char *entries_path;
397   const char *format_path;
398   const char *wcroot_abspath;
399   svn_error_t *err;
400 
401   err = svn_wc__wipe_postupgrade(wri_abspath, FALSE,
402                                  cancel_func, cancel_baton, scratch_pool);
403   if (err && err->apr_err == SVN_ERR_ENTRY_NOT_FOUND)
404     /* No entry, this can happen when the wq item is rerun. */
405     svn_error_clear(err);
406   else
407     SVN_ERR(err);
408 
409   SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, db, wri_abspath,
410                                 scratch_pool, scratch_pool));
411 
412   entries_path = svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_ENTRIES,
413                                    scratch_pool);
414   format_path = svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_FORMAT,
415                                    scratch_pool);
416 
417   /* Write the 'format' and 'entries' files.
418 
419      ### The order may matter for some sufficiently old clients.. but
420      ### this code only runs during upgrade after the files had been
421      ### removed earlier during the upgrade. */
422   SVN_ERR(svn_io_write_atomic2(format_path, SVN_WC__NON_ENTRIES_STRING,
423                                sizeof(SVN_WC__NON_ENTRIES_STRING) - 1,
424                                NULL, TRUE, scratch_pool));
425 
426   SVN_ERR(svn_io_write_atomic2(entries_path, SVN_WC__NON_ENTRIES_STRING,
427                                sizeof(SVN_WC__NON_ENTRIES_STRING) - 1,
428                                NULL, TRUE, scratch_pool));
429 
430   return SVN_NO_ERROR;
431 }
432 
433 svn_error_t *
svn_wc__wq_build_postupgrade(svn_skel_t ** work_item,apr_pool_t * result_pool)434 svn_wc__wq_build_postupgrade(svn_skel_t **work_item,
435                              apr_pool_t *result_pool)
436 {
437   *work_item = svn_skel__make_empty_list(result_pool);
438 
439   svn_skel__prepend_str(OP_POSTUPGRADE, *work_item, result_pool);
440 
441   return SVN_NO_ERROR;
442 }
443 
444 /* ------------------------------------------------------------------------ */
445 
446 /* OP_FILE_INSTALL */
447 
448 /* Process the OP_FILE_INSTALL work item WORK_ITEM.
449  * See svn_wc__wq_build_file_install() which generates this work item.
450  * Implements (struct work_item_dispatch).func. */
451 static svn_error_t *
run_file_install(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)452 run_file_install(work_item_baton_t *wqb,
453                  svn_wc__db_t *db,
454                  const svn_skel_t *work_item,
455                  const char *wri_abspath,
456                  svn_cancel_func_t cancel_func,
457                  void *cancel_baton,
458                  apr_pool_t *scratch_pool)
459 {
460   const svn_skel_t *arg1 = work_item->children->next;
461   const svn_skel_t *arg4 = arg1->next->next->next;
462   const char *local_relpath;
463   const char *local_abspath;
464   svn_boolean_t use_commit_times;
465   svn_boolean_t record_fileinfo;
466   svn_boolean_t special;
467   svn_stream_t *src_stream;
468   svn_subst_eol_style_t style;
469   const char *eol;
470   apr_hash_t *keywords;
471   const char *temp_dir_abspath;
472   svn_stream_t *dst_stream;
473   apr_int64_t val;
474   const char *wcroot_abspath;
475   const char *source_abspath;
476   const svn_checksum_t *checksum;
477   apr_hash_t *props;
478   apr_time_t changed_date;
479 
480   local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
481   SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
482                                   local_relpath, scratch_pool, scratch_pool));
483 
484   SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
485   use_commit_times = (val != 0);
486   SVN_ERR(svn_skel__parse_int(&val, arg1->next->next, scratch_pool));
487   record_fileinfo = (val != 0);
488 
489   SVN_ERR(svn_wc__db_read_node_install_info(&wcroot_abspath,
490                                             &checksum, &props,
491                                             &changed_date,
492                                             db, local_abspath, wri_abspath,
493                                             scratch_pool, scratch_pool));
494 
495   if (arg4 != NULL)
496     {
497       /* Use the provided path for the source.  */
498       local_relpath = apr_pstrmemdup(scratch_pool, arg4->data, arg4->len);
499       SVN_ERR(svn_wc__db_from_relpath(&source_abspath, db, wri_abspath,
500                                       local_relpath,
501                                       scratch_pool, scratch_pool));
502     }
503   else if (! checksum)
504     {
505       /* This error replaces a previous assertion. Reporting an error from here
506          leaves the workingqueue operation in place, so the working copy is
507          still broken!
508 
509          But when we report this error the user at least knows what node has
510          this specific problem, so maybe we can find out why users see this
511          error */
512       return svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
513                                _("Can't install '%s' from pristine store, "
514                                  "because no checksum is recorded for this "
515                                  "file"),
516                                svn_dirent_local_style(local_abspath,
517                                                       scratch_pool));
518     }
519   else
520     {
521       SVN_ERR(svn_wc__db_pristine_get_future_path(&source_abspath,
522                                                   wcroot_abspath,
523                                                   checksum,
524                                                   scratch_pool, scratch_pool));
525     }
526 
527   SVN_ERR(svn_stream_open_readonly(&src_stream, source_abspath,
528                                    scratch_pool, scratch_pool));
529 
530   /* Fetch all the translation bits.  */
531   SVN_ERR(svn_wc__get_translate_info(&style, &eol,
532                                      &keywords,
533                                      &special, db, local_abspath,
534                                      props, FALSE,
535                                      scratch_pool, scratch_pool));
536   if (special)
537     {
538       /* When this stream is closed, the resulting special file will
539          atomically be created/moved into place at LOCAL_ABSPATH.  */
540       SVN_ERR(svn_subst_create_specialfile(&dst_stream, local_abspath,
541                                            scratch_pool, scratch_pool));
542 
543       /* Copy the "repository normal" form of the special file into the
544          special stream.  */
545       SVN_ERR(svn_stream_copy3(src_stream, dst_stream,
546                                cancel_func, cancel_baton,
547                                scratch_pool));
548 
549       /* No need to set exec or read-only flags on special files.  */
550 
551       /* ### Shouldn't this record a timestamp and size, etc.? */
552       return SVN_NO_ERROR;
553     }
554 
555   if (svn_subst_translation_required(style, eol, keywords,
556                                      FALSE /* special */,
557                                      TRUE /* force_eol_check */))
558     {
559       /* Wrap it in a translating (expanding) stream.  */
560       src_stream = svn_subst_stream_translated(src_stream, eol,
561                                                TRUE /* repair */,
562                                                keywords,
563                                                TRUE /* expand */,
564                                                scratch_pool);
565     }
566 
567   /* Where is the Right Place to put a temp file in this working copy?  */
568   SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath,
569                                          db, wcroot_abspath,
570                                          scratch_pool, scratch_pool));
571 
572   /* Translate to a temporary file. We don't want the user seeing a partial
573      file, nor let them muck with it while we translate. We may also need to
574      get its TRANSLATED_SIZE before the user can monkey it.  */
575   SVN_ERR(svn_stream__create_for_install(&dst_stream, temp_dir_abspath,
576                                          scratch_pool, scratch_pool));
577 
578   /* Copy from the source to the dest, translating as we go. This will also
579      close both streams.  */
580   SVN_ERR(svn_stream_copy3(src_stream, dst_stream,
581                            cancel_func, cancel_baton,
582                            scratch_pool));
583 
584   /* All done. Move the file into place.  */
585   /* With a single db we might want to install files in a missing directory.
586      Simply trying this scenario on error won't do any harm and at least
587      one user reported this problem on IRC. */
588   SVN_ERR(svn_stream__install_stream(dst_stream, local_abspath,
589                                      TRUE /* make_parents*/, scratch_pool));
590 
591   /* Tweak the on-disk file according to its properties.  */
592 #ifndef WIN32
593   if (props && svn_hash_gets(props, SVN_PROP_EXECUTABLE))
594     SVN_ERR(svn_io_set_file_executable(local_abspath, TRUE, FALSE,
595                                        scratch_pool));
596 #endif
597 
598   /* Note that this explicitly checks the pristine properties, to make sure
599      that when the lock is locally set (=modification) it is not read only */
600   if (props && svn_hash_gets(props, SVN_PROP_NEEDS_LOCK))
601     {
602       svn_wc__db_status_t status;
603       svn_wc__db_lock_t *lock;
604       SVN_ERR(svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL,
605                                    NULL, NULL, NULL, NULL, NULL, NULL, NULL,
606                                    NULL, NULL, &lock, NULL, NULL, NULL, NULL,
607                                    NULL, NULL, NULL, NULL, NULL, NULL,
608                                    db, local_abspath,
609                                    scratch_pool, scratch_pool));
610 
611       if (!lock && status != svn_wc__db_status_added)
612         SVN_ERR(svn_io_set_file_read_only(local_abspath, FALSE, scratch_pool));
613     }
614 
615   if (use_commit_times)
616     {
617       if (changed_date)
618         SVN_ERR(svn_io_set_file_affected_time(changed_date,
619                                               local_abspath,
620                                               scratch_pool));
621     }
622 
623   /* ### this should happen before we rename the file into place.  */
624   if (record_fileinfo)
625     {
626       SVN_ERR(get_and_record_fileinfo(wqb, local_abspath,
627                                       FALSE /* ignore_enoent */,
628                                       scratch_pool));
629     }
630 
631   return SVN_NO_ERROR;
632 }
633 
634 
635 svn_error_t *
svn_wc__wq_build_file_install(svn_skel_t ** work_item,svn_wc__db_t * db,const char * local_abspath,const char * source_abspath,svn_boolean_t use_commit_times,svn_boolean_t record_fileinfo,apr_pool_t * result_pool,apr_pool_t * scratch_pool)636 svn_wc__wq_build_file_install(svn_skel_t **work_item,
637                               svn_wc__db_t *db,
638                               const char *local_abspath,
639                               const char *source_abspath,
640                               svn_boolean_t use_commit_times,
641                               svn_boolean_t record_fileinfo,
642                               apr_pool_t *result_pool,
643                               apr_pool_t *scratch_pool)
644 {
645   const char *local_relpath;
646   const char *wri_abspath;
647   *work_item = svn_skel__make_empty_list(result_pool);
648 
649   /* Use the directory of the file to install as wri_abspath to avoid
650      filestats on just obtaining the wc-root */
651   wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
652 
653   /* If a SOURCE_ABSPATH was provided, then put it into the skel. If this
654      value is not provided, then the file's pristine contents will be used. */
655   if (source_abspath != NULL)
656     {
657       SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
658                                     source_abspath,
659                                     result_pool, scratch_pool));
660 
661       svn_skel__prepend_str(local_relpath, *work_item, result_pool);
662     }
663 
664   SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
665                                 local_abspath, result_pool, scratch_pool));
666 
667   svn_skel__prepend_int(record_fileinfo, *work_item, result_pool);
668   svn_skel__prepend_int(use_commit_times, *work_item, result_pool);
669   svn_skel__prepend_str(local_relpath, *work_item, result_pool);
670   svn_skel__prepend_str(OP_FILE_INSTALL, *work_item, result_pool);
671 
672   return SVN_NO_ERROR;
673 }
674 
675 
676 /* ------------------------------------------------------------------------ */
677 
678 /* OP_FILE_REMOVE  */
679 
680 /* Process the OP_FILE_REMOVE work item WORK_ITEM.
681  * See svn_wc__wq_build_file_remove() which generates this work item.
682  * Implements (struct work_item_dispatch).func. */
683 static svn_error_t *
run_file_remove(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)684 run_file_remove(work_item_baton_t *wqb,
685                 svn_wc__db_t *db,
686                 const svn_skel_t *work_item,
687                 const char *wri_abspath,
688                 svn_cancel_func_t cancel_func,
689                 void *cancel_baton,
690                 apr_pool_t *scratch_pool)
691 {
692   const svn_skel_t *arg1 = work_item->children->next;
693   const char *local_relpath;
694   const char *local_abspath;
695 
696   local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
697   SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
698                                   local_relpath, scratch_pool, scratch_pool));
699 
700   /* Remove the path, no worrying if it isn't there.  */
701   return svn_error_trace(svn_io_remove_file2(local_abspath, TRUE,
702                                              scratch_pool));
703 }
704 
705 
706 svn_error_t *
svn_wc__wq_build_file_remove(svn_skel_t ** work_item,svn_wc__db_t * db,const char * wri_abspath,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)707 svn_wc__wq_build_file_remove(svn_skel_t **work_item,
708                              svn_wc__db_t *db,
709                              const char *wri_abspath,
710                              const char *local_abspath,
711                              apr_pool_t *result_pool,
712                              apr_pool_t *scratch_pool)
713 {
714   const char *local_relpath;
715   *work_item = svn_skel__make_empty_list(result_pool);
716 
717   SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
718                                 local_abspath, result_pool, scratch_pool));
719 
720   svn_skel__prepend_str(local_relpath, *work_item, result_pool);
721   svn_skel__prepend_str(OP_FILE_REMOVE, *work_item, result_pool);
722 
723   return SVN_NO_ERROR;
724 }
725 
726 /* ------------------------------------------------------------------------ */
727 
728 /* OP_DIRECTORY_REMOVE  */
729 
730 /* Process the OP_FILE_REMOVE work item WORK_ITEM.
731  * See svn_wc__wq_build_file_remove() which generates this work item.
732  * Implements (struct work_item_dispatch).func. */
733 static svn_error_t *
run_dir_remove(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)734 run_dir_remove(work_item_baton_t *wqb,
735                svn_wc__db_t *db,
736                const svn_skel_t *work_item,
737                const char *wri_abspath,
738                svn_cancel_func_t cancel_func,
739                void *cancel_baton,
740                apr_pool_t *scratch_pool)
741 {
742   const svn_skel_t *arg1 = work_item->children->next;
743   const char *local_relpath;
744   const char *local_abspath;
745   svn_boolean_t recursive;
746 
747   local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
748   SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
749                                   local_relpath, scratch_pool, scratch_pool));
750 
751   recursive = FALSE;
752   if (arg1->next)
753     {
754       apr_int64_t val;
755       SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
756 
757       recursive = (val != 0);
758     }
759 
760   /* Remove the path, no worrying if it isn't there.  */
761   if (recursive)
762     return svn_error_trace(
763                 svn_io_remove_dir2(local_abspath, TRUE,
764                                    cancel_func, cancel_baton,
765                                    scratch_pool));
766   else
767     {
768       svn_error_t *err;
769 
770       err = svn_io_dir_remove_nonrecursive(local_abspath, scratch_pool);
771 
772       if (err && (APR_STATUS_IS_ENOENT(err->apr_err)
773                   || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)
774                   || APR_STATUS_IS_ENOTEMPTY(err->apr_err)))
775         {
776           svn_error_clear(err);
777           err = NULL;
778         }
779 
780       return svn_error_trace(err);
781     }
782 }
783 
784 svn_error_t *
svn_wc__wq_build_dir_remove(svn_skel_t ** work_item,svn_wc__db_t * db,const char * wri_abspath,const char * local_abspath,svn_boolean_t recursive,apr_pool_t * result_pool,apr_pool_t * scratch_pool)785 svn_wc__wq_build_dir_remove(svn_skel_t **work_item,
786                             svn_wc__db_t *db,
787                             const char *wri_abspath,
788                             const char *local_abspath,
789                             svn_boolean_t recursive,
790                             apr_pool_t *result_pool,
791                             apr_pool_t *scratch_pool)
792 {
793   const char *local_relpath;
794   *work_item = svn_skel__make_empty_list(result_pool);
795 
796   SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
797                                 local_abspath, result_pool, scratch_pool));
798 
799   if (recursive)
800     svn_skel__prepend_int(TRUE, *work_item, result_pool);
801 
802   svn_skel__prepend_str(local_relpath, *work_item, result_pool);
803   svn_skel__prepend_str(OP_DIRECTORY_REMOVE, *work_item, result_pool);
804 
805   return SVN_NO_ERROR;
806 }
807 
808 /* ------------------------------------------------------------------------ */
809 
810 /* OP_FILE_MOVE  */
811 
812 /* Process the OP_FILE_MOVE work item WORK_ITEM.
813  * See svn_wc__wq_build_file_move() which generates this work item.
814  * Implements (struct work_item_dispatch).func. */
815 static svn_error_t *
run_file_move(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)816 run_file_move(work_item_baton_t *wqb,
817               svn_wc__db_t *db,
818               const svn_skel_t *work_item,
819               const char *wri_abspath,
820               svn_cancel_func_t cancel_func,
821               void *cancel_baton,
822               apr_pool_t *scratch_pool)
823 {
824   const svn_skel_t *arg1 = work_item->children->next;
825   const char *src_abspath, *dst_abspath;
826   const char *local_relpath;
827   svn_error_t *err;
828 
829   local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
830   SVN_ERR(svn_wc__db_from_relpath(&src_abspath, db, wri_abspath, local_relpath,
831                                   scratch_pool, scratch_pool));
832   local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->data,
833                                  arg1->next->len);
834   SVN_ERR(svn_wc__db_from_relpath(&dst_abspath, db, wri_abspath, local_relpath,
835                                   scratch_pool, scratch_pool));
836 
837   /* Use svn_io_file_move() instead of svn_io_file_rename() to allow cross
838      device copies. We should not fail in the workqueue. */
839 
840   err = svn_io_file_move(src_abspath, dst_abspath, scratch_pool);
841 
842   /* If the source is not found, we assume the wq op is already handled */
843   if (err && APR_STATUS_IS_ENOENT(err->apr_err))
844     svn_error_clear(err);
845   else
846     SVN_ERR(err);
847 
848   return SVN_NO_ERROR;
849 }
850 
851 
852 svn_error_t *
svn_wc__wq_build_file_move(svn_skel_t ** work_item,svn_wc__db_t * db,const char * wri_abspath,const char * src_abspath,const char * dst_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)853 svn_wc__wq_build_file_move(svn_skel_t **work_item,
854                            svn_wc__db_t *db,
855                            const char *wri_abspath,
856                            const char *src_abspath,
857                            const char *dst_abspath,
858                            apr_pool_t *result_pool,
859                            apr_pool_t *scratch_pool)
860 {
861   svn_node_kind_t kind;
862   const char *local_relpath;
863   *work_item = svn_skel__make_empty_list(result_pool);
864 
865   SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
866   SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
867   SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
868 
869   /* File must exist */
870   SVN_ERR(svn_io_check_path(src_abspath, &kind, result_pool));
871 
872   if (kind == svn_node_none)
873     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
874                              _("'%s' not found"),
875                              svn_dirent_local_style(src_abspath,
876                                                     scratch_pool));
877 
878   SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath, dst_abspath,
879                                 result_pool, scratch_pool));
880   svn_skel__prepend_str(local_relpath, *work_item, result_pool);
881 
882   SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath, src_abspath,
883                                 result_pool, scratch_pool));
884   svn_skel__prepend_str(local_relpath, *work_item, result_pool);
885 
886   svn_skel__prepend_str(OP_FILE_MOVE, *work_item, result_pool);
887 
888   return SVN_NO_ERROR;
889 }
890 
891 /* ------------------------------------------------------------------------ */
892 
893 /* OP_FILE_COPY_TRANSLATED */
894 
895 /* Process the OP_FILE_COPY_TRANSLATED work item WORK_ITEM.
896  * See run_file_copy_translated() which generates this work item.
897  * Implements (struct work_item_dispatch).func. */
898 static svn_error_t *
run_file_copy_translated(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)899 run_file_copy_translated(work_item_baton_t *wqb,
900                          svn_wc__db_t *db,
901                          const svn_skel_t *work_item,
902                          const char *wri_abspath,
903                          svn_cancel_func_t cancel_func,
904                          void *cancel_baton,
905                          apr_pool_t *scratch_pool)
906 {
907   const svn_skel_t *arg1 = work_item->children->next;
908   const char *local_abspath, *src_abspath, *dst_abspath;
909   const char *local_relpath;
910   svn_subst_eol_style_t style;
911   const char *eol;
912   apr_hash_t *keywords;
913   svn_boolean_t special;
914 
915   local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
916   SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
917                                   local_relpath, scratch_pool, scratch_pool));
918 
919   local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->data,
920                                arg1->next->len);
921   SVN_ERR(svn_wc__db_from_relpath(&src_abspath, db, wri_abspath,
922                                   local_relpath, scratch_pool, scratch_pool));
923 
924   local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->next->data,
925                                 arg1->next->next->len);
926   SVN_ERR(svn_wc__db_from_relpath(&dst_abspath, db, wri_abspath,
927                                   local_relpath, scratch_pool, scratch_pool));
928 
929   SVN_ERR(svn_wc__get_translate_info(&style, &eol,
930                                      &keywords,
931                                      &special,
932                                      db, local_abspath, NULL, FALSE,
933                                      scratch_pool, scratch_pool));
934 
935   SVN_ERR(svn_subst_copy_and_translate4(src_abspath, dst_abspath,
936                                         eol, TRUE /* repair */,
937                                         keywords, TRUE /* expand */,
938                                         special,
939                                         cancel_func, cancel_baton,
940                                         scratch_pool));
941   return SVN_NO_ERROR;
942 }
943 
944 
945 svn_error_t *
svn_wc__wq_build_file_copy_translated(svn_skel_t ** work_item,svn_wc__db_t * db,const char * local_abspath,const char * src_abspath,const char * dst_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)946 svn_wc__wq_build_file_copy_translated(svn_skel_t **work_item,
947                                       svn_wc__db_t *db,
948                                       const char *local_abspath,
949                                       const char *src_abspath,
950                                       const char *dst_abspath,
951                                       apr_pool_t *result_pool,
952                                       apr_pool_t *scratch_pool)
953 {
954   svn_node_kind_t kind;
955   const char *local_relpath;
956 
957   *work_item = svn_skel__make_empty_list(result_pool);
958 
959   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
960   SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
961   SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
962 
963   /* File must exist */
964   SVN_ERR(svn_io_check_path(src_abspath, &kind, result_pool));
965 
966   if (kind == svn_node_none)
967     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
968                              _("'%s' not found"),
969                              svn_dirent_local_style(src_abspath,
970                                                     scratch_pool));
971 
972   SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, dst_abspath,
973                                 result_pool, scratch_pool));
974   svn_skel__prepend_str(local_relpath, *work_item, result_pool);
975 
976   SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, src_abspath,
977                                 result_pool, scratch_pool));
978   svn_skel__prepend_str(local_relpath, *work_item, result_pool);
979 
980   SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
981                                 local_abspath, result_pool, scratch_pool));
982   svn_skel__prepend_str(local_relpath, *work_item, result_pool);
983 
984   svn_skel__prepend_str(OP_FILE_COPY_TRANSLATED, *work_item, result_pool);
985 
986   return SVN_NO_ERROR;
987 }
988 
989 /* ------------------------------------------------------------------------ */
990 
991 /* OP_DIRECTORY_INSTALL  */
992 
993 static svn_error_t *
run_dir_install(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)994 run_dir_install(work_item_baton_t *wqb,
995                 svn_wc__db_t *db,
996                 const svn_skel_t *work_item,
997                 const char *wri_abspath,
998                 svn_cancel_func_t cancel_func,
999                 void *cancel_baton,
1000                 apr_pool_t *scratch_pool)
1001 {
1002   const svn_skel_t *arg1 = work_item->children->next;
1003   const char *local_relpath;
1004   const char *local_abspath;
1005 
1006   local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1007   SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1008                                   local_relpath, scratch_pool, scratch_pool));
1009 
1010   SVN_ERR(svn_wc__ensure_directory(local_abspath, scratch_pool));
1011 
1012   return SVN_NO_ERROR;
1013 }
1014 
1015 svn_error_t *
svn_wc__wq_build_dir_install(svn_skel_t ** work_item,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1016 svn_wc__wq_build_dir_install(svn_skel_t **work_item,
1017                              svn_wc__db_t *db,
1018                              const char *local_abspath,
1019                              apr_pool_t *result_pool,
1020                              apr_pool_t *scratch_pool)
1021 {
1022   const char *local_relpath;
1023 
1024   *work_item = svn_skel__make_empty_list(result_pool);
1025 
1026   SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
1027                                 local_abspath, result_pool, scratch_pool));
1028   svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1029 
1030   svn_skel__prepend_str(OP_DIRECTORY_INSTALL, *work_item, result_pool);
1031 
1032   return SVN_NO_ERROR;
1033 }
1034 
1035 
1036 /* ------------------------------------------------------------------------ */
1037 
1038 /* OP_SYNC_FILE_FLAGS  */
1039 
1040 /* Process the OP_SYNC_FILE_FLAGS work item WORK_ITEM.
1041  * See svn_wc__wq_build_sync_file_flags() which generates this work item.
1042  * Implements (struct work_item_dispatch).func. */
1043 static svn_error_t *
run_sync_file_flags(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)1044 run_sync_file_flags(work_item_baton_t *wqb,
1045                     svn_wc__db_t *db,
1046                     const svn_skel_t *work_item,
1047                     const char *wri_abspath,
1048                     svn_cancel_func_t cancel_func,
1049                     void *cancel_baton,
1050                     apr_pool_t *scratch_pool)
1051 {
1052   const svn_skel_t *arg1 = work_item->children->next;
1053   const char *local_relpath;
1054   const char *local_abspath;
1055 
1056   local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1057   SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1058                                   local_relpath, scratch_pool, scratch_pool));
1059 
1060   return svn_error_trace(svn_wc__sync_flags_with_props(NULL, db,
1061                                             local_abspath, scratch_pool));
1062 }
1063 
1064 
1065 svn_error_t *
svn_wc__wq_build_sync_file_flags(svn_skel_t ** work_item,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1066 svn_wc__wq_build_sync_file_flags(svn_skel_t **work_item,
1067                                  svn_wc__db_t *db,
1068                                  const char *local_abspath,
1069                                  apr_pool_t *result_pool,
1070                                  apr_pool_t *scratch_pool)
1071 {
1072   const char *local_relpath;
1073   *work_item = svn_skel__make_empty_list(result_pool);
1074 
1075   SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
1076                                 local_abspath, result_pool, scratch_pool));
1077 
1078   svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1079   svn_skel__prepend_str(OP_SYNC_FILE_FLAGS, *work_item, result_pool);
1080 
1081   return SVN_NO_ERROR;
1082 }
1083 
1084 
1085 /* ------------------------------------------------------------------------ */
1086 
1087 /* OP_PREJ_INSTALL  */
1088 
1089 static svn_error_t *
run_prej_install(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)1090 run_prej_install(work_item_baton_t *wqb,
1091                  svn_wc__db_t *db,
1092                  const svn_skel_t *work_item,
1093                  const char *wri_abspath,
1094                  svn_cancel_func_t cancel_func,
1095                  void *cancel_baton,
1096                  apr_pool_t *scratch_pool)
1097 {
1098   const svn_skel_t *arg1 = work_item->children->next;
1099   const char *local_relpath;
1100   const char *local_abspath;
1101   svn_skel_t *conflicts;
1102   const svn_skel_t *prop_conflict_skel;
1103   const char *tmp_prejfile_abspath;
1104   const char *prejfile_abspath;
1105 
1106   local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1107   SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1108                                   local_relpath, scratch_pool, scratch_pool));
1109 
1110   SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL, db, local_abspath,
1111                                    scratch_pool, scratch_pool));
1112 
1113   SVN_ERR(svn_wc__conflict_read_prop_conflict(&prejfile_abspath,
1114                                               NULL, NULL, NULL, NULL,
1115                                               db, local_abspath, conflicts,
1116                                               scratch_pool, scratch_pool));
1117 
1118   if (arg1->next != NULL)
1119     prop_conflict_skel = arg1->next; /* Before Subversion 1.9 */
1120   else
1121     prop_conflict_skel = NULL; /* Read from DB */
1122 
1123   /* Construct a property reject file in the temporary area.  */
1124   SVN_ERR(svn_wc__create_prejfile(&tmp_prejfile_abspath,
1125                                   db, local_abspath,
1126                                   prop_conflict_skel,
1127                                   cancel_func, cancel_baton,
1128                                   scratch_pool, scratch_pool));
1129 
1130   /* ... and atomically move it into place.  */
1131   SVN_ERR(svn_io_file_rename2(tmp_prejfile_abspath,
1132                               prejfile_abspath, FALSE,
1133                               scratch_pool));
1134 
1135   return SVN_NO_ERROR;
1136 }
1137 
1138 
1139 svn_error_t *
svn_wc__wq_build_prej_install(svn_skel_t ** work_item,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1140 svn_wc__wq_build_prej_install(svn_skel_t **work_item,
1141                               svn_wc__db_t *db,
1142                               const char *local_abspath,
1143                               /*svn_skel_t *conflict_skel,*/
1144                               apr_pool_t *result_pool,
1145                               apr_pool_t *scratch_pool)
1146 {
1147   const char *local_relpath;
1148   *work_item = svn_skel__make_empty_list(result_pool);
1149 
1150   SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
1151                                 local_abspath, result_pool, scratch_pool));
1152 
1153   /* ### In Subversion 1.7 and 1.8 we created a legacy property conflict skel
1154          here:
1155     if (conflict_skel != NULL)
1156       svn_skel__prepend(conflict_skel, *work_item);
1157    */
1158   svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1159   svn_skel__prepend_str(OP_PREJ_INSTALL, *work_item, result_pool);
1160 
1161   return SVN_NO_ERROR;
1162 }
1163 
1164 
1165 /* ------------------------------------------------------------------------ */
1166 
1167 /* OP_RECORD_FILEINFO  */
1168 
1169 
1170 static svn_error_t *
run_record_fileinfo(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)1171 run_record_fileinfo(work_item_baton_t *wqb,
1172                     svn_wc__db_t *db,
1173                     const svn_skel_t *work_item,
1174                     const char *wri_abspath,
1175                     svn_cancel_func_t cancel_func,
1176                     void *cancel_baton,
1177                     apr_pool_t *scratch_pool)
1178 {
1179   const svn_skel_t *arg1 = work_item->children->next;
1180   const char *local_relpath;
1181   const char *local_abspath;
1182   apr_time_t set_time = 0;
1183 
1184   local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1185 
1186   SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1187                                   local_relpath, scratch_pool, scratch_pool));
1188 
1189   if (arg1->next)
1190     {
1191       apr_int64_t val;
1192 
1193       SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
1194       set_time = (apr_time_t)val;
1195     }
1196 
1197   if (set_time != 0)
1198     {
1199       svn_node_kind_t kind;
1200       svn_boolean_t is_special;
1201 
1202       /* Do not set the timestamp on special files. */
1203       SVN_ERR(svn_io_check_special_path(local_abspath, &kind, &is_special,
1204                                         scratch_pool));
1205 
1206       /* Don't set affected time when local_abspath does not exist or is
1207          a special file */
1208       if (kind == svn_node_file && !is_special)
1209         SVN_ERR(svn_io_set_file_affected_time(set_time, local_abspath,
1210                                               scratch_pool));
1211 
1212       /* Note that we can't use the value we get here for recording as the
1213          filesystem might have a different timestamp granularity */
1214     }
1215 
1216 
1217   return svn_error_trace(get_and_record_fileinfo(wqb, local_abspath,
1218                                                  TRUE /* ignore_enoent */,
1219                                                  scratch_pool));
1220 }
1221 
1222 /* ------------------------------------------------------------------------ */
1223 
1224 /* OP_TMP_SET_TEXT_CONFLICT_MARKERS  */
1225 
1226 
1227 static svn_error_t *
run_set_text_conflict_markers(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)1228 run_set_text_conflict_markers(work_item_baton_t *wqb,
1229                               svn_wc__db_t *db,
1230                               const svn_skel_t *work_item,
1231                               const char *wri_abspath,
1232                               svn_cancel_func_t cancel_func,
1233                               void *cancel_baton,
1234                               apr_pool_t *scratch_pool)
1235 {
1236   const svn_skel_t *arg = work_item->children->next;
1237   const char *local_relpath;
1238   const char *local_abspath;
1239   const char *old_abspath = NULL;
1240   const char *new_abspath = NULL;
1241   const char *wrk_abspath = NULL;
1242 
1243   local_relpath = apr_pstrmemdup(scratch_pool, arg->data, arg->len);
1244   SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1245                                   local_relpath, scratch_pool, scratch_pool));
1246 
1247   arg = arg->next;
1248   local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1249                            : NULL;
1250 
1251   if (local_relpath)
1252     {
1253       SVN_ERR(svn_wc__db_from_relpath(&old_abspath, db, wri_abspath,
1254                                       local_relpath,
1255                                       scratch_pool, scratch_pool));
1256     }
1257 
1258   arg = arg->next;
1259   local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1260                            : NULL;
1261   if (local_relpath)
1262     {
1263       SVN_ERR(svn_wc__db_from_relpath(&new_abspath, db, wri_abspath,
1264                                       local_relpath,
1265                                       scratch_pool, scratch_pool));
1266     }
1267 
1268   arg = arg->next;
1269   local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1270                            : NULL;
1271 
1272   if (local_relpath)
1273     {
1274       SVN_ERR(svn_wc__db_from_relpath(&wrk_abspath, db, wri_abspath,
1275                                       local_relpath,
1276                                       scratch_pool, scratch_pool));
1277     }
1278 
1279   /* Upgrade scenario: We have a workqueue item that describes how to install a
1280      non skel conflict. Fetch all the information we can to create a new style
1281      conflict. */
1282   /* ### Before format 30 this is/was a common code path as we didn't install
1283      ### the conflict directly in the db. It just calls the wc_db code
1284      ### to set the right fields. */
1285 
1286   {
1287     /* Check if we should combine with a property conflict... */
1288     svn_skel_t *conflicts;
1289 
1290     SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL, db, local_abspath,
1291                                      scratch_pool, scratch_pool));
1292 
1293     if (! conflicts)
1294       {
1295         /* No conflict exists, create a basic skel */
1296         conflicts = svn_wc__conflict_skel_create(scratch_pool);
1297 
1298         SVN_ERR(svn_wc__conflict_skel_set_op_update(conflicts, NULL, NULL,
1299                                                     scratch_pool,
1300                                                     scratch_pool));
1301       }
1302 
1303     /* Add the text conflict to the existing onflict */
1304     SVN_ERR(svn_wc__conflict_skel_add_text_conflict(conflicts, db,
1305                                                     local_abspath,
1306                                                     wrk_abspath,
1307                                                     old_abspath,
1308                                                     new_abspath,
1309                                                     scratch_pool,
1310                                                     scratch_pool));
1311 
1312     SVN_ERR(svn_wc__db_op_mark_conflict(db, local_abspath, conflicts,
1313                                         NULL, scratch_pool));
1314   }
1315   return SVN_NO_ERROR;
1316 }
1317 
1318 /* ------------------------------------------------------------------------ */
1319 
1320 /* OP_TMP_SET_PROPERTY_CONFLICT_MARKER  */
1321 
1322 static svn_error_t *
run_set_property_conflict_marker(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)1323 run_set_property_conflict_marker(work_item_baton_t *wqb,
1324                                  svn_wc__db_t *db,
1325                                  const svn_skel_t *work_item,
1326                                  const char *wri_abspath,
1327                                  svn_cancel_func_t cancel_func,
1328                                  void *cancel_baton,
1329                                  apr_pool_t *scratch_pool)
1330 {
1331   const svn_skel_t *arg = work_item->children->next;
1332   const char *local_relpath;
1333   const char *local_abspath;
1334   const char *prej_abspath = NULL;
1335 
1336   local_relpath = apr_pstrmemdup(scratch_pool, arg->data, arg->len);
1337 
1338   SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1339                                   local_relpath, scratch_pool, scratch_pool));
1340 
1341 
1342   arg = arg->next;
1343   local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1344                            : NULL;
1345 
1346   if (local_relpath)
1347     SVN_ERR(svn_wc__db_from_relpath(&prej_abspath, db, wri_abspath,
1348                                     local_relpath,
1349                                     scratch_pool, scratch_pool));
1350 
1351   {
1352     /* Check if we should combine with a text conflict... */
1353     svn_skel_t *conflicts;
1354     apr_hash_t *prop_names;
1355 
1356     SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL,
1357                                      db, local_abspath,
1358                                      scratch_pool, scratch_pool));
1359 
1360     if (! conflicts)
1361       {
1362         /* No conflict exists, create a basic skel */
1363         conflicts = svn_wc__conflict_skel_create(scratch_pool);
1364 
1365         SVN_ERR(svn_wc__conflict_skel_set_op_update(conflicts, NULL, NULL,
1366                                                     scratch_pool,
1367                                                     scratch_pool));
1368       }
1369 
1370     prop_names = apr_hash_make(scratch_pool);
1371     SVN_ERR(svn_wc__conflict_skel_add_prop_conflict(conflicts, db,
1372                                                     local_abspath,
1373                                                     prej_abspath,
1374                                                     NULL, NULL, NULL,
1375                                                     prop_names,
1376                                                     scratch_pool,
1377                                                     scratch_pool));
1378 
1379     SVN_ERR(svn_wc__db_op_mark_conflict(db, local_abspath, conflicts,
1380                                         NULL, scratch_pool));
1381   }
1382   return SVN_NO_ERROR;
1383 }
1384 
1385 /* ------------------------------------------------------------------------ */
1386 
1387 static const struct work_item_dispatch dispatch_table[] = {
1388   { OP_FILE_COMMIT, run_file_commit },
1389   { OP_FILE_INSTALL, run_file_install },
1390   { OP_FILE_REMOVE, run_file_remove },
1391   { OP_FILE_MOVE, run_file_move },
1392   { OP_FILE_COPY_TRANSLATED, run_file_copy_translated },
1393   { OP_SYNC_FILE_FLAGS, run_sync_file_flags },
1394   { OP_PREJ_INSTALL, run_prej_install },
1395   { OP_DIRECTORY_REMOVE, run_dir_remove },
1396   { OP_DIRECTORY_INSTALL, run_dir_install },
1397 
1398   /* Upgrade steps */
1399   { OP_POSTUPGRADE, run_postupgrade },
1400 
1401   /* Legacy workqueue items. No longer created */
1402   { OP_BASE_REMOVE, run_base_remove },
1403   { OP_RECORD_FILEINFO, run_record_fileinfo },
1404   { OP_TMP_SET_TEXT_CONFLICT_MARKERS, run_set_text_conflict_markers },
1405   { OP_TMP_SET_PROPERTY_CONFLICT_MARKER, run_set_property_conflict_marker },
1406 
1407   /* Sentinel.  */
1408   { NULL }
1409 };
1410 
1411 struct work_item_baton_t
1412 {
1413   apr_pool_t *result_pool; /* Pool to allocate result in */
1414 
1415   svn_boolean_t used; /* needs reset */
1416 
1417   apr_hash_t *record_map; /* const char * -> svn_io_dirent2_t map */
1418 };
1419 
1420 
1421 static svn_error_t *
dispatch_work_item(work_item_baton_t * wqb,svn_wc__db_t * db,const char * wri_abspath,const svn_skel_t * work_item,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)1422 dispatch_work_item(work_item_baton_t *wqb,
1423                    svn_wc__db_t *db,
1424                    const char *wri_abspath,
1425                    const svn_skel_t *work_item,
1426                    svn_cancel_func_t cancel_func,
1427                    void *cancel_baton,
1428                    apr_pool_t *scratch_pool)
1429 {
1430   const struct work_item_dispatch *scan;
1431 
1432   /* Scan the dispatch table for a function to handle this work item.  */
1433   for (scan = &dispatch_table[0]; scan->name != NULL; ++scan)
1434     {
1435       if (svn_skel__matches_atom(work_item->children, scan->name))
1436         {
1437 
1438 #ifdef SVN_DEBUG_WORK_QUEUE
1439           SVN_DBG(("dispatch: operation='%s'\n", scan->name));
1440 #endif
1441           SVN_ERR((*scan->func)(wqb, db, work_item, wri_abspath,
1442                                 cancel_func, cancel_baton,
1443                                 scratch_pool));
1444 
1445 #ifdef SVN_RUN_WORK_QUEUE_TWICE
1446 #ifdef SVN_DEBUG_WORK_QUEUE
1447           SVN_DBG(("dispatch: operation='%s'\n", scan->name));
1448 #endif
1449           /* Being able to run every workqueue item twice is one
1450              requirement for workqueues to be restartable. */
1451           SVN_ERR((*scan->func)(db, work_item, wri_abspath,
1452                                 cancel_func, cancel_baton,
1453                                 scratch_pool));
1454 #endif
1455 
1456           break;
1457         }
1458     }
1459 
1460   if (scan->name == NULL)
1461     {
1462       /* We should know about ALL possible work items here. If we do not,
1463          then something is wrong. Most likely, some kind of format/code
1464          skew. There is nothing more we can do. Erasing or ignoring this
1465          work item could leave the WC in an even more broken state.
1466 
1467          Contrary to issue #1581, we cannot simply remove work items and
1468          continue, so bail out with an error.  */
1469       return svn_error_createf(SVN_ERR_WC_BAD_ADM_LOG, NULL,
1470                                _("Unrecognized work item in the queue"));
1471     }
1472 
1473   return SVN_NO_ERROR;
1474 }
1475 
1476 
1477 svn_error_t *
svn_wc__wq_run(svn_wc__db_t * db,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)1478 svn_wc__wq_run(svn_wc__db_t *db,
1479                const char *wri_abspath,
1480                svn_cancel_func_t cancel_func,
1481                void *cancel_baton,
1482                apr_pool_t *scratch_pool)
1483 {
1484   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1485   apr_uint64_t last_id = 0;
1486   work_item_baton_t wib = { 0 };
1487   wib.result_pool = svn_pool_create(scratch_pool);
1488 
1489 #ifdef SVN_DEBUG_WORK_QUEUE
1490   SVN_DBG(("wq_run: wri='%s'\n", wri_abspath));
1491   {
1492     static int count = 0;
1493     const char *count_env_var = getenv("SVN_DEBUG_WORK_QUEUE");
1494     int count_env_val;
1495 
1496     SVN_ERR(svn_cstring_atoi(&count_env_val, count_env_var));
1497 
1498     if (count_env_var && ++count == count_env_val)
1499       return svn_error_create(SVN_ERR_CANCELLED, NULL, "fake cancel");
1500   }
1501 #endif
1502 
1503   while (TRUE)
1504     {
1505       apr_uint64_t id;
1506       svn_skel_t *work_item;
1507       svn_error_t *err;
1508 
1509       svn_pool_clear(iterpool);
1510 
1511       if (! wib.used)
1512         {
1513           /* Make sure to do this *early* in the loop iteration. There may
1514              be a LAST_ID that needs to be marked as completed, *before* we
1515              start worrying about anything else.  */
1516           SVN_ERR(svn_wc__db_wq_fetch_next(&id, &work_item, db, wri_abspath,
1517                                            last_id, iterpool, iterpool));
1518         }
1519       else
1520         {
1521           /* Make sure to do this *early* in the loop iteration. There may
1522              be a LAST_ID that needs to be marked as completed, *before* we
1523              start worrying about anything else.  */
1524           SVN_ERR(svn_wc__db_wq_record_and_fetch_next(&id, &work_item,
1525                                                       db, wri_abspath,
1526                                                       last_id, wib.record_map,
1527                                                       iterpool,
1528                                                       wib.result_pool));
1529 
1530           svn_pool_clear(wib.result_pool);
1531           wib.record_map = NULL;
1532           wib.used = FALSE;
1533         }
1534 
1535       /* Stop work queue processing, if requested. A future 'svn cleanup'
1536          should be able to continue the processing. Note that we may
1537          have WORK_ITEM, but we'll just skip its processing for now.  */
1538       if (cancel_func)
1539         SVN_ERR(cancel_func(cancel_baton));
1540 
1541       /* If we have a WORK_ITEM, then process the sucker. Otherwise,
1542          we're done.  */
1543       if (work_item == NULL)
1544         break;
1545 
1546       err = dispatch_work_item(&wib, db, wri_abspath, work_item,
1547                                cancel_func, cancel_baton, iterpool);
1548       if (err)
1549         {
1550           const char *skel = svn_skel__unparse(work_item, scratch_pool)->data;
1551 
1552           return svn_error_createf(SVN_ERR_WC_BAD_ADM_LOG, err,
1553                                    _("Failed to run the WC DB work queue "
1554                                      "associated with '%s', work item %d %s"),
1555                                    svn_dirent_local_style(wri_abspath,
1556                                                           scratch_pool),
1557                                    (int)id, skel);
1558         }
1559 
1560       /* The work item finished without error. Mark it completed
1561          in the next loop.  */
1562       last_id = id;
1563     }
1564 
1565   svn_pool_destroy(iterpool);
1566   return SVN_NO_ERROR;
1567 }
1568 
1569 
1570 svn_skel_t *
svn_wc__wq_merge(svn_skel_t * work_item1,svn_skel_t * work_item2,apr_pool_t * result_pool)1571 svn_wc__wq_merge(svn_skel_t *work_item1,
1572                  svn_skel_t *work_item2,
1573                  apr_pool_t *result_pool)
1574 {
1575   /* If either argument is NULL, then just return the other.  */
1576   if (work_item1 == NULL)
1577     return work_item2;
1578   if (work_item2 == NULL)
1579     return work_item1;
1580 
1581   /* We have two items. Figure out how to join them.  */
1582   if (SVN_WC__SINGLE_WORK_ITEM(work_item1))
1583     {
1584       if (SVN_WC__SINGLE_WORK_ITEM(work_item2))
1585         {
1586           /* Both are singular work items. Construct a list, then put
1587              both work items into it (in the proper order).  */
1588 
1589           svn_skel_t *result = svn_skel__make_empty_list(result_pool);
1590 
1591           svn_skel__prepend(work_item2, result);
1592           svn_skel__prepend(work_item1, result);
1593           return result;
1594         }
1595 
1596       /* WORK_ITEM2 is a list of work items. We can simply shove WORK_ITEM1
1597          in the front to keep the ordering.  */
1598       svn_skel__prepend(work_item1, work_item2);
1599       return work_item2;
1600     }
1601   /* WORK_ITEM1 is a list of work items.  */
1602 
1603   if (SVN_WC__SINGLE_WORK_ITEM(work_item2))
1604     {
1605       /* Put WORK_ITEM2 onto the end of the WORK_ITEM1 list.  */
1606       svn_skel__append(work_item1, work_item2);
1607       return work_item1;
1608     }
1609 
1610   /* We have two lists of work items. We need to chain all of the work
1611      items into one big list. We will leave behind the WORK_ITEM2 skel,
1612      as we only want its children.  */
1613   svn_skel__append(work_item1, work_item2->children);
1614   return work_item1;
1615 }
1616 
1617 
1618 static svn_error_t *
get_and_record_fileinfo(work_item_baton_t * wqb,const char * local_abspath,svn_boolean_t ignore_enoent,apr_pool_t * scratch_pool)1619 get_and_record_fileinfo(work_item_baton_t *wqb,
1620                         const char *local_abspath,
1621                         svn_boolean_t ignore_enoent,
1622                         apr_pool_t *scratch_pool)
1623 {
1624   const svn_io_dirent2_t *dirent;
1625 
1626   SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, ignore_enoent,
1627                               wqb->result_pool, scratch_pool));
1628 
1629   if (dirent->kind != svn_node_file)
1630     return SVN_NO_ERROR;
1631 
1632   wqb->used = TRUE;
1633 
1634   if (! wqb->record_map)
1635     wqb->record_map = apr_hash_make(wqb->result_pool);
1636 
1637   svn_hash_sets(wqb->record_map, apr_pstrdup(wqb->result_pool, local_abspath),
1638                 dirent);
1639 
1640   return SVN_NO_ERROR;
1641 }
1642