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
67typedef struct work_item_baton_t work_item_baton_t;
68
69struct 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 */
81static svn_error_t *
82get_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. */
98static svn_error_t *
99run_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 */
183static svn_error_t *
184install_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
277static svn_error_t *
278process_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
338static svn_error_t *
339run_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
363svn_error_t *
364svn_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
387static svn_error_t *
388run_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
433svn_error_t *
434svn_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. */
451static svn_error_t *
452run_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
635svn_error_t *
636svn_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. */
683static svn_error_t *
684run_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
706svn_error_t *
707svn_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. */
733static svn_error_t *
734run_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
784svn_error_t *
785svn_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. */
815static svn_error_t *
816run_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
852svn_error_t *
853svn_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. */
898static svn_error_t *
899run_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
945svn_error_t *
946svn_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
993static svn_error_t *
994run_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
1015svn_error_t *
1016svn_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. */
1043static svn_error_t *
1044run_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
1065svn_error_t *
1066svn_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
1089static svn_error_t *
1090run_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
1139svn_error_t *
1140svn_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
1170static svn_error_t *
1171run_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
1227static svn_error_t *
1228run_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
1322static svn_error_t *
1323run_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
1387static 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
1411struct 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
1421static svn_error_t *
1422dispatch_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
1477svn_error_t *
1478svn_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
1570svn_skel_t *
1571svn_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
1618static svn_error_t *
1619get_and_record_fileinfo(work_item_baton_t *wqb,
1620                        const char *local_abspath,
1621                        svn_boolean_t ignore_enoent,
1622                        apr_pool_t *scratch_pool)
1623{
1624  const svn_io_dirent2_t *dirent;
1625
1626  SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, ignore_enoent,
1627                              wqb->result_pool, scratch_pool));
1628
1629  if (dirent->kind != svn_node_file)
1630    return SVN_NO_ERROR;
1631
1632  wqb->used = TRUE;
1633
1634  if (! wqb->record_map)
1635    wqb->record_map = apr_hash_make(wqb->result_pool);
1636
1637  svn_hash_sets(wqb->record_map, apr_pstrdup(wqb->result_pool, local_abspath),
1638                dirent);
1639
1640  return SVN_NO_ERROR;
1641}
1642