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