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