1251881Speter/*
2251881Speter * workqueue.c :  manipulating work queue items
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter#include <apr_pools.h>
25251881Speter
26299742Sdim#include "svn_private_config.h"
27251881Speter#include "svn_types.h"
28251881Speter#include "svn_pools.h"
29251881Speter#include "svn_dirent_uri.h"
30251881Speter#include "svn_subst.h"
31251881Speter#include "svn_hash.h"
32251881Speter#include "svn_io.h"
33251881Speter
34251881Speter#include "wc.h"
35251881Speter#include "wc_db.h"
36251881Speter#include "workqueue.h"
37251881Speter#include "adm_files.h"
38251881Speter#include "conflicts.h"
39251881Speter#include "translate.h"
40251881Speter
41299742Sdim#include "private/svn_io_private.h"
42251881Speter#include "private/svn_skel.h"
43251881Speter
44251881Speter
45251881Speter/* Workqueue operation names.  */
46251881Speter#define OP_FILE_COMMIT "file-commit"
47251881Speter#define OP_FILE_INSTALL "file-install"
48251881Speter#define OP_FILE_REMOVE "file-remove"
49251881Speter#define OP_FILE_MOVE "file-move"
50251881Speter#define OP_FILE_COPY_TRANSLATED "file-translate"
51251881Speter#define OP_SYNC_FILE_FLAGS "sync-file-flags"
52251881Speter#define OP_PREJ_INSTALL "prej-install"
53251881Speter#define OP_DIRECTORY_REMOVE "dir-remove"
54251881Speter#define OP_DIRECTORY_INSTALL "dir-install"
55251881Speter
56251881Speter#define OP_POSTUPGRADE "postupgrade"
57251881Speter
58251881Speter/* Legacy items */
59251881Speter#define OP_BASE_REMOVE "base-remove"
60251881Speter#define OP_RECORD_FILEINFO "record-fileinfo"
61251881Speter#define OP_TMP_SET_TEXT_CONFLICT_MARKERS "tmp-set-text-conflict-markers"
62251881Speter#define OP_TMP_SET_PROPERTY_CONFLICT_MARKER "tmp-set-property-conflict-marker"
63251881Speter
64251881Speter/* For work queue debugging. Generates output about its operation.  */
65251881Speter/* #define SVN_DEBUG_WORK_QUEUE */
66251881Speter
67251881Spetertypedef struct work_item_baton_t work_item_baton_t;
68251881Speter
69251881Speterstruct work_item_dispatch {
70251881Speter  const char *name;
71251881Speter  svn_error_t *(*func)(work_item_baton_t *wqb,
72251881Speter                       svn_wc__db_t *db,
73251881Speter                       const svn_skel_t *work_item,
74251881Speter                       const char *wri_abspath,
75251881Speter                       svn_cancel_func_t cancel_func,
76251881Speter                       void *cancel_baton,
77251881Speter                       apr_pool_t *scratch_pool);
78251881Speter};
79251881Speter
80251881Speter/* Forward definition */
81251881Speterstatic svn_error_t *
82251881Speterget_and_record_fileinfo(work_item_baton_t *wqb,
83251881Speter                        const char *local_abspath,
84251881Speter                        svn_boolean_t ignore_enoent,
85251881Speter                        apr_pool_t *scratch_pool);
86251881Speter
87251881Speter/* ------------------------------------------------------------------------ */
88251881Speter/* OP_REMOVE_BASE  */
89251881Speter
90251881Speter/* Removes a BASE_NODE and all it's data, leaving any adds and copies as is.
91251881Speter   Do this as a depth first traversal to make sure than any parent still exists
92251881Speter   on error conditions.
93251881Speter */
94251881Speter
95251881Speter/* Process the OP_REMOVE_BASE work item WORK_ITEM.
96251881Speter * See svn_wc__wq_build_remove_base() which generates this work item.
97251881Speter * Implements (struct work_item_dispatch).func. */
98251881Speterstatic svn_error_t *
99251881Speterrun_base_remove(work_item_baton_t *wqb,
100251881Speter                svn_wc__db_t *db,
101251881Speter                const svn_skel_t *work_item,
102251881Speter                const char *wri_abspath,
103251881Speter                svn_cancel_func_t cancel_func,
104251881Speter                void *cancel_baton,
105251881Speter                apr_pool_t *scratch_pool)
106251881Speter{
107251881Speter  const svn_skel_t *arg1 = work_item->children->next;
108251881Speter  const char *local_relpath;
109251881Speter  const char *local_abspath;
110251881Speter  svn_revnum_t not_present_rev = SVN_INVALID_REVNUM;
111251881Speter  apr_int64_t val;
112251881Speter
113251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
114251881Speter  SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
115251881Speter                                  local_relpath, scratch_pool, scratch_pool));
116251881Speter  SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
117251881Speter
118251881Speter  if (arg1->next->next)
119251881Speter    {
120251881Speter      not_present_rev = (svn_revnum_t)val;
121251881Speter
122251881Speter      SVN_ERR(svn_skel__parse_int(&val, arg1->next->next, scratch_pool));
123251881Speter    }
124251881Speter  else
125251881Speter    {
126251881Speter      svn_boolean_t keep_not_present;
127251881Speter
128251881Speter      SVN_ERR_ASSERT(SVN_WC__VERSION <= 28); /* Case unused in later versions*/
129251881Speter
130251881Speter      keep_not_present = (val != 0);
131251881Speter
132251881Speter      if (keep_not_present)
133251881Speter        {
134251881Speter          SVN_ERR(svn_wc__db_base_get_info(NULL, NULL,
135251881Speter                                           &not_present_rev, NULL,
136251881Speter                                           NULL, NULL, NULL,
137251881Speter                                           NULL, NULL, NULL, NULL, NULL, NULL,
138251881Speter                                           NULL, NULL, NULL,
139251881Speter                                           db, local_abspath,
140251881Speter                                           scratch_pool, scratch_pool));
141251881Speter        }
142251881Speter    }
143251881Speter
144251881Speter  SVN_ERR(svn_wc__db_base_remove(db, local_abspath,
145251881Speter                                 FALSE /* keep_as_working */,
146299742Sdim                                 SVN_IS_VALID_REVNUM(not_present_rev), FALSE,
147251881Speter                                 not_present_rev,
148251881Speter                                 NULL, NULL, scratch_pool));
149251881Speter
150251881Speter  return SVN_NO_ERROR;
151251881Speter}
152251881Speter
153251881Speter/* ------------------------------------------------------------------------ */
154251881Speter
155251881Speter/* ------------------------------------------------------------------------ */
156251881Speter
157251881Speter/* OP_FILE_COMMIT  */
158251881Speter
159251881Speter
160251881Speter/* FILE_ABSPATH is the new text base of the newly-committed versioned file,
161251881Speter * in repository-normal form (aka "detranslated" form).  Adjust the working
162251881Speter * file accordingly.
163251881Speter *
164251881Speter * If eol and/or keyword translation would cause the working file to
165251881Speter * change, then overwrite the working file with a translated copy of
166251881Speter * the new text base (but only if the translated copy differs from the
167251881Speter * current working file -- if they are the same, do nothing, to avoid
168251881Speter * clobbering timestamps unnecessarily).
169251881Speter *
170251881Speter * Set the working file's executability according to its svn:executable
171251881Speter * property.
172251881Speter *
173251881Speter * Set the working file's read-only attribute according to its properties
174251881Speter * and lock status (see svn_wc__maybe_set_read_only()).
175251881Speter *
176251881Speter * If the working file was re-translated or had its executability or
177251881Speter * read-only state changed,
178251881Speter * then set OVERWROTE_WORKING to TRUE.  If the working file isn't
179251881Speter * touched at all, then set to FALSE.
180251881Speter *
181251881Speter * Use SCRATCH_POOL for any temporary allocation.
182251881Speter */
183251881Speterstatic svn_error_t *
184251881Speterinstall_committed_file(svn_boolean_t *overwrote_working,
185251881Speter                       svn_wc__db_t *db,
186251881Speter                       const char *file_abspath,
187251881Speter                       svn_cancel_func_t cancel_func,
188251881Speter                       void *cancel_baton,
189251881Speter                       apr_pool_t *scratch_pool)
190251881Speter{
191251881Speter  svn_boolean_t same;
192251881Speter  const char *tmp_wfile;
193251881Speter  svn_boolean_t special;
194251881Speter
195251881Speter  /* start off assuming that the working file isn't touched. */
196251881Speter  *overwrote_working = FALSE;
197251881Speter
198251881Speter  /* In the commit, newlines and keywords may have been
199251881Speter   * canonicalized and/or contracted... Or they may not have
200251881Speter   * been.  It's kind of hard to know.  Here's how we find out:
201251881Speter   *
202251881Speter   *    1. Make a translated tmp copy of the committed text base,
203251881Speter   *       translated according to the versioned file's properties.
204251881Speter   *       Or, if no committed text base exists (the commit must have
205251881Speter   *       been a propchange only), make a translated tmp copy of the
206251881Speter   *       working file.
207251881Speter   *    2. Compare the translated tmpfile to the working file.
208251881Speter   *    3. If different, copy the tmpfile over working file.
209251881Speter   *
210251881Speter   * This means we only rewrite the working file if we absolutely
211251881Speter   * have to, which is good because it avoids changing the file's
212251881Speter   * timestamp unless necessary, so editors aren't tempted to
213251881Speter   * reread the file if they don't really need to.
214251881Speter   */
215251881Speter
216251881Speter  /* Copy and translate the new base-to-be file (if found, else the working
217251881Speter   * file) from repository-normal form to working form, writing a new
218251881Speter   * temporary file if any translation was actually done.  Set TMP_WFILE to
219251881Speter   * the translated file's path, which may be the source file's path if no
220251881Speter   * translation was done.  Set SAME to indicate whether the new working
221251881Speter   * text is the same as the old working text (or TRUE if it's a special
222251881Speter   * file). */
223251881Speter  {
224251881Speter    const char *tmp = file_abspath;
225251881Speter
226251881Speter    /* Copy and translate, if necessary. The output file will be deleted at
227251881Speter     * scratch_pool cleanup.
228251881Speter     * ### That's not quite safe: we might rename the file and then maybe
229251881Speter     * its path will get re-used for another temp file before pool clean-up.
230251881Speter     * Instead, we should take responsibility for deleting it. */
231251881Speter    SVN_ERR(svn_wc__internal_translated_file(&tmp_wfile, tmp, db,
232251881Speter                                             file_abspath,
233251881Speter                                             SVN_WC_TRANSLATE_FROM_NF,
234251881Speter                                             cancel_func, cancel_baton,
235251881Speter                                             scratch_pool, scratch_pool));
236251881Speter
237251881Speter    /* If the translation is a no-op, the text base and the working copy
238251881Speter     * file contain the same content, because we use the same props here
239251881Speter     * as were used to detranslate from working file to text base.
240251881Speter     *
241251881Speter     * In that case: don't replace the working file, but make sure
242251881Speter     * it has the right executable and read_write attributes set.
243251881Speter     */
244251881Speter
245251881Speter    SVN_ERR(svn_wc__get_translate_info(NULL, NULL,
246251881Speter                                       NULL,
247251881Speter                                       &special,
248251881Speter                                       db, file_abspath, NULL, FALSE,
249251881Speter                                       scratch_pool, scratch_pool));
250251881Speter    /* Translated file returns the exact pointer if not translated. */
251251881Speter    if (! special && tmp != tmp_wfile)
252251881Speter      SVN_ERR(svn_io_files_contents_same_p(&same, tmp_wfile,
253251881Speter                                           file_abspath, scratch_pool));
254251881Speter    else
255251881Speter      same = TRUE;
256251881Speter  }
257251881Speter
258251881Speter  if (! same)
259251881Speter    {
260251881Speter      SVN_ERR(svn_io_file_rename(tmp_wfile, file_abspath, scratch_pool));
261251881Speter      *overwrote_working = TRUE;
262251881Speter    }
263251881Speter
264251881Speter  /* ### should be using OP_SYNC_FILE_FLAGS, or an internal version of
265251881Speter     ### that here. do we need to set *OVERWROTE_WORKING? */
266251881Speter
267251881Speter  /* ### Re: OVERWROTE_WORKING, the following function is rather liberal
268251881Speter     ### with setting that flag, so we should probably decide if we really
269251881Speter     ### care about it when syncing flags. */
270251881Speter  SVN_ERR(svn_wc__sync_flags_with_props(overwrote_working, db, file_abspath,
271251881Speter                                        scratch_pool));
272251881Speter
273251881Speter  return SVN_NO_ERROR;
274251881Speter}
275251881Speter
276251881Speterstatic svn_error_t *
277251881Speterprocess_commit_file_install(svn_wc__db_t *db,
278251881Speter                       const char *local_abspath,
279251881Speter                       svn_cancel_func_t cancel_func,
280251881Speter                       void *cancel_baton,
281251881Speter                       apr_pool_t *scratch_pool)
282251881Speter{
283251881Speter  svn_boolean_t overwrote_working;
284251881Speter
285251881Speter  /* Install the new file, which may involve expanding keywords.
286251881Speter     A copy of this file should have been dropped into our `tmp/text-base'
287251881Speter     directory during the commit process.  Part of this process
288251881Speter     involves recording the textual timestamp for this entry.  We'd like
289251881Speter     to just use the timestamp of the working file, but it is possible
290251881Speter     that at some point during the commit, the real working file might
291251881Speter     have changed again.
292251881Speter   */
293251881Speter
294251881Speter  SVN_ERR(install_committed_file(&overwrote_working, db,
295251881Speter                                 local_abspath,
296251881Speter                                 cancel_func, cancel_baton,
297251881Speter                                 scratch_pool));
298251881Speter
299251881Speter  /* We will compute and modify the size and timestamp */
300251881Speter  if (overwrote_working)
301251881Speter    {
302251881Speter      apr_finfo_t finfo;
303251881Speter
304251881Speter      SVN_ERR(svn_io_stat(&finfo, local_abspath,
305251881Speter                          APR_FINFO_MIN | APR_FINFO_LINK, scratch_pool));
306251881Speter      SVN_ERR(svn_wc__db_global_record_fileinfo(db, local_abspath,
307251881Speter                                                finfo.size, finfo.mtime,
308251881Speter                                                scratch_pool));
309251881Speter    }
310251881Speter  else
311251881Speter    {
312251881Speter      svn_boolean_t modified;
313251881Speter
314251881Speter      /* The working copy file hasn't been overwritten.  We just
315251881Speter         removed the recorded size and modification time from the nodes
316251881Speter         record by calling svn_wc__db_global_commit().
317251881Speter
318251881Speter         Now we have some file in our working copy that might be what
319251881Speter         we just committed, but we are not certain at this point.
320251881Speter
321251881Speter         We still have a write lock here, so we check if the file is
322251881Speter         what we expect it to be and if it is the right file we update
323251881Speter         the recorded information. (If it isn't we keep the null data).
324251881Speter
325251881Speter         Instead of reimplementing all this here, we just call a function
326251881Speter         that already does implement this when it notices that we have the
327251881Speter         right kind of lock (and we ignore the result)
328251881Speter       */
329251881Speter      SVN_ERR(svn_wc__internal_file_modified_p(&modified,
330251881Speter                                               db, local_abspath, FALSE,
331251881Speter                                               scratch_pool));
332251881Speter    }
333251881Speter  return SVN_NO_ERROR;
334251881Speter}
335251881Speter
336251881Speter
337251881Speterstatic svn_error_t *
338251881Speterrun_file_commit(work_item_baton_t *wqb,
339251881Speter                svn_wc__db_t *db,
340251881Speter                const svn_skel_t *work_item,
341251881Speter                const char *wri_abspath,
342251881Speter                svn_cancel_func_t cancel_func,
343251881Speter                void *cancel_baton,
344251881Speter                apr_pool_t *scratch_pool)
345251881Speter{
346251881Speter  const svn_skel_t *arg1 = work_item->children->next;
347251881Speter  const char *local_relpath;
348251881Speter  const char *local_abspath;
349251881Speter
350251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
351251881Speter  SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
352251881Speter                                  local_relpath, scratch_pool, scratch_pool));
353251881Speter
354251881Speter  /* We don't both parsing the other two values in the skel. */
355251881Speter
356251881Speter  return svn_error_trace(
357251881Speter                process_commit_file_install(db, local_abspath,
358251881Speter                                            cancel_func, cancel_baton,
359251881Speter                                            scratch_pool));
360251881Speter}
361251881Speter
362251881Spetersvn_error_t *
363251881Spetersvn_wc__wq_build_file_commit(svn_skel_t **work_item,
364251881Speter                             svn_wc__db_t *db,
365251881Speter                             const char *local_abspath,
366251881Speter                             svn_boolean_t props_mod,
367251881Speter                             apr_pool_t *result_pool,
368251881Speter                             apr_pool_t *scratch_pool)
369251881Speter{
370251881Speter  const char *local_relpath;
371251881Speter  *work_item = svn_skel__make_empty_list(result_pool);
372251881Speter
373251881Speter  SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
374251881Speter                                local_abspath, result_pool, scratch_pool));
375251881Speter
376251881Speter  svn_skel__prepend_str(local_relpath, *work_item, result_pool);
377251881Speter
378251881Speter  svn_skel__prepend_str(OP_FILE_COMMIT, *work_item, result_pool);
379251881Speter
380251881Speter  return SVN_NO_ERROR;
381251881Speter}
382251881Speter
383251881Speter/* ------------------------------------------------------------------------ */
384251881Speter/* OP_POSTUPGRADE  */
385251881Speter
386251881Speterstatic svn_error_t *
387251881Speterrun_postupgrade(work_item_baton_t *wqb,
388251881Speter                svn_wc__db_t *db,
389251881Speter                const svn_skel_t *work_item,
390251881Speter                const char *wri_abspath,
391251881Speter                svn_cancel_func_t cancel_func,
392251881Speter                void *cancel_baton,
393251881Speter                apr_pool_t *scratch_pool)
394251881Speter{
395251881Speter  const char *entries_path;
396251881Speter  const char *format_path;
397251881Speter  const char *wcroot_abspath;
398251881Speter  svn_error_t *err;
399251881Speter
400251881Speter  err = svn_wc__wipe_postupgrade(wri_abspath, FALSE,
401251881Speter                                 cancel_func, cancel_baton, scratch_pool);
402251881Speter  if (err && err->apr_err == SVN_ERR_ENTRY_NOT_FOUND)
403251881Speter    /* No entry, this can happen when the wq item is rerun. */
404251881Speter    svn_error_clear(err);
405251881Speter  else
406251881Speter    SVN_ERR(err);
407251881Speter
408251881Speter  SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, db, wri_abspath,
409251881Speter                                scratch_pool, scratch_pool));
410251881Speter
411251881Speter  entries_path = svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_ENTRIES,
412251881Speter                                   scratch_pool);
413251881Speter  format_path = svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_FORMAT,
414251881Speter                                   scratch_pool);
415251881Speter
416251881Speter  /* Write the 'format' and 'entries' files.
417251881Speter
418251881Speter     ### The order may matter for some sufficiently old clients.. but
419251881Speter     ### this code only runs during upgrade after the files had been
420251881Speter     ### removed earlier during the upgrade. */
421299742Sdim  SVN_ERR(svn_io_write_atomic(format_path, SVN_WC__NON_ENTRIES_STRING,
422251881Speter                              sizeof(SVN_WC__NON_ENTRIES_STRING) - 1,
423299742Sdim                              NULL, scratch_pool));
424251881Speter
425299742Sdim  SVN_ERR(svn_io_write_atomic(entries_path, SVN_WC__NON_ENTRIES_STRING,
426251881Speter                              sizeof(SVN_WC__NON_ENTRIES_STRING) - 1,
427299742Sdim                              NULL, scratch_pool));
428251881Speter
429251881Speter  return SVN_NO_ERROR;
430251881Speter}
431251881Speter
432251881Spetersvn_error_t *
433251881Spetersvn_wc__wq_build_postupgrade(svn_skel_t **work_item,
434251881Speter                             apr_pool_t *result_pool)
435251881Speter{
436251881Speter  *work_item = svn_skel__make_empty_list(result_pool);
437251881Speter
438251881Speter  svn_skel__prepend_str(OP_POSTUPGRADE, *work_item, result_pool);
439251881Speter
440251881Speter  return SVN_NO_ERROR;
441251881Speter}
442251881Speter
443251881Speter/* ------------------------------------------------------------------------ */
444251881Speter
445251881Speter/* OP_FILE_INSTALL */
446251881Speter
447251881Speter/* Process the OP_FILE_INSTALL work item WORK_ITEM.
448251881Speter * See svn_wc__wq_build_file_install() which generates this work item.
449251881Speter * Implements (struct work_item_dispatch).func. */
450251881Speterstatic svn_error_t *
451251881Speterrun_file_install(work_item_baton_t *wqb,
452251881Speter                 svn_wc__db_t *db,
453251881Speter                 const svn_skel_t *work_item,
454251881Speter                 const char *wri_abspath,
455251881Speter                 svn_cancel_func_t cancel_func,
456251881Speter                 void *cancel_baton,
457251881Speter                 apr_pool_t *scratch_pool)
458251881Speter{
459251881Speter  const svn_skel_t *arg1 = work_item->children->next;
460251881Speter  const svn_skel_t *arg4 = arg1->next->next->next;
461251881Speter  const char *local_relpath;
462251881Speter  const char *local_abspath;
463251881Speter  svn_boolean_t use_commit_times;
464251881Speter  svn_boolean_t record_fileinfo;
465251881Speter  svn_boolean_t special;
466251881Speter  svn_stream_t *src_stream;
467251881Speter  svn_subst_eol_style_t style;
468251881Speter  const char *eol;
469251881Speter  apr_hash_t *keywords;
470251881Speter  const char *temp_dir_abspath;
471251881Speter  svn_stream_t *dst_stream;
472251881Speter  apr_int64_t val;
473251881Speter  const char *wcroot_abspath;
474251881Speter  const char *source_abspath;
475251881Speter  const svn_checksum_t *checksum;
476251881Speter  apr_hash_t *props;
477251881Speter  apr_time_t changed_date;
478251881Speter
479251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
480251881Speter  SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
481251881Speter                                  local_relpath, scratch_pool, scratch_pool));
482251881Speter
483251881Speter  SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
484251881Speter  use_commit_times = (val != 0);
485251881Speter  SVN_ERR(svn_skel__parse_int(&val, arg1->next->next, scratch_pool));
486251881Speter  record_fileinfo = (val != 0);
487251881Speter
488251881Speter  SVN_ERR(svn_wc__db_read_node_install_info(&wcroot_abspath,
489251881Speter                                            &checksum, &props,
490251881Speter                                            &changed_date,
491251881Speter                                            db, local_abspath, wri_abspath,
492251881Speter                                            scratch_pool, scratch_pool));
493251881Speter
494251881Speter  if (arg4 != NULL)
495251881Speter    {
496251881Speter      /* Use the provided path for the source.  */
497251881Speter      local_relpath = apr_pstrmemdup(scratch_pool, arg4->data, arg4->len);
498251881Speter      SVN_ERR(svn_wc__db_from_relpath(&source_abspath, db, wri_abspath,
499251881Speter                                      local_relpath,
500251881Speter                                      scratch_pool, scratch_pool));
501251881Speter    }
502251881Speter  else if (! checksum)
503251881Speter    {
504251881Speter      /* This error replaces a previous assertion. Reporting an error from here
505251881Speter         leaves the workingqueue operation in place, so the working copy is
506251881Speter         still broken!
507251881Speter
508251881Speter         But when we report this error the user at least knows what node has
509251881Speter         this specific problem, so maybe we can find out why users see this
510251881Speter         error */
511251881Speter      return svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
512251881Speter                               _("Can't install '%s' from pristine store, "
513251881Speter                                 "because no checksum is recorded for this "
514251881Speter                                 "file"),
515251881Speter                               svn_dirent_local_style(local_abspath,
516251881Speter                                                      scratch_pool));
517251881Speter    }
518251881Speter  else
519251881Speter    {
520251881Speter      SVN_ERR(svn_wc__db_pristine_get_future_path(&source_abspath,
521251881Speter                                                  wcroot_abspath,
522251881Speter                                                  checksum,
523251881Speter                                                  scratch_pool, scratch_pool));
524251881Speter    }
525251881Speter
526251881Speter  SVN_ERR(svn_stream_open_readonly(&src_stream, source_abspath,
527251881Speter                                   scratch_pool, scratch_pool));
528251881Speter
529251881Speter  /* Fetch all the translation bits.  */
530251881Speter  SVN_ERR(svn_wc__get_translate_info(&style, &eol,
531251881Speter                                     &keywords,
532251881Speter                                     &special, db, local_abspath,
533251881Speter                                     props, FALSE,
534251881Speter                                     scratch_pool, scratch_pool));
535251881Speter  if (special)
536251881Speter    {
537251881Speter      /* When this stream is closed, the resulting special file will
538251881Speter         atomically be created/moved into place at LOCAL_ABSPATH.  */
539251881Speter      SVN_ERR(svn_subst_create_specialfile(&dst_stream, local_abspath,
540251881Speter                                           scratch_pool, scratch_pool));
541251881Speter
542251881Speter      /* Copy the "repository normal" form of the special file into the
543251881Speter         special stream.  */
544251881Speter      SVN_ERR(svn_stream_copy3(src_stream, dst_stream,
545251881Speter                               cancel_func, cancel_baton,
546251881Speter                               scratch_pool));
547251881Speter
548251881Speter      /* No need to set exec or read-only flags on special files.  */
549251881Speter
550251881Speter      /* ### Shouldn't this record a timestamp and size, etc.? */
551251881Speter      return SVN_NO_ERROR;
552251881Speter    }
553251881Speter
554251881Speter  if (svn_subst_translation_required(style, eol, keywords,
555251881Speter                                     FALSE /* special */,
556251881Speter                                     TRUE /* force_eol_check */))
557251881Speter    {
558251881Speter      /* Wrap it in a translating (expanding) stream.  */
559251881Speter      src_stream = svn_subst_stream_translated(src_stream, eol,
560251881Speter                                               TRUE /* repair */,
561251881Speter                                               keywords,
562251881Speter                                               TRUE /* expand */,
563251881Speter                                               scratch_pool);
564251881Speter    }
565251881Speter
566251881Speter  /* Where is the Right Place to put a temp file in this working copy?  */
567251881Speter  SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath,
568251881Speter                                         db, wcroot_abspath,
569251881Speter                                         scratch_pool, scratch_pool));
570251881Speter
571251881Speter  /* Translate to a temporary file. We don't want the user seeing a partial
572251881Speter     file, nor let them muck with it while we translate. We may also need to
573251881Speter     get its TRANSLATED_SIZE before the user can monkey it.  */
574299742Sdim  SVN_ERR(svn_stream__create_for_install(&dst_stream, temp_dir_abspath,
575299742Sdim                                         scratch_pool, scratch_pool));
576251881Speter
577251881Speter  /* Copy from the source to the dest, translating as we go. This will also
578251881Speter     close both streams.  */
579251881Speter  SVN_ERR(svn_stream_copy3(src_stream, dst_stream,
580251881Speter                           cancel_func, cancel_baton,
581251881Speter                           scratch_pool));
582251881Speter
583251881Speter  /* All done. Move the file into place.  */
584299742Sdim  /* With a single db we might want to install files in a missing directory.
585299742Sdim     Simply trying this scenario on error won't do any harm and at least
586299742Sdim     one user reported this problem on IRC. */
587299742Sdim  SVN_ERR(svn_stream__install_stream(dst_stream, local_abspath,
588299742Sdim                                     TRUE /* make_parents*/, scratch_pool));
589251881Speter
590251881Speter  /* Tweak the on-disk file according to its properties.  */
591251881Speter#ifndef WIN32
592251881Speter  if (props && svn_hash_gets(props, SVN_PROP_EXECUTABLE))
593251881Speter    SVN_ERR(svn_io_set_file_executable(local_abspath, TRUE, FALSE,
594251881Speter                                       scratch_pool));
595251881Speter#endif
596251881Speter
597251881Speter  /* Note that this explicitly checks the pristine properties, to make sure
598251881Speter     that when the lock is locally set (=modification) it is not read only */
599251881Speter  if (props && svn_hash_gets(props, SVN_PROP_NEEDS_LOCK))
600251881Speter    {
601251881Speter      svn_wc__db_status_t status;
602251881Speter      svn_wc__db_lock_t *lock;
603251881Speter      SVN_ERR(svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL,
604251881Speter                                   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
605251881Speter                                   NULL, NULL, &lock, NULL, NULL, NULL, NULL,
606251881Speter                                   NULL, NULL, NULL, NULL, NULL, NULL,
607251881Speter                                   db, local_abspath,
608251881Speter                                   scratch_pool, scratch_pool));
609251881Speter
610251881Speter      if (!lock && status != svn_wc__db_status_added)
611251881Speter        SVN_ERR(svn_io_set_file_read_only(local_abspath, FALSE, scratch_pool));
612251881Speter    }
613251881Speter
614251881Speter  if (use_commit_times)
615251881Speter    {
616251881Speter      if (changed_date)
617251881Speter        SVN_ERR(svn_io_set_file_affected_time(changed_date,
618251881Speter                                              local_abspath,
619251881Speter                                              scratch_pool));
620251881Speter    }
621251881Speter
622251881Speter  /* ### this should happen before we rename the file into place.  */
623251881Speter  if (record_fileinfo)
624251881Speter    {
625251881Speter      SVN_ERR(get_and_record_fileinfo(wqb, local_abspath,
626251881Speter                                      FALSE /* ignore_enoent */,
627251881Speter                                      scratch_pool));
628251881Speter    }
629251881Speter
630251881Speter  return SVN_NO_ERROR;
631251881Speter}
632251881Speter
633251881Speter
634251881Spetersvn_error_t *
635251881Spetersvn_wc__wq_build_file_install(svn_skel_t **work_item,
636251881Speter                              svn_wc__db_t *db,
637251881Speter                              const char *local_abspath,
638251881Speter                              const char *source_abspath,
639251881Speter                              svn_boolean_t use_commit_times,
640251881Speter                              svn_boolean_t record_fileinfo,
641251881Speter                              apr_pool_t *result_pool,
642251881Speter                              apr_pool_t *scratch_pool)
643251881Speter{
644251881Speter  const char *local_relpath;
645251881Speter  const char *wri_abspath;
646251881Speter  *work_item = svn_skel__make_empty_list(result_pool);
647251881Speter
648251881Speter  /* Use the directory of the file to install as wri_abspath to avoid
649251881Speter     filestats on just obtaining the wc-root */
650251881Speter  wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
651251881Speter
652251881Speter  /* If a SOURCE_ABSPATH was provided, then put it into the skel. If this
653251881Speter     value is not provided, then the file's pristine contents will be used. */
654251881Speter  if (source_abspath != NULL)
655251881Speter    {
656251881Speter      SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
657251881Speter                                    source_abspath,
658251881Speter                                    result_pool, scratch_pool));
659251881Speter
660251881Speter      svn_skel__prepend_str(local_relpath, *work_item, result_pool);
661251881Speter    }
662251881Speter
663251881Speter  SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
664251881Speter                                local_abspath, result_pool, scratch_pool));
665251881Speter
666251881Speter  svn_skel__prepend_int(record_fileinfo, *work_item, result_pool);
667251881Speter  svn_skel__prepend_int(use_commit_times, *work_item, result_pool);
668251881Speter  svn_skel__prepend_str(local_relpath, *work_item, result_pool);
669251881Speter  svn_skel__prepend_str(OP_FILE_INSTALL, *work_item, result_pool);
670251881Speter
671251881Speter  return SVN_NO_ERROR;
672251881Speter}
673251881Speter
674251881Speter
675251881Speter/* ------------------------------------------------------------------------ */
676251881Speter
677251881Speter/* OP_FILE_REMOVE  */
678251881Speter
679251881Speter/* Process the OP_FILE_REMOVE work item WORK_ITEM.
680251881Speter * See svn_wc__wq_build_file_remove() which generates this work item.
681251881Speter * Implements (struct work_item_dispatch).func. */
682251881Speterstatic svn_error_t *
683251881Speterrun_file_remove(work_item_baton_t *wqb,
684251881Speter                svn_wc__db_t *db,
685251881Speter                const svn_skel_t *work_item,
686251881Speter                const char *wri_abspath,
687251881Speter                svn_cancel_func_t cancel_func,
688251881Speter                void *cancel_baton,
689251881Speter                apr_pool_t *scratch_pool)
690251881Speter{
691251881Speter  const svn_skel_t *arg1 = work_item->children->next;
692251881Speter  const char *local_relpath;
693251881Speter  const char *local_abspath;
694251881Speter
695251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
696251881Speter  SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
697251881Speter                                  local_relpath, scratch_pool, scratch_pool));
698251881Speter
699251881Speter  /* Remove the path, no worrying if it isn't there.  */
700251881Speter  return svn_error_trace(svn_io_remove_file2(local_abspath, TRUE,
701251881Speter                                             scratch_pool));
702251881Speter}
703251881Speter
704251881Speter
705251881Spetersvn_error_t *
706251881Spetersvn_wc__wq_build_file_remove(svn_skel_t **work_item,
707251881Speter                             svn_wc__db_t *db,
708251881Speter                             const char *wri_abspath,
709251881Speter                             const char *local_abspath,
710251881Speter                             apr_pool_t *result_pool,
711251881Speter                             apr_pool_t *scratch_pool)
712251881Speter{
713251881Speter  const char *local_relpath;
714251881Speter  *work_item = svn_skel__make_empty_list(result_pool);
715251881Speter
716251881Speter  SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
717251881Speter                                local_abspath, result_pool, scratch_pool));
718251881Speter
719251881Speter  svn_skel__prepend_str(local_relpath, *work_item, result_pool);
720251881Speter  svn_skel__prepend_str(OP_FILE_REMOVE, *work_item, result_pool);
721251881Speter
722251881Speter  return SVN_NO_ERROR;
723251881Speter}
724251881Speter
725251881Speter/* ------------------------------------------------------------------------ */
726251881Speter
727251881Speter/* OP_DIRECTORY_REMOVE  */
728251881Speter
729251881Speter/* Process the OP_FILE_REMOVE work item WORK_ITEM.
730251881Speter * See svn_wc__wq_build_file_remove() which generates this work item.
731251881Speter * Implements (struct work_item_dispatch).func. */
732251881Speterstatic svn_error_t *
733251881Speterrun_dir_remove(work_item_baton_t *wqb,
734251881Speter               svn_wc__db_t *db,
735251881Speter               const svn_skel_t *work_item,
736251881Speter               const char *wri_abspath,
737251881Speter               svn_cancel_func_t cancel_func,
738251881Speter               void *cancel_baton,
739251881Speter               apr_pool_t *scratch_pool)
740251881Speter{
741251881Speter  const svn_skel_t *arg1 = work_item->children->next;
742251881Speter  const char *local_relpath;
743251881Speter  const char *local_abspath;
744251881Speter  svn_boolean_t recursive;
745251881Speter
746251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
747251881Speter  SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
748251881Speter                                  local_relpath, scratch_pool, scratch_pool));
749251881Speter
750251881Speter  recursive = FALSE;
751251881Speter  if (arg1->next)
752251881Speter    {
753251881Speter      apr_int64_t val;
754251881Speter      SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
755251881Speter
756251881Speter      recursive = (val != 0);
757251881Speter    }
758251881Speter
759251881Speter  /* Remove the path, no worrying if it isn't there.  */
760251881Speter  if (recursive)
761251881Speter    return svn_error_trace(
762251881Speter                svn_io_remove_dir2(local_abspath, TRUE,
763251881Speter                                   cancel_func, cancel_baton,
764251881Speter                                   scratch_pool));
765251881Speter  else
766251881Speter    {
767251881Speter      svn_error_t *err;
768251881Speter
769251881Speter      err = svn_io_dir_remove_nonrecursive(local_abspath, scratch_pool);
770251881Speter
771251881Speter      if (err && (APR_STATUS_IS_ENOENT(err->apr_err)
772251881Speter                  || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)
773251881Speter                  || APR_STATUS_IS_ENOTEMPTY(err->apr_err)))
774251881Speter        {
775251881Speter          svn_error_clear(err);
776251881Speter          err = NULL;
777251881Speter        }
778251881Speter
779251881Speter      return svn_error_trace(err);
780251881Speter    }
781251881Speter}
782251881Speter
783251881Spetersvn_error_t *
784251881Spetersvn_wc__wq_build_dir_remove(svn_skel_t **work_item,
785251881Speter                            svn_wc__db_t *db,
786251881Speter                            const char *wri_abspath,
787251881Speter                            const char *local_abspath,
788251881Speter                            svn_boolean_t recursive,
789251881Speter                            apr_pool_t *result_pool,
790251881Speter                            apr_pool_t *scratch_pool)
791251881Speter{
792251881Speter  const char *local_relpath;
793251881Speter  *work_item = svn_skel__make_empty_list(result_pool);
794251881Speter
795251881Speter  SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
796251881Speter                                local_abspath, result_pool, scratch_pool));
797251881Speter
798251881Speter  if (recursive)
799251881Speter    svn_skel__prepend_int(TRUE, *work_item, result_pool);
800251881Speter
801251881Speter  svn_skel__prepend_str(local_relpath, *work_item, result_pool);
802251881Speter  svn_skel__prepend_str(OP_DIRECTORY_REMOVE, *work_item, result_pool);
803251881Speter
804251881Speter  return SVN_NO_ERROR;
805251881Speter}
806251881Speter
807251881Speter/* ------------------------------------------------------------------------ */
808251881Speter
809251881Speter/* OP_FILE_MOVE  */
810251881Speter
811251881Speter/* Process the OP_FILE_MOVE work item WORK_ITEM.
812251881Speter * See svn_wc__wq_build_file_move() which generates this work item.
813251881Speter * Implements (struct work_item_dispatch).func. */
814251881Speterstatic svn_error_t *
815251881Speterrun_file_move(work_item_baton_t *wqb,
816251881Speter              svn_wc__db_t *db,
817251881Speter              const svn_skel_t *work_item,
818251881Speter              const char *wri_abspath,
819251881Speter              svn_cancel_func_t cancel_func,
820251881Speter              void *cancel_baton,
821251881Speter              apr_pool_t *scratch_pool)
822251881Speter{
823251881Speter  const svn_skel_t *arg1 = work_item->children->next;
824251881Speter  const char *src_abspath, *dst_abspath;
825251881Speter  const char *local_relpath;
826251881Speter  svn_error_t *err;
827251881Speter
828251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
829251881Speter  SVN_ERR(svn_wc__db_from_relpath(&src_abspath, db, wri_abspath, local_relpath,
830251881Speter                                  scratch_pool, scratch_pool));
831251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->data,
832251881Speter                                 arg1->next->len);
833251881Speter  SVN_ERR(svn_wc__db_from_relpath(&dst_abspath, db, wri_abspath, local_relpath,
834251881Speter                                  scratch_pool, scratch_pool));
835251881Speter
836251881Speter  /* Use svn_io_file_move() instead of svn_io_file_rename() to allow cross
837251881Speter     device copies. We should not fail in the workqueue. */
838251881Speter
839251881Speter  err = svn_io_file_move(src_abspath, dst_abspath, scratch_pool);
840251881Speter
841251881Speter  /* If the source is not found, we assume the wq op is already handled */
842251881Speter  if (err && APR_STATUS_IS_ENOENT(err->apr_err))
843251881Speter    svn_error_clear(err);
844251881Speter  else
845251881Speter    SVN_ERR(err);
846251881Speter
847251881Speter  return SVN_NO_ERROR;
848251881Speter}
849251881Speter
850251881Speter
851251881Spetersvn_error_t *
852251881Spetersvn_wc__wq_build_file_move(svn_skel_t **work_item,
853251881Speter                           svn_wc__db_t *db,
854251881Speter                           const char *wri_abspath,
855251881Speter                           const char *src_abspath,
856251881Speter                           const char *dst_abspath,
857251881Speter                           apr_pool_t *result_pool,
858251881Speter                           apr_pool_t *scratch_pool)
859251881Speter{
860251881Speter  svn_node_kind_t kind;
861251881Speter  const char *local_relpath;
862251881Speter  *work_item = svn_skel__make_empty_list(result_pool);
863251881Speter
864251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
865251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
866251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
867251881Speter
868251881Speter  /* File must exist */
869251881Speter  SVN_ERR(svn_io_check_path(src_abspath, &kind, result_pool));
870251881Speter
871251881Speter  if (kind == svn_node_none)
872251881Speter    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
873251881Speter                             _("'%s' not found"),
874251881Speter                             svn_dirent_local_style(src_abspath,
875251881Speter                                                    scratch_pool));
876251881Speter
877251881Speter  SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath, dst_abspath,
878251881Speter                                result_pool, scratch_pool));
879251881Speter  svn_skel__prepend_str(local_relpath, *work_item, result_pool);
880251881Speter
881251881Speter  SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath, src_abspath,
882251881Speter                                result_pool, scratch_pool));
883251881Speter  svn_skel__prepend_str(local_relpath, *work_item, result_pool);
884251881Speter
885251881Speter  svn_skel__prepend_str(OP_FILE_MOVE, *work_item, result_pool);
886251881Speter
887251881Speter  return SVN_NO_ERROR;
888251881Speter}
889251881Speter
890251881Speter/* ------------------------------------------------------------------------ */
891251881Speter
892251881Speter/* OP_FILE_COPY_TRANSLATED */
893251881Speter
894251881Speter/* Process the OP_FILE_COPY_TRANSLATED work item WORK_ITEM.
895251881Speter * See run_file_copy_translated() which generates this work item.
896251881Speter * Implements (struct work_item_dispatch).func. */
897251881Speterstatic svn_error_t *
898251881Speterrun_file_copy_translated(work_item_baton_t *wqb,
899251881Speter                         svn_wc__db_t *db,
900251881Speter                         const svn_skel_t *work_item,
901251881Speter                         const char *wri_abspath,
902251881Speter                         svn_cancel_func_t cancel_func,
903251881Speter                         void *cancel_baton,
904251881Speter                         apr_pool_t *scratch_pool)
905251881Speter{
906251881Speter  const svn_skel_t *arg1 = work_item->children->next;
907251881Speter  const char *local_abspath, *src_abspath, *dst_abspath;
908251881Speter  const char *local_relpath;
909251881Speter  svn_subst_eol_style_t style;
910251881Speter  const char *eol;
911251881Speter  apr_hash_t *keywords;
912251881Speter  svn_boolean_t special;
913251881Speter
914251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
915251881Speter  SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
916251881Speter                                  local_relpath, scratch_pool, scratch_pool));
917251881Speter
918251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->data,
919251881Speter                               arg1->next->len);
920251881Speter  SVN_ERR(svn_wc__db_from_relpath(&src_abspath, db, wri_abspath,
921251881Speter                                  local_relpath, scratch_pool, scratch_pool));
922251881Speter
923251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->next->data,
924251881Speter                                arg1->next->next->len);
925251881Speter  SVN_ERR(svn_wc__db_from_relpath(&dst_abspath, db, wri_abspath,
926251881Speter                                  local_relpath, scratch_pool, scratch_pool));
927251881Speter
928251881Speter  SVN_ERR(svn_wc__get_translate_info(&style, &eol,
929251881Speter                                     &keywords,
930251881Speter                                     &special,
931251881Speter                                     db, local_abspath, NULL, FALSE,
932251881Speter                                     scratch_pool, scratch_pool));
933251881Speter
934251881Speter  SVN_ERR(svn_subst_copy_and_translate4(src_abspath, dst_abspath,
935251881Speter                                        eol, TRUE /* repair */,
936251881Speter                                        keywords, TRUE /* expand */,
937251881Speter                                        special,
938251881Speter                                        cancel_func, cancel_baton,
939251881Speter                                        scratch_pool));
940251881Speter  return SVN_NO_ERROR;
941251881Speter}
942251881Speter
943251881Speter
944251881Spetersvn_error_t *
945251881Spetersvn_wc__wq_build_file_copy_translated(svn_skel_t **work_item,
946251881Speter                                      svn_wc__db_t *db,
947251881Speter                                      const char *local_abspath,
948251881Speter                                      const char *src_abspath,
949251881Speter                                      const char *dst_abspath,
950251881Speter                                      apr_pool_t *result_pool,
951251881Speter                                      apr_pool_t *scratch_pool)
952251881Speter{
953251881Speter  svn_node_kind_t kind;
954251881Speter  const char *local_relpath;
955251881Speter
956251881Speter  *work_item = svn_skel__make_empty_list(result_pool);
957251881Speter
958251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
959251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
960251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
961251881Speter
962251881Speter  /* File must exist */
963251881Speter  SVN_ERR(svn_io_check_path(src_abspath, &kind, result_pool));
964251881Speter
965251881Speter  if (kind == svn_node_none)
966251881Speter    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
967251881Speter                             _("'%s' not found"),
968251881Speter                             svn_dirent_local_style(src_abspath,
969251881Speter                                                    scratch_pool));
970251881Speter
971251881Speter  SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, dst_abspath,
972251881Speter                                result_pool, scratch_pool));
973251881Speter  svn_skel__prepend_str(local_relpath, *work_item, result_pool);
974251881Speter
975251881Speter  SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, src_abspath,
976251881Speter                                result_pool, scratch_pool));
977251881Speter  svn_skel__prepend_str(local_relpath, *work_item, result_pool);
978251881Speter
979251881Speter  SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
980251881Speter                                local_abspath, result_pool, scratch_pool));
981251881Speter  svn_skel__prepend_str(local_relpath, *work_item, result_pool);
982251881Speter
983251881Speter  svn_skel__prepend_str(OP_FILE_COPY_TRANSLATED, *work_item, result_pool);
984251881Speter
985251881Speter  return SVN_NO_ERROR;
986251881Speter}
987251881Speter
988251881Speter/* ------------------------------------------------------------------------ */
989251881Speter
990251881Speter/* OP_DIRECTORY_INSTALL  */
991251881Speter
992251881Speterstatic svn_error_t *
993251881Speterrun_dir_install(work_item_baton_t *wqb,
994251881Speter                svn_wc__db_t *db,
995251881Speter                const svn_skel_t *work_item,
996251881Speter                const char *wri_abspath,
997251881Speter                svn_cancel_func_t cancel_func,
998251881Speter                void *cancel_baton,
999251881Speter                apr_pool_t *scratch_pool)
1000251881Speter{
1001251881Speter  const svn_skel_t *arg1 = work_item->children->next;
1002251881Speter  const char *local_relpath;
1003251881Speter  const char *local_abspath;
1004251881Speter
1005251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1006251881Speter  SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1007251881Speter                                  local_relpath, scratch_pool, scratch_pool));
1008251881Speter
1009251881Speter  SVN_ERR(svn_wc__ensure_directory(local_abspath, scratch_pool));
1010251881Speter
1011251881Speter  return SVN_NO_ERROR;
1012251881Speter}
1013251881Speter
1014251881Spetersvn_error_t *
1015251881Spetersvn_wc__wq_build_dir_install(svn_skel_t **work_item,
1016251881Speter                             svn_wc__db_t *db,
1017251881Speter                             const char *local_abspath,
1018299742Sdim                             apr_pool_t *result_pool,
1019299742Sdim                             apr_pool_t *scratch_pool)
1020251881Speter{
1021251881Speter  const char *local_relpath;
1022251881Speter
1023251881Speter  *work_item = svn_skel__make_empty_list(result_pool);
1024251881Speter
1025251881Speter  SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
1026251881Speter                                local_abspath, result_pool, scratch_pool));
1027251881Speter  svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1028251881Speter
1029251881Speter  svn_skel__prepend_str(OP_DIRECTORY_INSTALL, *work_item, result_pool);
1030251881Speter
1031251881Speter  return SVN_NO_ERROR;
1032251881Speter}
1033251881Speter
1034251881Speter
1035251881Speter/* ------------------------------------------------------------------------ */
1036251881Speter
1037251881Speter/* OP_SYNC_FILE_FLAGS  */
1038251881Speter
1039251881Speter/* Process the OP_SYNC_FILE_FLAGS work item WORK_ITEM.
1040251881Speter * See svn_wc__wq_build_sync_file_flags() which generates this work item.
1041251881Speter * Implements (struct work_item_dispatch).func. */
1042251881Speterstatic svn_error_t *
1043251881Speterrun_sync_file_flags(work_item_baton_t *wqb,
1044251881Speter                    svn_wc__db_t *db,
1045251881Speter                    const svn_skel_t *work_item,
1046251881Speter                    const char *wri_abspath,
1047251881Speter                    svn_cancel_func_t cancel_func,
1048251881Speter                    void *cancel_baton,
1049251881Speter                    apr_pool_t *scratch_pool)
1050251881Speter{
1051251881Speter  const svn_skel_t *arg1 = work_item->children->next;
1052251881Speter  const char *local_relpath;
1053251881Speter  const char *local_abspath;
1054251881Speter
1055251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1056251881Speter  SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1057251881Speter                                  local_relpath, scratch_pool, scratch_pool));
1058251881Speter
1059251881Speter  return svn_error_trace(svn_wc__sync_flags_with_props(NULL, db,
1060251881Speter                                            local_abspath, scratch_pool));
1061251881Speter}
1062251881Speter
1063251881Speter
1064251881Spetersvn_error_t *
1065251881Spetersvn_wc__wq_build_sync_file_flags(svn_skel_t **work_item,
1066251881Speter                                 svn_wc__db_t *db,
1067251881Speter                                 const char *local_abspath,
1068251881Speter                                 apr_pool_t *result_pool,
1069251881Speter                                 apr_pool_t *scratch_pool)
1070251881Speter{
1071251881Speter  const char *local_relpath;
1072251881Speter  *work_item = svn_skel__make_empty_list(result_pool);
1073251881Speter
1074251881Speter  SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
1075251881Speter                                local_abspath, result_pool, scratch_pool));
1076251881Speter
1077251881Speter  svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1078251881Speter  svn_skel__prepend_str(OP_SYNC_FILE_FLAGS, *work_item, result_pool);
1079251881Speter
1080251881Speter  return SVN_NO_ERROR;
1081251881Speter}
1082251881Speter
1083251881Speter
1084251881Speter/* ------------------------------------------------------------------------ */
1085251881Speter
1086251881Speter/* OP_PREJ_INSTALL  */
1087251881Speter
1088251881Speterstatic svn_error_t *
1089251881Speterrun_prej_install(work_item_baton_t *wqb,
1090251881Speter                 svn_wc__db_t *db,
1091251881Speter                 const svn_skel_t *work_item,
1092251881Speter                 const char *wri_abspath,
1093251881Speter                 svn_cancel_func_t cancel_func,
1094251881Speter                 void *cancel_baton,
1095251881Speter                 apr_pool_t *scratch_pool)
1096251881Speter{
1097251881Speter  const svn_skel_t *arg1 = work_item->children->next;
1098251881Speter  const char *local_relpath;
1099251881Speter  const char *local_abspath;
1100251881Speter  svn_skel_t *conflicts;
1101251881Speter  const svn_skel_t *prop_conflict_skel;
1102251881Speter  const char *tmp_prejfile_abspath;
1103251881Speter  const char *prejfile_abspath;
1104251881Speter
1105251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1106251881Speter  SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1107251881Speter                                  local_relpath, scratch_pool, scratch_pool));
1108251881Speter
1109299742Sdim  SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL, db, local_abspath,
1110251881Speter                                   scratch_pool, scratch_pool));
1111251881Speter
1112251881Speter  SVN_ERR(svn_wc__conflict_read_prop_conflict(&prejfile_abspath,
1113251881Speter                                              NULL, NULL, NULL, NULL,
1114251881Speter                                              db, local_abspath, conflicts,
1115251881Speter                                              scratch_pool, scratch_pool));
1116251881Speter
1117251881Speter  if (arg1->next != NULL)
1118299742Sdim    prop_conflict_skel = arg1->next; /* Before Subversion 1.9 */
1119251881Speter  else
1120299742Sdim    prop_conflict_skel = NULL; /* Read from DB */
1121251881Speter
1122251881Speter  /* Construct a property reject file in the temporary area.  */
1123251881Speter  SVN_ERR(svn_wc__create_prejfile(&tmp_prejfile_abspath,
1124251881Speter                                  db, local_abspath,
1125251881Speter                                  prop_conflict_skel,
1126299742Sdim                                  cancel_func, cancel_baton,
1127251881Speter                                  scratch_pool, scratch_pool));
1128251881Speter
1129251881Speter  /* ... and atomically move it into place.  */
1130251881Speter  SVN_ERR(svn_io_file_rename(tmp_prejfile_abspath,
1131251881Speter                             prejfile_abspath,
1132251881Speter                             scratch_pool));
1133251881Speter
1134251881Speter  return SVN_NO_ERROR;
1135251881Speter}
1136251881Speter
1137251881Speter
1138251881Spetersvn_error_t *
1139251881Spetersvn_wc__wq_build_prej_install(svn_skel_t **work_item,
1140251881Speter                              svn_wc__db_t *db,
1141251881Speter                              const char *local_abspath,
1142299742Sdim                              /*svn_skel_t *conflict_skel,*/
1143251881Speter                              apr_pool_t *result_pool,
1144251881Speter                              apr_pool_t *scratch_pool)
1145251881Speter{
1146251881Speter  const char *local_relpath;
1147251881Speter  *work_item = svn_skel__make_empty_list(result_pool);
1148251881Speter
1149251881Speter  SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
1150251881Speter                                local_abspath, result_pool, scratch_pool));
1151251881Speter
1152299742Sdim  /* ### In Subversion 1.7 and 1.8 we created a legacy property conflict skel
1153299742Sdim         here:
1154299742Sdim    if (conflict_skel != NULL)
1155299742Sdim      svn_skel__prepend(conflict_skel, *work_item);
1156299742Sdim   */
1157251881Speter  svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1158251881Speter  svn_skel__prepend_str(OP_PREJ_INSTALL, *work_item, result_pool);
1159251881Speter
1160251881Speter  return SVN_NO_ERROR;
1161251881Speter}
1162251881Speter
1163251881Speter
1164251881Speter/* ------------------------------------------------------------------------ */
1165251881Speter
1166251881Speter/* OP_RECORD_FILEINFO  */
1167251881Speter
1168251881Speter
1169251881Speterstatic svn_error_t *
1170251881Speterrun_record_fileinfo(work_item_baton_t *wqb,
1171251881Speter                    svn_wc__db_t *db,
1172251881Speter                    const svn_skel_t *work_item,
1173251881Speter                    const char *wri_abspath,
1174251881Speter                    svn_cancel_func_t cancel_func,
1175251881Speter                    void *cancel_baton,
1176251881Speter                    apr_pool_t *scratch_pool)
1177251881Speter{
1178251881Speter  const svn_skel_t *arg1 = work_item->children->next;
1179251881Speter  const char *local_relpath;
1180251881Speter  const char *local_abspath;
1181251881Speter  apr_time_t set_time = 0;
1182251881Speter
1183251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1184251881Speter
1185251881Speter  SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1186251881Speter                                  local_relpath, scratch_pool, scratch_pool));
1187251881Speter
1188251881Speter  if (arg1->next)
1189251881Speter    {
1190251881Speter      apr_int64_t val;
1191251881Speter
1192251881Speter      SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
1193251881Speter      set_time = (apr_time_t)val;
1194251881Speter    }
1195251881Speter
1196251881Speter  if (set_time != 0)
1197251881Speter    {
1198251881Speter      svn_node_kind_t kind;
1199251881Speter      svn_boolean_t is_special;
1200251881Speter
1201251881Speter      /* Do not set the timestamp on special files. */
1202251881Speter      SVN_ERR(svn_io_check_special_path(local_abspath, &kind, &is_special,
1203251881Speter                                        scratch_pool));
1204251881Speter
1205251881Speter      /* Don't set affected time when local_abspath does not exist or is
1206251881Speter         a special file */
1207251881Speter      if (kind == svn_node_file && !is_special)
1208251881Speter        SVN_ERR(svn_io_set_file_affected_time(set_time, local_abspath,
1209251881Speter                                              scratch_pool));
1210251881Speter
1211251881Speter      /* Note that we can't use the value we get here for recording as the
1212251881Speter         filesystem might have a different timestamp granularity */
1213251881Speter    }
1214251881Speter
1215251881Speter
1216251881Speter  return svn_error_trace(get_and_record_fileinfo(wqb, local_abspath,
1217251881Speter                                                 TRUE /* ignore_enoent */,
1218251881Speter                                                 scratch_pool));
1219251881Speter}
1220251881Speter
1221251881Speter/* ------------------------------------------------------------------------ */
1222251881Speter
1223251881Speter/* OP_TMP_SET_TEXT_CONFLICT_MARKERS  */
1224251881Speter
1225251881Speter
1226251881Speterstatic svn_error_t *
1227251881Speterrun_set_text_conflict_markers(work_item_baton_t *wqb,
1228251881Speter                              svn_wc__db_t *db,
1229251881Speter                              const svn_skel_t *work_item,
1230251881Speter                              const char *wri_abspath,
1231251881Speter                              svn_cancel_func_t cancel_func,
1232251881Speter                              void *cancel_baton,
1233251881Speter                              apr_pool_t *scratch_pool)
1234251881Speter{
1235251881Speter  const svn_skel_t *arg = work_item->children->next;
1236251881Speter  const char *local_relpath;
1237251881Speter  const char *local_abspath;
1238251881Speter  const char *old_abspath = NULL;
1239251881Speter  const char *new_abspath = NULL;
1240251881Speter  const char *wrk_abspath = NULL;
1241251881Speter
1242251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg->data, arg->len);
1243251881Speter  SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1244251881Speter                                  local_relpath, scratch_pool, scratch_pool));
1245251881Speter
1246251881Speter  arg = arg->next;
1247251881Speter  local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1248251881Speter                           : NULL;
1249251881Speter
1250251881Speter  if (local_relpath)
1251251881Speter    {
1252251881Speter      SVN_ERR(svn_wc__db_from_relpath(&old_abspath, db, wri_abspath,
1253251881Speter                                      local_relpath,
1254251881Speter                                      scratch_pool, scratch_pool));
1255251881Speter    }
1256251881Speter
1257251881Speter  arg = arg->next;
1258251881Speter  local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1259251881Speter                           : NULL;
1260251881Speter  if (local_relpath)
1261251881Speter    {
1262251881Speter      SVN_ERR(svn_wc__db_from_relpath(&new_abspath, db, wri_abspath,
1263251881Speter                                      local_relpath,
1264251881Speter                                      scratch_pool, scratch_pool));
1265251881Speter    }
1266251881Speter
1267251881Speter  arg = arg->next;
1268251881Speter  local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1269251881Speter                           : NULL;
1270251881Speter
1271251881Speter  if (local_relpath)
1272251881Speter    {
1273251881Speter      SVN_ERR(svn_wc__db_from_relpath(&wrk_abspath, db, wri_abspath,
1274251881Speter                                      local_relpath,
1275251881Speter                                      scratch_pool, scratch_pool));
1276251881Speter    }
1277251881Speter
1278251881Speter  /* Upgrade scenario: We have a workqueue item that describes how to install a
1279251881Speter     non skel conflict. Fetch all the information we can to create a new style
1280251881Speter     conflict. */
1281251881Speter  /* ### Before format 30 this is/was a common code path as we didn't install
1282251881Speter     ### the conflict directly in the db. It just calls the wc_db code
1283251881Speter     ### to set the right fields. */
1284251881Speter
1285251881Speter  {
1286251881Speter    /* Check if we should combine with a property conflict... */
1287251881Speter    svn_skel_t *conflicts;
1288251881Speter
1289299742Sdim    SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL, db, local_abspath,
1290251881Speter                                     scratch_pool, scratch_pool));
1291251881Speter
1292251881Speter    if (! conflicts)
1293251881Speter      {
1294251881Speter        /* No conflict exists, create a basic skel */
1295251881Speter        conflicts = svn_wc__conflict_skel_create(scratch_pool);
1296251881Speter
1297251881Speter        SVN_ERR(svn_wc__conflict_skel_set_op_update(conflicts, NULL, NULL,
1298251881Speter                                                    scratch_pool,
1299251881Speter                                                    scratch_pool));
1300251881Speter      }
1301251881Speter
1302251881Speter    /* Add the text conflict to the existing onflict */
1303251881Speter    SVN_ERR(svn_wc__conflict_skel_add_text_conflict(conflicts, db,
1304251881Speter                                                    local_abspath,
1305251881Speter                                                    wrk_abspath,
1306251881Speter                                                    old_abspath,
1307251881Speter                                                    new_abspath,
1308251881Speter                                                    scratch_pool,
1309251881Speter                                                    scratch_pool));
1310251881Speter
1311251881Speter    SVN_ERR(svn_wc__db_op_mark_conflict(db, local_abspath, conflicts,
1312251881Speter                                        NULL, scratch_pool));
1313251881Speter  }
1314251881Speter  return SVN_NO_ERROR;
1315251881Speter}
1316251881Speter
1317251881Speter/* ------------------------------------------------------------------------ */
1318251881Speter
1319251881Speter/* OP_TMP_SET_PROPERTY_CONFLICT_MARKER  */
1320251881Speter
1321251881Speterstatic svn_error_t *
1322251881Speterrun_set_property_conflict_marker(work_item_baton_t *wqb,
1323251881Speter                                 svn_wc__db_t *db,
1324251881Speter                                 const svn_skel_t *work_item,
1325251881Speter                                 const char *wri_abspath,
1326251881Speter                                 svn_cancel_func_t cancel_func,
1327251881Speter                                 void *cancel_baton,
1328251881Speter                                 apr_pool_t *scratch_pool)
1329251881Speter{
1330251881Speter  const svn_skel_t *arg = work_item->children->next;
1331251881Speter  const char *local_relpath;
1332251881Speter  const char *local_abspath;
1333251881Speter  const char *prej_abspath = NULL;
1334251881Speter
1335251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg->data, arg->len);
1336251881Speter
1337251881Speter  SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1338251881Speter                                  local_relpath, scratch_pool, scratch_pool));
1339251881Speter
1340251881Speter
1341251881Speter  arg = arg->next;
1342251881Speter  local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1343251881Speter                           : NULL;
1344251881Speter
1345251881Speter  if (local_relpath)
1346251881Speter    SVN_ERR(svn_wc__db_from_relpath(&prej_abspath, db, wri_abspath,
1347251881Speter                                    local_relpath,
1348251881Speter                                    scratch_pool, scratch_pool));
1349251881Speter
1350251881Speter  {
1351251881Speter    /* Check if we should combine with a text conflict... */
1352251881Speter    svn_skel_t *conflicts;
1353251881Speter    apr_hash_t *prop_names;
1354251881Speter
1355299742Sdim    SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL,
1356299742Sdim                                     db, local_abspath,
1357251881Speter                                     scratch_pool, scratch_pool));
1358251881Speter
1359251881Speter    if (! conflicts)
1360251881Speter      {
1361251881Speter        /* No conflict exists, create a basic skel */
1362251881Speter        conflicts = svn_wc__conflict_skel_create(scratch_pool);
1363251881Speter
1364251881Speter        SVN_ERR(svn_wc__conflict_skel_set_op_update(conflicts, NULL, NULL,
1365251881Speter                                                    scratch_pool,
1366251881Speter                                                    scratch_pool));
1367251881Speter      }
1368251881Speter
1369251881Speter    prop_names = apr_hash_make(scratch_pool);
1370251881Speter    SVN_ERR(svn_wc__conflict_skel_add_prop_conflict(conflicts, db,
1371251881Speter                                                    local_abspath,
1372251881Speter                                                    prej_abspath,
1373251881Speter                                                    NULL, NULL, NULL,
1374251881Speter                                                    prop_names,
1375251881Speter                                                    scratch_pool,
1376251881Speter                                                    scratch_pool));
1377251881Speter
1378251881Speter    SVN_ERR(svn_wc__db_op_mark_conflict(db, local_abspath, conflicts,
1379251881Speter                                        NULL, scratch_pool));
1380251881Speter  }
1381251881Speter  return SVN_NO_ERROR;
1382251881Speter}
1383251881Speter
1384251881Speter/* ------------------------------------------------------------------------ */
1385251881Speter
1386251881Speterstatic const struct work_item_dispatch dispatch_table[] = {
1387251881Speter  { OP_FILE_COMMIT, run_file_commit },
1388251881Speter  { OP_FILE_INSTALL, run_file_install },
1389251881Speter  { OP_FILE_REMOVE, run_file_remove },
1390251881Speter  { OP_FILE_MOVE, run_file_move },
1391251881Speter  { OP_FILE_COPY_TRANSLATED, run_file_copy_translated },
1392251881Speter  { OP_SYNC_FILE_FLAGS, run_sync_file_flags },
1393251881Speter  { OP_PREJ_INSTALL, run_prej_install },
1394251881Speter  { OP_DIRECTORY_REMOVE, run_dir_remove },
1395251881Speter  { OP_DIRECTORY_INSTALL, run_dir_install },
1396251881Speter
1397251881Speter  /* Upgrade steps */
1398251881Speter  { OP_POSTUPGRADE, run_postupgrade },
1399251881Speter
1400251881Speter  /* Legacy workqueue items. No longer created */
1401251881Speter  { OP_BASE_REMOVE, run_base_remove },
1402251881Speter  { OP_RECORD_FILEINFO, run_record_fileinfo },
1403251881Speter  { OP_TMP_SET_TEXT_CONFLICT_MARKERS, run_set_text_conflict_markers },
1404251881Speter  { OP_TMP_SET_PROPERTY_CONFLICT_MARKER, run_set_property_conflict_marker },
1405251881Speter
1406251881Speter  /* Sentinel.  */
1407251881Speter  { NULL }
1408251881Speter};
1409251881Speter
1410251881Speterstruct work_item_baton_t
1411251881Speter{
1412251881Speter  apr_pool_t *result_pool; /* Pool to allocate result in */
1413251881Speter
1414251881Speter  svn_boolean_t used; /* needs reset */
1415251881Speter
1416251881Speter  apr_hash_t *record_map; /* const char * -> svn_io_dirent2_t map */
1417251881Speter};
1418251881Speter
1419251881Speter
1420251881Speterstatic svn_error_t *
1421251881Speterdispatch_work_item(work_item_baton_t *wqb,
1422251881Speter                   svn_wc__db_t *db,
1423251881Speter                   const char *wri_abspath,
1424251881Speter                   const svn_skel_t *work_item,
1425251881Speter                   svn_cancel_func_t cancel_func,
1426251881Speter                   void *cancel_baton,
1427251881Speter                   apr_pool_t *scratch_pool)
1428251881Speter{
1429251881Speter  const struct work_item_dispatch *scan;
1430251881Speter
1431251881Speter  /* Scan the dispatch table for a function to handle this work item.  */
1432251881Speter  for (scan = &dispatch_table[0]; scan->name != NULL; ++scan)
1433251881Speter    {
1434251881Speter      if (svn_skel__matches_atom(work_item->children, scan->name))
1435251881Speter        {
1436251881Speter
1437251881Speter#ifdef SVN_DEBUG_WORK_QUEUE
1438251881Speter          SVN_DBG(("dispatch: operation='%s'\n", scan->name));
1439251881Speter#endif
1440251881Speter          SVN_ERR((*scan->func)(wqb, db, work_item, wri_abspath,
1441251881Speter                                cancel_func, cancel_baton,
1442251881Speter                                scratch_pool));
1443251881Speter
1444251881Speter#ifdef SVN_RUN_WORK_QUEUE_TWICE
1445251881Speter#ifdef SVN_DEBUG_WORK_QUEUE
1446251881Speter          SVN_DBG(("dispatch: operation='%s'\n", scan->name));
1447251881Speter#endif
1448251881Speter          /* Being able to run every workqueue item twice is one
1449251881Speter             requirement for workqueues to be restartable. */
1450251881Speter          SVN_ERR((*scan->func)(db, work_item, wri_abspath,
1451251881Speter                                cancel_func, cancel_baton,
1452251881Speter                                scratch_pool));
1453251881Speter#endif
1454251881Speter
1455251881Speter          break;
1456251881Speter        }
1457251881Speter    }
1458251881Speter
1459251881Speter  if (scan->name == NULL)
1460251881Speter    {
1461251881Speter      /* We should know about ALL possible work items here. If we do not,
1462251881Speter         then something is wrong. Most likely, some kind of format/code
1463251881Speter         skew. There is nothing more we can do. Erasing or ignoring this
1464251881Speter         work item could leave the WC in an even more broken state.
1465251881Speter
1466251881Speter         Contrary to issue #1581, we cannot simply remove work items and
1467251881Speter         continue, so bail out with an error.  */
1468251881Speter      return svn_error_createf(SVN_ERR_WC_BAD_ADM_LOG, NULL,
1469251881Speter                               _("Unrecognized work item in the queue"));
1470251881Speter    }
1471251881Speter
1472251881Speter  return SVN_NO_ERROR;
1473251881Speter}
1474251881Speter
1475251881Speter
1476251881Spetersvn_error_t *
1477251881Spetersvn_wc__wq_run(svn_wc__db_t *db,
1478251881Speter               const char *wri_abspath,
1479251881Speter               svn_cancel_func_t cancel_func,
1480251881Speter               void *cancel_baton,
1481251881Speter               apr_pool_t *scratch_pool)
1482251881Speter{
1483251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1484251881Speter  apr_uint64_t last_id = 0;
1485251881Speter  work_item_baton_t wib = { 0 };
1486251881Speter  wib.result_pool = svn_pool_create(scratch_pool);
1487251881Speter
1488251881Speter#ifdef SVN_DEBUG_WORK_QUEUE
1489251881Speter  SVN_DBG(("wq_run: wri='%s'\n", wri_abspath));
1490251881Speter  {
1491251881Speter    static int count = 0;
1492251881Speter    const char *count_env_var = getenv("SVN_DEBUG_WORK_QUEUE");
1493299742Sdim    int count_env_val;
1494251881Speter
1495299742Sdim    SVN_ERR(svn_cstring_atoi(&count_env_val, count_env_var));
1496299742Sdim
1497299742Sdim    if (count_env_var && ++count == count_env_val)
1498251881Speter      return svn_error_create(SVN_ERR_CANCELLED, NULL, "fake cancel");
1499251881Speter  }
1500251881Speter#endif
1501251881Speter
1502251881Speter  while (TRUE)
1503251881Speter    {
1504251881Speter      apr_uint64_t id;
1505251881Speter      svn_skel_t *work_item;
1506251881Speter      svn_error_t *err;
1507251881Speter
1508251881Speter      svn_pool_clear(iterpool);
1509251881Speter
1510251881Speter      if (! wib.used)
1511251881Speter        {
1512251881Speter          /* Make sure to do this *early* in the loop iteration. There may
1513251881Speter             be a LAST_ID that needs to be marked as completed, *before* we
1514251881Speter             start worrying about anything else.  */
1515251881Speter          SVN_ERR(svn_wc__db_wq_fetch_next(&id, &work_item, db, wri_abspath,
1516251881Speter                                           last_id, iterpool, iterpool));
1517251881Speter        }
1518251881Speter      else
1519251881Speter        {
1520251881Speter          /* Make sure to do this *early* in the loop iteration. There may
1521251881Speter             be a LAST_ID that needs to be marked as completed, *before* we
1522251881Speter             start worrying about anything else.  */
1523251881Speter          SVN_ERR(svn_wc__db_wq_record_and_fetch_next(&id, &work_item,
1524251881Speter                                                      db, wri_abspath,
1525251881Speter                                                      last_id, wib.record_map,
1526251881Speter                                                      iterpool,
1527251881Speter                                                      wib.result_pool));
1528251881Speter
1529251881Speter          svn_pool_clear(wib.result_pool);
1530251881Speter          wib.record_map = NULL;
1531251881Speter          wib.used = FALSE;
1532251881Speter        }
1533251881Speter
1534251881Speter      /* Stop work queue processing, if requested. A future 'svn cleanup'
1535251881Speter         should be able to continue the processing. Note that we may
1536251881Speter         have WORK_ITEM, but we'll just skip its processing for now.  */
1537251881Speter      if (cancel_func)
1538251881Speter        SVN_ERR(cancel_func(cancel_baton));
1539251881Speter
1540251881Speter      /* If we have a WORK_ITEM, then process the sucker. Otherwise,
1541251881Speter         we're done.  */
1542251881Speter      if (work_item == NULL)
1543251881Speter        break;
1544251881Speter
1545251881Speter      err = dispatch_work_item(&wib, db, wri_abspath, work_item,
1546251881Speter                               cancel_func, cancel_baton, iterpool);
1547251881Speter      if (err)
1548251881Speter        {
1549251881Speter          const char *skel = svn_skel__unparse(work_item, scratch_pool)->data;
1550251881Speter
1551251881Speter          return svn_error_createf(SVN_ERR_WC_BAD_ADM_LOG, err,
1552251881Speter                                   _("Failed to run the WC DB work queue "
1553251881Speter                                     "associated with '%s', work item %d %s"),
1554251881Speter                                   svn_dirent_local_style(wri_abspath,
1555251881Speter                                                          scratch_pool),
1556251881Speter                                   (int)id, skel);
1557251881Speter        }
1558251881Speter
1559251881Speter      /* The work item finished without error. Mark it completed
1560251881Speter         in the next loop.  */
1561251881Speter      last_id = id;
1562251881Speter    }
1563251881Speter
1564251881Speter  svn_pool_destroy(iterpool);
1565251881Speter  return SVN_NO_ERROR;
1566251881Speter}
1567251881Speter
1568251881Speter
1569251881Spetersvn_skel_t *
1570251881Spetersvn_wc__wq_merge(svn_skel_t *work_item1,
1571251881Speter                 svn_skel_t *work_item2,
1572251881Speter                 apr_pool_t *result_pool)
1573251881Speter{
1574251881Speter  /* If either argument is NULL, then just return the other.  */
1575251881Speter  if (work_item1 == NULL)
1576251881Speter    return work_item2;
1577251881Speter  if (work_item2 == NULL)
1578251881Speter    return work_item1;
1579251881Speter
1580251881Speter  /* We have two items. Figure out how to join them.  */
1581251881Speter  if (SVN_WC__SINGLE_WORK_ITEM(work_item1))
1582251881Speter    {
1583251881Speter      if (SVN_WC__SINGLE_WORK_ITEM(work_item2))
1584251881Speter        {
1585251881Speter          /* Both are singular work items. Construct a list, then put
1586251881Speter             both work items into it (in the proper order).  */
1587251881Speter
1588251881Speter          svn_skel_t *result = svn_skel__make_empty_list(result_pool);
1589251881Speter
1590251881Speter          svn_skel__prepend(work_item2, result);
1591251881Speter          svn_skel__prepend(work_item1, result);
1592251881Speter          return result;
1593251881Speter        }
1594251881Speter
1595251881Speter      /* WORK_ITEM2 is a list of work items. We can simply shove WORK_ITEM1
1596251881Speter         in the front to keep the ordering.  */
1597251881Speter      svn_skel__prepend(work_item1, work_item2);
1598251881Speter      return work_item2;
1599251881Speter    }
1600251881Speter  /* WORK_ITEM1 is a list of work items.  */
1601251881Speter
1602251881Speter  if (SVN_WC__SINGLE_WORK_ITEM(work_item2))
1603251881Speter    {
1604251881Speter      /* Put WORK_ITEM2 onto the end of the WORK_ITEM1 list.  */
1605251881Speter      svn_skel__append(work_item1, work_item2);
1606251881Speter      return work_item1;
1607251881Speter    }
1608251881Speter
1609251881Speter  /* We have two lists of work items. We need to chain all of the work
1610251881Speter     items into one big list. We will leave behind the WORK_ITEM2 skel,
1611251881Speter     as we only want its children.  */
1612251881Speter  svn_skel__append(work_item1, work_item2->children);
1613251881Speter  return work_item1;
1614251881Speter}
1615251881Speter
1616251881Speter
1617251881Speterstatic svn_error_t *
1618251881Speterget_and_record_fileinfo(work_item_baton_t *wqb,
1619251881Speter                        const char *local_abspath,
1620251881Speter                        svn_boolean_t ignore_enoent,
1621251881Speter                        apr_pool_t *scratch_pool)
1622251881Speter{
1623251881Speter  const svn_io_dirent2_t *dirent;
1624251881Speter
1625251881Speter  SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, ignore_enoent,
1626251881Speter                              wqb->result_pool, scratch_pool));
1627251881Speter
1628251881Speter  if (dirent->kind != svn_node_file)
1629251881Speter    return SVN_NO_ERROR;
1630251881Speter
1631251881Speter  wqb->used = TRUE;
1632251881Speter
1633251881Speter  if (! wqb->record_map)
1634251881Speter    wqb->record_map = apr_hash_make(wqb->result_pool);
1635251881Speter
1636251881Speter  svn_hash_sets(wqb->record_map, apr_pstrdup(wqb->result_pool, local_abspath),
1637251881Speter                dirent);
1638251881Speter
1639251881Speter  return SVN_NO_ERROR;
1640251881Speter}
1641