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