]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/subversion/subversion/libsvn_wc/workqueue.c
Upgrade Unbound to 1.6.4. More to follow.
[FreeBSD/FreeBSD.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_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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 }