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
26251881Speter#include "svn_types.h"
27251881Speter#include "svn_pools.h"
28251881Speter#include "svn_dirent_uri.h"
29251881Speter#include "svn_subst.h"
30251881Speter#include "svn_hash.h"
31251881Speter#include "svn_io.h"
32251881Speter
33251881Speter#include "wc.h"
34251881Speter#include "wc_db.h"
35251881Speter#include "workqueue.h"
36251881Speter#include "adm_files.h"
37251881Speter#include "conflicts.h"
38251881Speter#include "translate.h"
39251881Speter
40251881Speter#include "svn_private_config.h"
41251881Speter#include "private/svn_skel.h"
42251881Speter
43251881Speter
44251881Speter/* Workqueue operation names.  */
45251881Speter#define OP_FILE_COMMIT "file-commit"
46251881Speter#define OP_FILE_INSTALL "file-install"
47251881Speter#define OP_FILE_REMOVE "file-remove"
48251881Speter#define OP_FILE_MOVE "file-move"
49251881Speter#define OP_FILE_COPY_TRANSLATED "file-translate"
50251881Speter#define OP_SYNC_FILE_FLAGS "sync-file-flags"
51251881Speter#define OP_PREJ_INSTALL "prej-install"
52251881Speter#define OP_DIRECTORY_REMOVE "dir-remove"
53251881Speter#define OP_DIRECTORY_INSTALL "dir-install"
54251881Speter
55251881Speter#define OP_POSTUPGRADE "postupgrade"
56251881Speter
57251881Speter/* Legacy items */
58251881Speter#define OP_BASE_REMOVE "base-remove"
59251881Speter#define OP_RECORD_FILEINFO "record-fileinfo"
60251881Speter#define OP_TMP_SET_TEXT_CONFLICT_MARKERS "tmp-set-text-conflict-markers"
61251881Speter#define OP_TMP_SET_PROPERTY_CONFLICT_MARKER "tmp-set-property-conflict-marker"
62251881Speter
63251881Speter/* For work queue debugging. Generates output about its operation.  */
64251881Speter/* #define SVN_DEBUG_WORK_QUEUE */
65251881Speter
66251881Spetertypedef struct work_item_baton_t work_item_baton_t;
67251881Speter
68251881Speterstruct work_item_dispatch {
69251881Speter  const char *name;
70251881Speter  svn_error_t *(*func)(work_item_baton_t *wqb,
71251881Speter                       svn_wc__db_t *db,
72251881Speter                       const svn_skel_t *work_item,
73251881Speter                       const char *wri_abspath,
74251881Speter                       svn_cancel_func_t cancel_func,
75251881Speter                       void *cancel_baton,
76251881Speter                       apr_pool_t *scratch_pool);
77251881Speter};
78251881Speter
79251881Speter/* Forward definition */
80251881Speterstatic svn_error_t *
81251881Speterget_and_record_fileinfo(work_item_baton_t *wqb,
82251881Speter                        const char *local_abspath,
83251881Speter                        svn_boolean_t ignore_enoent,
84251881Speter                        apr_pool_t *scratch_pool);
85251881Speter
86251881Speter/* ------------------------------------------------------------------------ */
87251881Speter/* OP_REMOVE_BASE  */
88251881Speter
89251881Speter/* Removes a BASE_NODE and all it's data, leaving any adds and copies as is.
90251881Speter   Do this as a depth first traversal to make sure than any parent still exists
91251881Speter   on error conditions.
92251881Speter */
93251881Speter
94251881Speter/* Process the OP_REMOVE_BASE work item WORK_ITEM.
95251881Speter * See svn_wc__wq_build_remove_base() which generates this work item.
96251881Speter * Implements (struct work_item_dispatch).func. */
97251881Speterstatic svn_error_t *
98251881Speterrun_base_remove(work_item_baton_t *wqb,
99251881Speter                svn_wc__db_t *db,
100251881Speter                const svn_skel_t *work_item,
101251881Speter                const char *wri_abspath,
102251881Speter                svn_cancel_func_t cancel_func,
103251881Speter                void *cancel_baton,
104251881Speter                apr_pool_t *scratch_pool)
105251881Speter{
106251881Speter  const svn_skel_t *arg1 = work_item->children->next;
107251881Speter  const char *local_relpath;
108251881Speter  const char *local_abspath;
109251881Speter  svn_revnum_t not_present_rev = SVN_INVALID_REVNUM;
110251881Speter  apr_int64_t val;
111251881Speter
112251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
113251881Speter  SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
114251881Speter                                  local_relpath, scratch_pool, scratch_pool));
115251881Speter  SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
116251881Speter
117251881Speter  if (arg1->next->next)
118251881Speter    {
119251881Speter      not_present_rev = (svn_revnum_t)val;
120251881Speter
121251881Speter      SVN_ERR(svn_skel__parse_int(&val, arg1->next->next, scratch_pool));
122251881Speter    }
123251881Speter  else
124251881Speter    {
125251881Speter      svn_boolean_t keep_not_present;
126251881Speter
127251881Speter      SVN_ERR_ASSERT(SVN_WC__VERSION <= 28); /* Case unused in later versions*/
128251881Speter
129251881Speter      keep_not_present = (val != 0);
130251881Speter
131251881Speter      if (keep_not_present)
132251881Speter        {
133251881Speter          SVN_ERR(svn_wc__db_base_get_info(NULL, NULL,
134251881Speter                                           &not_present_rev, NULL,
135251881Speter                                           NULL, NULL, NULL,
136251881Speter                                           NULL, NULL, NULL, NULL, NULL, NULL,
137251881Speter                                           NULL, NULL, NULL,
138251881Speter                                           db, local_abspath,
139251881Speter                                           scratch_pool, scratch_pool));
140251881Speter        }
141251881Speter    }
142251881Speter
143251881Speter  SVN_ERR(svn_wc__db_base_remove(db, local_abspath,
144251881Speter                                 FALSE /* keep_as_working */,
145251881Speter                                 TRUE /* queue_deletes */,
146253734Speter                                 FALSE /* remove_locks */,
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  const char *adm_path;
399251881Speter  const char *temp_path;
400251881Speter  svn_error_t *err;
401251881Speter
402251881Speter  err = svn_wc__wipe_postupgrade(wri_abspath, FALSE,
403251881Speter                                 cancel_func, cancel_baton, scratch_pool);
404251881Speter  if (err && err->apr_err == SVN_ERR_ENTRY_NOT_FOUND)
405251881Speter    /* No entry, this can happen when the wq item is rerun. */
406251881Speter    svn_error_clear(err);
407251881Speter  else
408251881Speter    SVN_ERR(err);
409251881Speter
410251881Speter  SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, db, wri_abspath,
411251881Speter                                scratch_pool, scratch_pool));
412251881Speter
413251881Speter  adm_path = svn_wc__adm_child(wcroot_abspath, NULL, scratch_pool);
414251881Speter  entries_path = svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_ENTRIES,
415251881Speter                                   scratch_pool);
416251881Speter  format_path = svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_FORMAT,
417251881Speter                                   scratch_pool);
418251881Speter
419251881Speter  /* Write the 'format' and 'entries' files.
420251881Speter
421251881Speter     ### The order may matter for some sufficiently old clients.. but
422251881Speter     ### this code only runs during upgrade after the files had been
423251881Speter     ### removed earlier during the upgrade. */
424251881Speter  SVN_ERR(svn_io_write_unique(&temp_path, adm_path, SVN_WC__NON_ENTRIES_STRING,
425251881Speter                              sizeof(SVN_WC__NON_ENTRIES_STRING) - 1,
426251881Speter                              svn_io_file_del_none, scratch_pool));
427251881Speter  SVN_ERR(svn_io_file_rename(temp_path, format_path, scratch_pool));
428251881Speter
429251881Speter  SVN_ERR(svn_io_write_unique(&temp_path, adm_path, SVN_WC__NON_ENTRIES_STRING,
430251881Speter                              sizeof(SVN_WC__NON_ENTRIES_STRING) - 1,
431251881Speter                              svn_io_file_del_none, scratch_pool));
432251881Speter  SVN_ERR(svn_io_file_rename(temp_path, entries_path, scratch_pool));
433251881Speter
434251881Speter  return SVN_NO_ERROR;
435251881Speter}
436251881Speter
437251881Spetersvn_error_t *
438251881Spetersvn_wc__wq_build_postupgrade(svn_skel_t **work_item,
439251881Speter                             apr_pool_t *result_pool)
440251881Speter{
441251881Speter  *work_item = svn_skel__make_empty_list(result_pool);
442251881Speter
443251881Speter  svn_skel__prepend_str(OP_POSTUPGRADE, *work_item, result_pool);
444251881Speter
445251881Speter  return SVN_NO_ERROR;
446251881Speter}
447251881Speter
448251881Speter/* ------------------------------------------------------------------------ */
449251881Speter
450251881Speter/* OP_FILE_INSTALL */
451251881Speter
452251881Speter/* Process the OP_FILE_INSTALL work item WORK_ITEM.
453251881Speter * See svn_wc__wq_build_file_install() which generates this work item.
454251881Speter * Implements (struct work_item_dispatch).func. */
455251881Speterstatic svn_error_t *
456251881Speterrun_file_install(work_item_baton_t *wqb,
457251881Speter                 svn_wc__db_t *db,
458251881Speter                 const svn_skel_t *work_item,
459251881Speter                 const char *wri_abspath,
460251881Speter                 svn_cancel_func_t cancel_func,
461251881Speter                 void *cancel_baton,
462251881Speter                 apr_pool_t *scratch_pool)
463251881Speter{
464251881Speter  const svn_skel_t *arg1 = work_item->children->next;
465251881Speter  const svn_skel_t *arg4 = arg1->next->next->next;
466251881Speter  const char *local_relpath;
467251881Speter  const char *local_abspath;
468251881Speter  svn_boolean_t use_commit_times;
469251881Speter  svn_boolean_t record_fileinfo;
470251881Speter  svn_boolean_t special;
471251881Speter  svn_stream_t *src_stream;
472251881Speter  svn_subst_eol_style_t style;
473251881Speter  const char *eol;
474251881Speter  apr_hash_t *keywords;
475251881Speter  const char *temp_dir_abspath;
476251881Speter  svn_stream_t *dst_stream;
477251881Speter  const char *dst_abspath;
478251881Speter  apr_int64_t val;
479251881Speter  const char *wcroot_abspath;
480251881Speter  const char *source_abspath;
481251881Speter  const svn_checksum_t *checksum;
482251881Speter  apr_hash_t *props;
483251881Speter  apr_time_t changed_date;
484251881Speter
485251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
486251881Speter  SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
487251881Speter                                  local_relpath, scratch_pool, scratch_pool));
488251881Speter
489251881Speter  SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
490251881Speter  use_commit_times = (val != 0);
491251881Speter  SVN_ERR(svn_skel__parse_int(&val, arg1->next->next, scratch_pool));
492251881Speter  record_fileinfo = (val != 0);
493251881Speter
494251881Speter  SVN_ERR(svn_wc__db_read_node_install_info(&wcroot_abspath,
495251881Speter                                            &checksum, &props,
496251881Speter                                            &changed_date,
497251881Speter                                            db, local_abspath, wri_abspath,
498251881Speter                                            scratch_pool, scratch_pool));
499251881Speter
500251881Speter  if (arg4 != NULL)
501251881Speter    {
502251881Speter      /* Use the provided path for the source.  */
503251881Speter      local_relpath = apr_pstrmemdup(scratch_pool, arg4->data, arg4->len);
504251881Speter      SVN_ERR(svn_wc__db_from_relpath(&source_abspath, db, wri_abspath,
505251881Speter                                      local_relpath,
506251881Speter                                      scratch_pool, scratch_pool));
507251881Speter    }
508251881Speter  else if (! checksum)
509251881Speter    {
510251881Speter      /* This error replaces a previous assertion. Reporting an error from here
511251881Speter         leaves the workingqueue operation in place, so the working copy is
512251881Speter         still broken!
513251881Speter
514251881Speter         But when we report this error the user at least knows what node has
515251881Speter         this specific problem, so maybe we can find out why users see this
516251881Speter         error */
517251881Speter      return svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
518251881Speter                               _("Can't install '%s' from pristine store, "
519251881Speter                                 "because no checksum is recorded for this "
520251881Speter                                 "file"),
521251881Speter                               svn_dirent_local_style(local_abspath,
522251881Speter                                                      scratch_pool));
523251881Speter    }
524251881Speter  else
525251881Speter    {
526251881Speter      SVN_ERR(svn_wc__db_pristine_get_future_path(&source_abspath,
527251881Speter                                                  wcroot_abspath,
528251881Speter                                                  checksum,
529251881Speter                                                  scratch_pool, scratch_pool));
530251881Speter    }
531251881Speter
532251881Speter  SVN_ERR(svn_stream_open_readonly(&src_stream, source_abspath,
533251881Speter                                   scratch_pool, scratch_pool));
534251881Speter
535251881Speter  /* Fetch all the translation bits.  */
536251881Speter  SVN_ERR(svn_wc__get_translate_info(&style, &eol,
537251881Speter                                     &keywords,
538251881Speter                                     &special, db, local_abspath,
539251881Speter                                     props, FALSE,
540251881Speter                                     scratch_pool, scratch_pool));
541251881Speter  if (special)
542251881Speter    {
543251881Speter      /* When this stream is closed, the resulting special file will
544251881Speter         atomically be created/moved into place at LOCAL_ABSPATH.  */
545251881Speter      SVN_ERR(svn_subst_create_specialfile(&dst_stream, local_abspath,
546251881Speter                                           scratch_pool, scratch_pool));
547251881Speter
548251881Speter      /* Copy the "repository normal" form of the special file into the
549251881Speter         special stream.  */
550251881Speter      SVN_ERR(svn_stream_copy3(src_stream, dst_stream,
551251881Speter                               cancel_func, cancel_baton,
552251881Speter                               scratch_pool));
553251881Speter
554251881Speter      /* No need to set exec or read-only flags on special files.  */
555251881Speter
556251881Speter      /* ### Shouldn't this record a timestamp and size, etc.? */
557251881Speter      return SVN_NO_ERROR;
558251881Speter    }
559251881Speter
560251881Speter  if (svn_subst_translation_required(style, eol, keywords,
561251881Speter                                     FALSE /* special */,
562251881Speter                                     TRUE /* force_eol_check */))
563251881Speter    {
564251881Speter      /* Wrap it in a translating (expanding) stream.  */
565251881Speter      src_stream = svn_subst_stream_translated(src_stream, eol,
566251881Speter                                               TRUE /* repair */,
567251881Speter                                               keywords,
568251881Speter                                               TRUE /* expand */,
569251881Speter                                               scratch_pool);
570251881Speter    }
571251881Speter
572251881Speter  /* Where is the Right Place to put a temp file in this working copy?  */
573251881Speter  SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath,
574251881Speter                                         db, wcroot_abspath,
575251881Speter                                         scratch_pool, scratch_pool));
576251881Speter
577251881Speter  /* Translate to a temporary file. We don't want the user seeing a partial
578251881Speter     file, nor let them muck with it while we translate. We may also need to
579251881Speter     get its TRANSLATED_SIZE before the user can monkey it.  */
580251881Speter  SVN_ERR(svn_stream_open_unique(&dst_stream, &dst_abspath,
581251881Speter                                 temp_dir_abspath,
582251881Speter                                 svn_io_file_del_none,
583251881Speter                                 scratch_pool, scratch_pool));
584251881Speter
585251881Speter  /* Copy from the source to the dest, translating as we go. This will also
586251881Speter     close both streams.  */
587251881Speter  SVN_ERR(svn_stream_copy3(src_stream, dst_stream,
588251881Speter                           cancel_func, cancel_baton,
589251881Speter                           scratch_pool));
590251881Speter
591251881Speter  /* All done. Move the file into place.  */
592251881Speter
593251881Speter  {
594251881Speter    svn_error_t *err;
595251881Speter
596251881Speter    err = svn_io_file_rename(dst_abspath, local_abspath, scratch_pool);
597251881Speter
598251881Speter    /* With a single db we might want to install files in a missing directory.
599251881Speter       Simply trying this scenario on error won't do any harm and at least
600251881Speter       one user reported this problem on IRC. */
601251881Speter    if (err && APR_STATUS_IS_ENOENT(err->apr_err))
602251881Speter      {
603251881Speter        svn_error_t *err2;
604251881Speter
605251881Speter        err2 = svn_io_make_dir_recursively(svn_dirent_dirname(local_abspath,
606251881Speter                                                              scratch_pool),
607251881Speter                                           scratch_pool);
608251881Speter
609251881Speter        if (err2)
610251881Speter          /* Creating directory didn't work: Return all errors */
611251881Speter          return svn_error_trace(svn_error_compose_create(err, err2));
612251881Speter        else
613251881Speter          /* We could create a directory: retry install */
614251881Speter          svn_error_clear(err);
615251881Speter
616251881Speter        SVN_ERR(svn_io_file_rename(dst_abspath, local_abspath, scratch_pool));
617251881Speter      }
618251881Speter    else
619251881Speter      SVN_ERR(err);
620251881Speter  }
621251881Speter
622251881Speter  /* Tweak the on-disk file according to its properties.  */
623251881Speter#ifndef WIN32
624251881Speter  if (props && svn_hash_gets(props, SVN_PROP_EXECUTABLE))
625251881Speter    SVN_ERR(svn_io_set_file_executable(local_abspath, TRUE, FALSE,
626251881Speter                                       scratch_pool));
627251881Speter#endif
628251881Speter
629251881Speter  /* Note that this explicitly checks the pristine properties, to make sure
630251881Speter     that when the lock is locally set (=modification) it is not read only */
631251881Speter  if (props && svn_hash_gets(props, SVN_PROP_NEEDS_LOCK))
632251881Speter    {
633251881Speter      svn_wc__db_status_t status;
634251881Speter      svn_wc__db_lock_t *lock;
635251881Speter      SVN_ERR(svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL,
636251881Speter                                   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
637251881Speter                                   NULL, NULL, &lock, NULL, NULL, NULL, NULL,
638251881Speter                                   NULL, NULL, NULL, NULL, NULL, NULL,
639251881Speter                                   db, local_abspath,
640251881Speter                                   scratch_pool, scratch_pool));
641251881Speter
642251881Speter      if (!lock && status != svn_wc__db_status_added)
643251881Speter        SVN_ERR(svn_io_set_file_read_only(local_abspath, FALSE, scratch_pool));
644251881Speter    }
645251881Speter
646251881Speter  if (use_commit_times)
647251881Speter    {
648251881Speter      if (changed_date)
649251881Speter        SVN_ERR(svn_io_set_file_affected_time(changed_date,
650251881Speter                                              local_abspath,
651251881Speter                                              scratch_pool));
652251881Speter    }
653251881Speter
654251881Speter  /* ### this should happen before we rename the file into place.  */
655251881Speter  if (record_fileinfo)
656251881Speter    {
657251881Speter      SVN_ERR(get_and_record_fileinfo(wqb, local_abspath,
658251881Speter                                      FALSE /* ignore_enoent */,
659251881Speter                                      scratch_pool));
660251881Speter    }
661251881Speter
662251881Speter  return SVN_NO_ERROR;
663251881Speter}
664251881Speter
665251881Speter
666251881Spetersvn_error_t *
667251881Spetersvn_wc__wq_build_file_install(svn_skel_t **work_item,
668251881Speter                              svn_wc__db_t *db,
669251881Speter                              const char *local_abspath,
670251881Speter                              const char *source_abspath,
671251881Speter                              svn_boolean_t use_commit_times,
672251881Speter                              svn_boolean_t record_fileinfo,
673251881Speter                              apr_pool_t *result_pool,
674251881Speter                              apr_pool_t *scratch_pool)
675251881Speter{
676251881Speter  const char *local_relpath;
677251881Speter  const char *wri_abspath;
678251881Speter  *work_item = svn_skel__make_empty_list(result_pool);
679251881Speter
680251881Speter  /* Use the directory of the file to install as wri_abspath to avoid
681251881Speter     filestats on just obtaining the wc-root */
682251881Speter  wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
683251881Speter
684251881Speter  /* If a SOURCE_ABSPATH was provided, then put it into the skel. If this
685251881Speter     value is not provided, then the file's pristine contents will be used. */
686251881Speter  if (source_abspath != NULL)
687251881Speter    {
688251881Speter      SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
689251881Speter                                    source_abspath,
690251881Speter                                    result_pool, scratch_pool));
691251881Speter
692251881Speter      svn_skel__prepend_str(local_relpath, *work_item, result_pool);
693251881Speter    }
694251881Speter
695251881Speter  SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
696251881Speter                                local_abspath, result_pool, scratch_pool));
697251881Speter
698251881Speter  svn_skel__prepend_int(record_fileinfo, *work_item, result_pool);
699251881Speter  svn_skel__prepend_int(use_commit_times, *work_item, result_pool);
700251881Speter  svn_skel__prepend_str(local_relpath, *work_item, result_pool);
701251881Speter  svn_skel__prepend_str(OP_FILE_INSTALL, *work_item, result_pool);
702251881Speter
703251881Speter  return SVN_NO_ERROR;
704251881Speter}
705251881Speter
706251881Speter
707251881Speter/* ------------------------------------------------------------------------ */
708251881Speter
709251881Speter/* OP_FILE_REMOVE  */
710251881Speter
711251881Speter/* Process the OP_FILE_REMOVE work item WORK_ITEM.
712251881Speter * See svn_wc__wq_build_file_remove() which generates this work item.
713251881Speter * Implements (struct work_item_dispatch).func. */
714251881Speterstatic svn_error_t *
715251881Speterrun_file_remove(work_item_baton_t *wqb,
716251881Speter                svn_wc__db_t *db,
717251881Speter                const svn_skel_t *work_item,
718251881Speter                const char *wri_abspath,
719251881Speter                svn_cancel_func_t cancel_func,
720251881Speter                void *cancel_baton,
721251881Speter                apr_pool_t *scratch_pool)
722251881Speter{
723251881Speter  const svn_skel_t *arg1 = work_item->children->next;
724251881Speter  const char *local_relpath;
725251881Speter  const char *local_abspath;
726251881Speter
727251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
728251881Speter  SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
729251881Speter                                  local_relpath, scratch_pool, scratch_pool));
730251881Speter
731251881Speter  /* Remove the path, no worrying if it isn't there.  */
732251881Speter  return svn_error_trace(svn_io_remove_file2(local_abspath, TRUE,
733251881Speter                                             scratch_pool));
734251881Speter}
735251881Speter
736251881Speter
737251881Spetersvn_error_t *
738251881Spetersvn_wc__wq_build_file_remove(svn_skel_t **work_item,
739251881Speter                             svn_wc__db_t *db,
740251881Speter                             const char *wri_abspath,
741251881Speter                             const char *local_abspath,
742251881Speter                             apr_pool_t *result_pool,
743251881Speter                             apr_pool_t *scratch_pool)
744251881Speter{
745251881Speter  const char *local_relpath;
746251881Speter  *work_item = svn_skel__make_empty_list(result_pool);
747251881Speter
748251881Speter  SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
749251881Speter                                local_abspath, result_pool, scratch_pool));
750251881Speter
751251881Speter  svn_skel__prepend_str(local_relpath, *work_item, result_pool);
752251881Speter  svn_skel__prepend_str(OP_FILE_REMOVE, *work_item, result_pool);
753251881Speter
754251881Speter  return SVN_NO_ERROR;
755251881Speter}
756251881Speter
757251881Speter/* ------------------------------------------------------------------------ */
758251881Speter
759251881Speter/* OP_DIRECTORY_REMOVE  */
760251881Speter
761251881Speter/* Process the OP_FILE_REMOVE work item WORK_ITEM.
762251881Speter * See svn_wc__wq_build_file_remove() which generates this work item.
763251881Speter * Implements (struct work_item_dispatch).func. */
764251881Speterstatic svn_error_t *
765251881Speterrun_dir_remove(work_item_baton_t *wqb,
766251881Speter               svn_wc__db_t *db,
767251881Speter               const svn_skel_t *work_item,
768251881Speter               const char *wri_abspath,
769251881Speter               svn_cancel_func_t cancel_func,
770251881Speter               void *cancel_baton,
771251881Speter               apr_pool_t *scratch_pool)
772251881Speter{
773251881Speter  const svn_skel_t *arg1 = work_item->children->next;
774251881Speter  const char *local_relpath;
775251881Speter  const char *local_abspath;
776251881Speter  svn_boolean_t recursive;
777251881Speter
778251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
779251881Speter  SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
780251881Speter                                  local_relpath, scratch_pool, scratch_pool));
781251881Speter
782251881Speter  recursive = FALSE;
783251881Speter  if (arg1->next)
784251881Speter    {
785251881Speter      apr_int64_t val;
786251881Speter      SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
787251881Speter
788251881Speter      recursive = (val != 0);
789251881Speter    }
790251881Speter
791251881Speter  /* Remove the path, no worrying if it isn't there.  */
792251881Speter  if (recursive)
793251881Speter    return svn_error_trace(
794251881Speter                svn_io_remove_dir2(local_abspath, TRUE,
795251881Speter                                   cancel_func, cancel_baton,
796251881Speter                                   scratch_pool));
797251881Speter  else
798251881Speter    {
799251881Speter      svn_error_t *err;
800251881Speter
801251881Speter      err = svn_io_dir_remove_nonrecursive(local_abspath, scratch_pool);
802251881Speter
803251881Speter      if (err && (APR_STATUS_IS_ENOENT(err->apr_err)
804251881Speter                  || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)
805251881Speter                  || APR_STATUS_IS_ENOTEMPTY(err->apr_err)))
806251881Speter        {
807251881Speter          svn_error_clear(err);
808251881Speter          err = NULL;
809251881Speter        }
810251881Speter
811251881Speter      return svn_error_trace(err);
812251881Speter    }
813251881Speter}
814251881Speter
815251881Spetersvn_error_t *
816251881Spetersvn_wc__wq_build_dir_remove(svn_skel_t **work_item,
817251881Speter                            svn_wc__db_t *db,
818251881Speter                            const char *wri_abspath,
819251881Speter                            const char *local_abspath,
820251881Speter                            svn_boolean_t recursive,
821251881Speter                            apr_pool_t *result_pool,
822251881Speter                            apr_pool_t *scratch_pool)
823251881Speter{
824251881Speter  const char *local_relpath;
825251881Speter  *work_item = svn_skel__make_empty_list(result_pool);
826251881Speter
827251881Speter  SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
828251881Speter                                local_abspath, result_pool, scratch_pool));
829251881Speter
830251881Speter  if (recursive)
831251881Speter    svn_skel__prepend_int(TRUE, *work_item, result_pool);
832251881Speter
833251881Speter  svn_skel__prepend_str(local_relpath, *work_item, result_pool);
834251881Speter  svn_skel__prepend_str(OP_DIRECTORY_REMOVE, *work_item, result_pool);
835251881Speter
836251881Speter  return SVN_NO_ERROR;
837251881Speter}
838251881Speter
839251881Speter/* ------------------------------------------------------------------------ */
840251881Speter
841251881Speter/* OP_FILE_MOVE  */
842251881Speter
843251881Speter/* Process the OP_FILE_MOVE work item WORK_ITEM.
844251881Speter * See svn_wc__wq_build_file_move() which generates this work item.
845251881Speter * Implements (struct work_item_dispatch).func. */
846251881Speterstatic svn_error_t *
847251881Speterrun_file_move(work_item_baton_t *wqb,
848251881Speter              svn_wc__db_t *db,
849251881Speter              const svn_skel_t *work_item,
850251881Speter              const char *wri_abspath,
851251881Speter              svn_cancel_func_t cancel_func,
852251881Speter              void *cancel_baton,
853251881Speter              apr_pool_t *scratch_pool)
854251881Speter{
855251881Speter  const svn_skel_t *arg1 = work_item->children->next;
856251881Speter  const char *src_abspath, *dst_abspath;
857251881Speter  const char *local_relpath;
858251881Speter  svn_error_t *err;
859251881Speter
860251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
861251881Speter  SVN_ERR(svn_wc__db_from_relpath(&src_abspath, db, wri_abspath, local_relpath,
862251881Speter                                  scratch_pool, scratch_pool));
863251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->data,
864251881Speter                                 arg1->next->len);
865251881Speter  SVN_ERR(svn_wc__db_from_relpath(&dst_abspath, db, wri_abspath, local_relpath,
866251881Speter                                  scratch_pool, scratch_pool));
867251881Speter
868251881Speter  /* Use svn_io_file_move() instead of svn_io_file_rename() to allow cross
869251881Speter     device copies. We should not fail in the workqueue. */
870251881Speter
871251881Speter  err = svn_io_file_move(src_abspath, dst_abspath, scratch_pool);
872251881Speter
873251881Speter  /* If the source is not found, we assume the wq op is already handled */
874251881Speter  if (err && APR_STATUS_IS_ENOENT(err->apr_err))
875251881Speter    svn_error_clear(err);
876251881Speter  else
877251881Speter    SVN_ERR(err);
878251881Speter
879251881Speter  return SVN_NO_ERROR;
880251881Speter}
881251881Speter
882251881Speter
883251881Spetersvn_error_t *
884251881Spetersvn_wc__wq_build_file_move(svn_skel_t **work_item,
885251881Speter                           svn_wc__db_t *db,
886251881Speter                           const char *wri_abspath,
887251881Speter                           const char *src_abspath,
888251881Speter                           const char *dst_abspath,
889251881Speter                           apr_pool_t *result_pool,
890251881Speter                           apr_pool_t *scratch_pool)
891251881Speter{
892251881Speter  svn_node_kind_t kind;
893251881Speter  const char *local_relpath;
894251881Speter  *work_item = svn_skel__make_empty_list(result_pool);
895251881Speter
896251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
897251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
898251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
899251881Speter
900251881Speter  /* File must exist */
901251881Speter  SVN_ERR(svn_io_check_path(src_abspath, &kind, result_pool));
902251881Speter
903251881Speter  if (kind == svn_node_none)
904251881Speter    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
905251881Speter                             _("'%s' not found"),
906251881Speter                             svn_dirent_local_style(src_abspath,
907251881Speter                                                    scratch_pool));
908251881Speter
909251881Speter  SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath, dst_abspath,
910251881Speter                                result_pool, scratch_pool));
911251881Speter  svn_skel__prepend_str(local_relpath, *work_item, result_pool);
912251881Speter
913251881Speter  SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath, src_abspath,
914251881Speter                                result_pool, scratch_pool));
915251881Speter  svn_skel__prepend_str(local_relpath, *work_item, result_pool);
916251881Speter
917251881Speter  svn_skel__prepend_str(OP_FILE_MOVE, *work_item, result_pool);
918251881Speter
919251881Speter  return SVN_NO_ERROR;
920251881Speter}
921251881Speter
922251881Speter/* ------------------------------------------------------------------------ */
923251881Speter
924251881Speter/* OP_FILE_COPY_TRANSLATED */
925251881Speter
926251881Speter/* Process the OP_FILE_COPY_TRANSLATED work item WORK_ITEM.
927251881Speter * See run_file_copy_translated() which generates this work item.
928251881Speter * Implements (struct work_item_dispatch).func. */
929251881Speterstatic svn_error_t *
930251881Speterrun_file_copy_translated(work_item_baton_t *wqb,
931251881Speter                         svn_wc__db_t *db,
932251881Speter                         const svn_skel_t *work_item,
933251881Speter                         const char *wri_abspath,
934251881Speter                         svn_cancel_func_t cancel_func,
935251881Speter                         void *cancel_baton,
936251881Speter                         apr_pool_t *scratch_pool)
937251881Speter{
938251881Speter  const svn_skel_t *arg1 = work_item->children->next;
939251881Speter  const char *local_abspath, *src_abspath, *dst_abspath;
940251881Speter  const char *local_relpath;
941251881Speter  svn_subst_eol_style_t style;
942251881Speter  const char *eol;
943251881Speter  apr_hash_t *keywords;
944251881Speter  svn_boolean_t special;
945251881Speter
946251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
947251881Speter  SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
948251881Speter                                  local_relpath, scratch_pool, scratch_pool));
949251881Speter
950251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->data,
951251881Speter                               arg1->next->len);
952251881Speter  SVN_ERR(svn_wc__db_from_relpath(&src_abspath, db, wri_abspath,
953251881Speter                                  local_relpath, scratch_pool, scratch_pool));
954251881Speter
955251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->next->data,
956251881Speter                                arg1->next->next->len);
957251881Speter  SVN_ERR(svn_wc__db_from_relpath(&dst_abspath, db, wri_abspath,
958251881Speter                                  local_relpath, scratch_pool, scratch_pool));
959251881Speter
960251881Speter  SVN_ERR(svn_wc__get_translate_info(&style, &eol,
961251881Speter                                     &keywords,
962251881Speter                                     &special,
963251881Speter                                     db, local_abspath, NULL, FALSE,
964251881Speter                                     scratch_pool, scratch_pool));
965251881Speter
966251881Speter  SVN_ERR(svn_subst_copy_and_translate4(src_abspath, dst_abspath,
967251881Speter                                        eol, TRUE /* repair */,
968251881Speter                                        keywords, TRUE /* expand */,
969251881Speter                                        special,
970251881Speter                                        cancel_func, cancel_baton,
971251881Speter                                        scratch_pool));
972251881Speter  return SVN_NO_ERROR;
973251881Speter}
974251881Speter
975251881Speter
976251881Spetersvn_error_t *
977251881Spetersvn_wc__wq_build_file_copy_translated(svn_skel_t **work_item,
978251881Speter                                      svn_wc__db_t *db,
979251881Speter                                      const char *local_abspath,
980251881Speter                                      const char *src_abspath,
981251881Speter                                      const char *dst_abspath,
982251881Speter                                      apr_pool_t *result_pool,
983251881Speter                                      apr_pool_t *scratch_pool)
984251881Speter{
985251881Speter  svn_node_kind_t kind;
986251881Speter  const char *local_relpath;
987251881Speter
988251881Speter  *work_item = svn_skel__make_empty_list(result_pool);
989251881Speter
990251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
991251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
992251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
993251881Speter
994251881Speter  /* File must exist */
995251881Speter  SVN_ERR(svn_io_check_path(src_abspath, &kind, result_pool));
996251881Speter
997251881Speter  if (kind == svn_node_none)
998251881Speter    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
999251881Speter                             _("'%s' not found"),
1000251881Speter                             svn_dirent_local_style(src_abspath,
1001251881Speter                                                    scratch_pool));
1002251881Speter
1003251881Speter  SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, dst_abspath,
1004251881Speter                                result_pool, scratch_pool));
1005251881Speter  svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1006251881Speter
1007251881Speter  SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, src_abspath,
1008251881Speter                                result_pool, scratch_pool));
1009251881Speter  svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1010251881Speter
1011251881Speter  SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
1012251881Speter                                local_abspath, result_pool, scratch_pool));
1013251881Speter  svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1014251881Speter
1015251881Speter  svn_skel__prepend_str(OP_FILE_COPY_TRANSLATED, *work_item, result_pool);
1016251881Speter
1017251881Speter  return SVN_NO_ERROR;
1018251881Speter}
1019251881Speter
1020251881Speter/* ------------------------------------------------------------------------ */
1021251881Speter
1022251881Speter/* OP_DIRECTORY_INSTALL  */
1023251881Speter
1024251881Speterstatic svn_error_t *
1025251881Speterrun_dir_install(work_item_baton_t *wqb,
1026251881Speter                svn_wc__db_t *db,
1027251881Speter                const svn_skel_t *work_item,
1028251881Speter                const char *wri_abspath,
1029251881Speter                svn_cancel_func_t cancel_func,
1030251881Speter                void *cancel_baton,
1031251881Speter                apr_pool_t *scratch_pool)
1032251881Speter{
1033251881Speter  const svn_skel_t *arg1 = work_item->children->next;
1034251881Speter  const char *local_relpath;
1035251881Speter  const char *local_abspath;
1036251881Speter
1037251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1038251881Speter  SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1039251881Speter                                  local_relpath, scratch_pool, scratch_pool));
1040251881Speter
1041251881Speter  SVN_ERR(svn_wc__ensure_directory(local_abspath, scratch_pool));
1042251881Speter
1043251881Speter  return SVN_NO_ERROR;
1044251881Speter}
1045251881Speter
1046251881Spetersvn_error_t *
1047251881Spetersvn_wc__wq_build_dir_install(svn_skel_t **work_item,
1048251881Speter                             svn_wc__db_t *db,
1049251881Speter                             const char *local_abspath,
1050251881Speter                             apr_pool_t *scratch_pool,
1051251881Speter                             apr_pool_t *result_pool)
1052251881Speter{
1053251881Speter  const char *local_relpath;
1054251881Speter
1055251881Speter  *work_item = svn_skel__make_empty_list(result_pool);
1056251881Speter
1057251881Speter  SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
1058251881Speter                                local_abspath, result_pool, scratch_pool));
1059251881Speter  svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1060251881Speter
1061251881Speter  svn_skel__prepend_str(OP_DIRECTORY_INSTALL, *work_item, result_pool);
1062251881Speter
1063251881Speter  return SVN_NO_ERROR;
1064251881Speter}
1065251881Speter
1066251881Speter
1067251881Speter/* ------------------------------------------------------------------------ */
1068251881Speter
1069251881Speter/* OP_SYNC_FILE_FLAGS  */
1070251881Speter
1071251881Speter/* Process the OP_SYNC_FILE_FLAGS work item WORK_ITEM.
1072251881Speter * See svn_wc__wq_build_sync_file_flags() which generates this work item.
1073251881Speter * Implements (struct work_item_dispatch).func. */
1074251881Speterstatic svn_error_t *
1075251881Speterrun_sync_file_flags(work_item_baton_t *wqb,
1076251881Speter                    svn_wc__db_t *db,
1077251881Speter                    const svn_skel_t *work_item,
1078251881Speter                    const char *wri_abspath,
1079251881Speter                    svn_cancel_func_t cancel_func,
1080251881Speter                    void *cancel_baton,
1081251881Speter                    apr_pool_t *scratch_pool)
1082251881Speter{
1083251881Speter  const svn_skel_t *arg1 = work_item->children->next;
1084251881Speter  const char *local_relpath;
1085251881Speter  const char *local_abspath;
1086251881Speter
1087251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1088251881Speter  SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1089251881Speter                                  local_relpath, scratch_pool, scratch_pool));
1090251881Speter
1091251881Speter  return svn_error_trace(svn_wc__sync_flags_with_props(NULL, db,
1092251881Speter                                            local_abspath, scratch_pool));
1093251881Speter}
1094251881Speter
1095251881Speter
1096251881Spetersvn_error_t *
1097251881Spetersvn_wc__wq_build_sync_file_flags(svn_skel_t **work_item,
1098251881Speter                                 svn_wc__db_t *db,
1099251881Speter                                 const char *local_abspath,
1100251881Speter                                 apr_pool_t *result_pool,
1101251881Speter                                 apr_pool_t *scratch_pool)
1102251881Speter{
1103251881Speter  const char *local_relpath;
1104251881Speter  *work_item = svn_skel__make_empty_list(result_pool);
1105251881Speter
1106251881Speter  SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
1107251881Speter                                local_abspath, result_pool, scratch_pool));
1108251881Speter
1109251881Speter  svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1110251881Speter  svn_skel__prepend_str(OP_SYNC_FILE_FLAGS, *work_item, result_pool);
1111251881Speter
1112251881Speter  return SVN_NO_ERROR;
1113251881Speter}
1114251881Speter
1115251881Speter
1116251881Speter/* ------------------------------------------------------------------------ */
1117251881Speter
1118251881Speter/* OP_PREJ_INSTALL  */
1119251881Speter
1120251881Speterstatic svn_error_t *
1121251881Speterrun_prej_install(work_item_baton_t *wqb,
1122251881Speter                 svn_wc__db_t *db,
1123251881Speter                 const svn_skel_t *work_item,
1124251881Speter                 const char *wri_abspath,
1125251881Speter                 svn_cancel_func_t cancel_func,
1126251881Speter                 void *cancel_baton,
1127251881Speter                 apr_pool_t *scratch_pool)
1128251881Speter{
1129251881Speter  const svn_skel_t *arg1 = work_item->children->next;
1130251881Speter  const char *local_relpath;
1131251881Speter  const char *local_abspath;
1132251881Speter  svn_skel_t *conflicts;
1133251881Speter  const svn_skel_t *prop_conflict_skel;
1134251881Speter  const char *tmp_prejfile_abspath;
1135251881Speter  const char *prejfile_abspath;
1136251881Speter
1137251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1138251881Speter  SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1139251881Speter                                  local_relpath, scratch_pool, scratch_pool));
1140251881Speter
1141251881Speter  SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath,
1142251881Speter                                   scratch_pool, scratch_pool));
1143251881Speter
1144251881Speter  SVN_ERR(svn_wc__conflict_read_prop_conflict(&prejfile_abspath,
1145251881Speter                                              NULL, NULL, NULL, NULL,
1146251881Speter                                              db, local_abspath, conflicts,
1147251881Speter                                              scratch_pool, scratch_pool));
1148251881Speter
1149251881Speter  if (arg1->next != NULL)
1150251881Speter    prop_conflict_skel = arg1->next;
1151251881Speter  else
1152251881Speter    SVN_ERR_MALFUNCTION();  /* ### wc_db can't provide it ... yet.  */
1153251881Speter
1154251881Speter  /* Construct a property reject file in the temporary area.  */
1155251881Speter  SVN_ERR(svn_wc__create_prejfile(&tmp_prejfile_abspath,
1156251881Speter                                  db, local_abspath,
1157251881Speter                                  prop_conflict_skel,
1158251881Speter                                  scratch_pool, scratch_pool));
1159251881Speter
1160251881Speter  /* ... and atomically move it into place.  */
1161251881Speter  SVN_ERR(svn_io_file_rename(tmp_prejfile_abspath,
1162251881Speter                             prejfile_abspath,
1163251881Speter                             scratch_pool));
1164251881Speter
1165251881Speter  return SVN_NO_ERROR;
1166251881Speter}
1167251881Speter
1168251881Speter
1169251881Spetersvn_error_t *
1170251881Spetersvn_wc__wq_build_prej_install(svn_skel_t **work_item,
1171251881Speter                              svn_wc__db_t *db,
1172251881Speter                              const char *local_abspath,
1173251881Speter                              svn_skel_t *conflict_skel,
1174251881Speter                              apr_pool_t *result_pool,
1175251881Speter                              apr_pool_t *scratch_pool)
1176251881Speter{
1177251881Speter  const char *local_relpath;
1178251881Speter  *work_item = svn_skel__make_empty_list(result_pool);
1179251881Speter
1180251881Speter  /* ### gotta have this, today  */
1181251881Speter  SVN_ERR_ASSERT(conflict_skel != NULL);
1182251881Speter
1183251881Speter  SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
1184251881Speter                                local_abspath, result_pool, scratch_pool));
1185251881Speter
1186251881Speter  if (conflict_skel != NULL)
1187251881Speter    svn_skel__prepend(conflict_skel, *work_item);
1188251881Speter  svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1189251881Speter  svn_skel__prepend_str(OP_PREJ_INSTALL, *work_item, result_pool);
1190251881Speter
1191251881Speter  return SVN_NO_ERROR;
1192251881Speter}
1193251881Speter
1194251881Speter
1195251881Speter/* ------------------------------------------------------------------------ */
1196251881Speter
1197251881Speter/* OP_RECORD_FILEINFO  */
1198251881Speter
1199251881Speter
1200251881Speterstatic svn_error_t *
1201251881Speterrun_record_fileinfo(work_item_baton_t *wqb,
1202251881Speter                    svn_wc__db_t *db,
1203251881Speter                    const svn_skel_t *work_item,
1204251881Speter                    const char *wri_abspath,
1205251881Speter                    svn_cancel_func_t cancel_func,
1206251881Speter                    void *cancel_baton,
1207251881Speter                    apr_pool_t *scratch_pool)
1208251881Speter{
1209251881Speter  const svn_skel_t *arg1 = work_item->children->next;
1210251881Speter  const char *local_relpath;
1211251881Speter  const char *local_abspath;
1212251881Speter  apr_time_t set_time = 0;
1213251881Speter
1214251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1215251881Speter
1216251881Speter  SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1217251881Speter                                  local_relpath, scratch_pool, scratch_pool));
1218251881Speter
1219251881Speter  if (arg1->next)
1220251881Speter    {
1221251881Speter      apr_int64_t val;
1222251881Speter
1223251881Speter      SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
1224251881Speter      set_time = (apr_time_t)val;
1225251881Speter    }
1226251881Speter
1227251881Speter  if (set_time != 0)
1228251881Speter    {
1229251881Speter      svn_node_kind_t kind;
1230251881Speter      svn_boolean_t is_special;
1231251881Speter
1232251881Speter      /* Do not set the timestamp on special files. */
1233251881Speter      SVN_ERR(svn_io_check_special_path(local_abspath, &kind, &is_special,
1234251881Speter                                        scratch_pool));
1235251881Speter
1236251881Speter      /* Don't set affected time when local_abspath does not exist or is
1237251881Speter         a special file */
1238251881Speter      if (kind == svn_node_file && !is_special)
1239251881Speter        SVN_ERR(svn_io_set_file_affected_time(set_time, local_abspath,
1240251881Speter                                              scratch_pool));
1241251881Speter
1242251881Speter      /* Note that we can't use the value we get here for recording as the
1243251881Speter         filesystem might have a different timestamp granularity */
1244251881Speter    }
1245251881Speter
1246251881Speter
1247251881Speter  return svn_error_trace(get_and_record_fileinfo(wqb, local_abspath,
1248251881Speter                                                 TRUE /* ignore_enoent */,
1249251881Speter                                                 scratch_pool));
1250251881Speter}
1251251881Speter
1252251881Speter/* ------------------------------------------------------------------------ */
1253251881Speter
1254251881Speter/* OP_TMP_SET_TEXT_CONFLICT_MARKERS  */
1255251881Speter
1256251881Speter
1257251881Speterstatic svn_error_t *
1258251881Speterrun_set_text_conflict_markers(work_item_baton_t *wqb,
1259251881Speter                              svn_wc__db_t *db,
1260251881Speter                              const svn_skel_t *work_item,
1261251881Speter                              const char *wri_abspath,
1262251881Speter                              svn_cancel_func_t cancel_func,
1263251881Speter                              void *cancel_baton,
1264251881Speter                              apr_pool_t *scratch_pool)
1265251881Speter{
1266251881Speter  const svn_skel_t *arg = work_item->children->next;
1267251881Speter  const char *local_relpath;
1268251881Speter  const char *local_abspath;
1269251881Speter  const char *old_abspath = NULL;
1270251881Speter  const char *new_abspath = NULL;
1271251881Speter  const char *wrk_abspath = NULL;
1272251881Speter
1273251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg->data, arg->len);
1274251881Speter  SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1275251881Speter                                  local_relpath, scratch_pool, scratch_pool));
1276251881Speter
1277251881Speter  arg = arg->next;
1278251881Speter  local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1279251881Speter                           : NULL;
1280251881Speter
1281251881Speter  if (local_relpath)
1282251881Speter    {
1283251881Speter      SVN_ERR(svn_wc__db_from_relpath(&old_abspath, db, wri_abspath,
1284251881Speter                                      local_relpath,
1285251881Speter                                      scratch_pool, scratch_pool));
1286251881Speter    }
1287251881Speter
1288251881Speter  arg = arg->next;
1289251881Speter  local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1290251881Speter                           : NULL;
1291251881Speter  if (local_relpath)
1292251881Speter    {
1293251881Speter      SVN_ERR(svn_wc__db_from_relpath(&new_abspath, db, wri_abspath,
1294251881Speter                                      local_relpath,
1295251881Speter                                      scratch_pool, scratch_pool));
1296251881Speter    }
1297251881Speter
1298251881Speter  arg = arg->next;
1299251881Speter  local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1300251881Speter                           : NULL;
1301251881Speter
1302251881Speter  if (local_relpath)
1303251881Speter    {
1304251881Speter      SVN_ERR(svn_wc__db_from_relpath(&wrk_abspath, db, wri_abspath,
1305251881Speter                                      local_relpath,
1306251881Speter                                      scratch_pool, scratch_pool));
1307251881Speter    }
1308251881Speter
1309251881Speter  /* Upgrade scenario: We have a workqueue item that describes how to install a
1310251881Speter     non skel conflict. Fetch all the information we can to create a new style
1311251881Speter     conflict. */
1312251881Speter  /* ### Before format 30 this is/was a common code path as we didn't install
1313251881Speter     ### the conflict directly in the db. It just calls the wc_db code
1314251881Speter     ### to set the right fields. */
1315251881Speter
1316251881Speter  {
1317251881Speter    /* Check if we should combine with a property conflict... */
1318251881Speter    svn_skel_t *conflicts;
1319251881Speter
1320251881Speter    SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath,
1321251881Speter                                     scratch_pool, scratch_pool));
1322251881Speter
1323251881Speter    if (! conflicts)
1324251881Speter      {
1325251881Speter        /* No conflict exists, create a basic skel */
1326251881Speter        conflicts = svn_wc__conflict_skel_create(scratch_pool);
1327251881Speter
1328251881Speter        SVN_ERR(svn_wc__conflict_skel_set_op_update(conflicts, NULL, NULL,
1329251881Speter                                                    scratch_pool,
1330251881Speter                                                    scratch_pool));
1331251881Speter      }
1332251881Speter
1333251881Speter    /* Add the text conflict to the existing onflict */
1334251881Speter    SVN_ERR(svn_wc__conflict_skel_add_text_conflict(conflicts, db,
1335251881Speter                                                    local_abspath,
1336251881Speter                                                    wrk_abspath,
1337251881Speter                                                    old_abspath,
1338251881Speter                                                    new_abspath,
1339251881Speter                                                    scratch_pool,
1340251881Speter                                                    scratch_pool));
1341251881Speter
1342251881Speter    SVN_ERR(svn_wc__db_op_mark_conflict(db, local_abspath, conflicts,
1343251881Speter                                        NULL, scratch_pool));
1344251881Speter  }
1345251881Speter  return SVN_NO_ERROR;
1346251881Speter}
1347251881Speter
1348251881Speter/* ------------------------------------------------------------------------ */
1349251881Speter
1350251881Speter/* OP_TMP_SET_PROPERTY_CONFLICT_MARKER  */
1351251881Speter
1352251881Speterstatic svn_error_t *
1353251881Speterrun_set_property_conflict_marker(work_item_baton_t *wqb,
1354251881Speter                                 svn_wc__db_t *db,
1355251881Speter                                 const svn_skel_t *work_item,
1356251881Speter                                 const char *wri_abspath,
1357251881Speter                                 svn_cancel_func_t cancel_func,
1358251881Speter                                 void *cancel_baton,
1359251881Speter                                 apr_pool_t *scratch_pool)
1360251881Speter{
1361251881Speter  const svn_skel_t *arg = work_item->children->next;
1362251881Speter  const char *local_relpath;
1363251881Speter  const char *local_abspath;
1364251881Speter  const char *prej_abspath = NULL;
1365251881Speter
1366251881Speter  local_relpath = apr_pstrmemdup(scratch_pool, arg->data, arg->len);
1367251881Speter
1368251881Speter  SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1369251881Speter                                  local_relpath, scratch_pool, scratch_pool));
1370251881Speter
1371251881Speter
1372251881Speter  arg = arg->next;
1373251881Speter  local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1374251881Speter                           : NULL;
1375251881Speter
1376251881Speter  if (local_relpath)
1377251881Speter    SVN_ERR(svn_wc__db_from_relpath(&prej_abspath, db, wri_abspath,
1378251881Speter                                    local_relpath,
1379251881Speter                                    scratch_pool, scratch_pool));
1380251881Speter
1381251881Speter  {
1382251881Speter    /* Check if we should combine with a text conflict... */
1383251881Speter    svn_skel_t *conflicts;
1384251881Speter    apr_hash_t *prop_names;
1385251881Speter
1386251881Speter    SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath,
1387251881Speter                                     scratch_pool, scratch_pool));
1388251881Speter
1389251881Speter    if (! conflicts)
1390251881Speter      {
1391251881Speter        /* No conflict exists, create a basic skel */
1392251881Speter        conflicts = svn_wc__conflict_skel_create(scratch_pool);
1393251881Speter
1394251881Speter        SVN_ERR(svn_wc__conflict_skel_set_op_update(conflicts, NULL, NULL,
1395251881Speter                                                    scratch_pool,
1396251881Speter                                                    scratch_pool));
1397251881Speter      }
1398251881Speter
1399251881Speter    prop_names = apr_hash_make(scratch_pool);
1400251881Speter    SVN_ERR(svn_wc__conflict_skel_add_prop_conflict(conflicts, db,
1401251881Speter                                                    local_abspath,
1402251881Speter                                                    prej_abspath,
1403251881Speter                                                    NULL, NULL, NULL,
1404251881Speter                                                    prop_names,
1405251881Speter                                                    scratch_pool,
1406251881Speter                                                    scratch_pool));
1407251881Speter
1408251881Speter    SVN_ERR(svn_wc__db_op_mark_conflict(db, local_abspath, conflicts,
1409251881Speter                                        NULL, scratch_pool));
1410251881Speter  }
1411251881Speter  return SVN_NO_ERROR;
1412251881Speter}
1413251881Speter
1414251881Speter/* ------------------------------------------------------------------------ */
1415251881Speter
1416251881Speterstatic const struct work_item_dispatch dispatch_table[] = {
1417251881Speter  { OP_FILE_COMMIT, run_file_commit },
1418251881Speter  { OP_FILE_INSTALL, run_file_install },
1419251881Speter  { OP_FILE_REMOVE, run_file_remove },
1420251881Speter  { OP_FILE_MOVE, run_file_move },
1421251881Speter  { OP_FILE_COPY_TRANSLATED, run_file_copy_translated },
1422251881Speter  { OP_SYNC_FILE_FLAGS, run_sync_file_flags },
1423251881Speter  { OP_PREJ_INSTALL, run_prej_install },
1424251881Speter  { OP_DIRECTORY_REMOVE, run_dir_remove },
1425251881Speter  { OP_DIRECTORY_INSTALL, run_dir_install },
1426251881Speter
1427251881Speter  /* Upgrade steps */
1428251881Speter  { OP_POSTUPGRADE, run_postupgrade },
1429251881Speter
1430251881Speter  /* Legacy workqueue items. No longer created */
1431251881Speter  { OP_BASE_REMOVE, run_base_remove },
1432251881Speter  { OP_RECORD_FILEINFO, run_record_fileinfo },
1433251881Speter  { OP_TMP_SET_TEXT_CONFLICT_MARKERS, run_set_text_conflict_markers },
1434251881Speter  { OP_TMP_SET_PROPERTY_CONFLICT_MARKER, run_set_property_conflict_marker },
1435251881Speter
1436251881Speter  /* Sentinel.  */
1437251881Speter  { NULL }
1438251881Speter};
1439251881Speter
1440251881Speterstruct work_item_baton_t
1441251881Speter{
1442251881Speter  apr_pool_t *result_pool; /* Pool to allocate result in */
1443251881Speter
1444251881Speter  svn_boolean_t used; /* needs reset */
1445251881Speter
1446251881Speter  apr_hash_t *record_map; /* const char * -> svn_io_dirent2_t map */
1447251881Speter};
1448251881Speter
1449251881Speter
1450251881Speterstatic svn_error_t *
1451251881Speterdispatch_work_item(work_item_baton_t *wqb,
1452251881Speter                   svn_wc__db_t *db,
1453251881Speter                   const char *wri_abspath,
1454251881Speter                   const svn_skel_t *work_item,
1455251881Speter                   svn_cancel_func_t cancel_func,
1456251881Speter                   void *cancel_baton,
1457251881Speter                   apr_pool_t *scratch_pool)
1458251881Speter{
1459251881Speter  const struct work_item_dispatch *scan;
1460251881Speter
1461251881Speter  /* Scan the dispatch table for a function to handle this work item.  */
1462251881Speter  for (scan = &dispatch_table[0]; scan->name != NULL; ++scan)
1463251881Speter    {
1464251881Speter      if (svn_skel__matches_atom(work_item->children, scan->name))
1465251881Speter        {
1466251881Speter
1467251881Speter#ifdef SVN_DEBUG_WORK_QUEUE
1468251881Speter          SVN_DBG(("dispatch: operation='%s'\n", scan->name));
1469251881Speter#endif
1470251881Speter          SVN_ERR((*scan->func)(wqb, db, work_item, wri_abspath,
1471251881Speter                                cancel_func, cancel_baton,
1472251881Speter                                scratch_pool));
1473251881Speter
1474251881Speter#ifdef SVN_RUN_WORK_QUEUE_TWICE
1475251881Speter#ifdef SVN_DEBUG_WORK_QUEUE
1476251881Speter          SVN_DBG(("dispatch: operation='%s'\n", scan->name));
1477251881Speter#endif
1478251881Speter          /* Being able to run every workqueue item twice is one
1479251881Speter             requirement for workqueues to be restartable. */
1480251881Speter          SVN_ERR((*scan->func)(db, work_item, wri_abspath,
1481251881Speter                                cancel_func, cancel_baton,
1482251881Speter                                scratch_pool));
1483251881Speter#endif
1484251881Speter
1485251881Speter          break;
1486251881Speter        }
1487251881Speter    }
1488251881Speter
1489251881Speter  if (scan->name == NULL)
1490251881Speter    {
1491251881Speter      /* We should know about ALL possible work items here. If we do not,
1492251881Speter         then something is wrong. Most likely, some kind of format/code
1493251881Speter         skew. There is nothing more we can do. Erasing or ignoring this
1494251881Speter         work item could leave the WC in an even more broken state.
1495251881Speter
1496251881Speter         Contrary to issue #1581, we cannot simply remove work items and
1497251881Speter         continue, so bail out with an error.  */
1498251881Speter      return svn_error_createf(SVN_ERR_WC_BAD_ADM_LOG, NULL,
1499251881Speter                               _("Unrecognized work item in the queue"));
1500251881Speter    }
1501251881Speter
1502251881Speter  return SVN_NO_ERROR;
1503251881Speter}
1504251881Speter
1505251881Speter
1506251881Spetersvn_error_t *
1507251881Spetersvn_wc__wq_run(svn_wc__db_t *db,
1508251881Speter               const char *wri_abspath,
1509251881Speter               svn_cancel_func_t cancel_func,
1510251881Speter               void *cancel_baton,
1511251881Speter               apr_pool_t *scratch_pool)
1512251881Speter{
1513251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1514251881Speter  apr_uint64_t last_id = 0;
1515251881Speter  work_item_baton_t wib = { 0 };
1516251881Speter  wib.result_pool = svn_pool_create(scratch_pool);
1517251881Speter
1518251881Speter#ifdef SVN_DEBUG_WORK_QUEUE
1519251881Speter  SVN_DBG(("wq_run: wri='%s'\n", wri_abspath));
1520251881Speter  {
1521251881Speter    static int count = 0;
1522251881Speter    const char *count_env_var = getenv("SVN_DEBUG_WORK_QUEUE");
1523251881Speter
1524251881Speter    if (count_env_var && ++count == atoi(count_env_var))
1525251881Speter      return svn_error_create(SVN_ERR_CANCELLED, NULL, "fake cancel");
1526251881Speter  }
1527251881Speter#endif
1528251881Speter
1529251881Speter  while (TRUE)
1530251881Speter    {
1531251881Speter      apr_uint64_t id;
1532251881Speter      svn_skel_t *work_item;
1533251881Speter      svn_error_t *err;
1534251881Speter
1535251881Speter      svn_pool_clear(iterpool);
1536251881Speter
1537251881Speter      if (! wib.used)
1538251881Speter        {
1539251881Speter          /* Make sure to do this *early* in the loop iteration. There may
1540251881Speter             be a LAST_ID that needs to be marked as completed, *before* we
1541251881Speter             start worrying about anything else.  */
1542251881Speter          SVN_ERR(svn_wc__db_wq_fetch_next(&id, &work_item, db, wri_abspath,
1543251881Speter                                           last_id, iterpool, iterpool));
1544251881Speter        }
1545251881Speter      else
1546251881Speter        {
1547251881Speter          /* Make sure to do this *early* in the loop iteration. There may
1548251881Speter             be a LAST_ID that needs to be marked as completed, *before* we
1549251881Speter             start worrying about anything else.  */
1550251881Speter          SVN_ERR(svn_wc__db_wq_record_and_fetch_next(&id, &work_item,
1551251881Speter                                                      db, wri_abspath,
1552251881Speter                                                      last_id, wib.record_map,
1553251881Speter                                                      iterpool,
1554251881Speter                                                      wib.result_pool));
1555251881Speter
1556251881Speter          svn_pool_clear(wib.result_pool);
1557251881Speter          wib.record_map = NULL;
1558251881Speter          wib.used = FALSE;
1559251881Speter        }
1560251881Speter
1561251881Speter      /* Stop work queue processing, if requested. A future 'svn cleanup'
1562251881Speter         should be able to continue the processing. Note that we may
1563251881Speter         have WORK_ITEM, but we'll just skip its processing for now.  */
1564251881Speter      if (cancel_func)
1565251881Speter        SVN_ERR(cancel_func(cancel_baton));
1566251881Speter
1567251881Speter      /* If we have a WORK_ITEM, then process the sucker. Otherwise,
1568251881Speter         we're done.  */
1569251881Speter      if (work_item == NULL)
1570251881Speter        break;
1571251881Speter
1572251881Speter      err = dispatch_work_item(&wib, db, wri_abspath, work_item,
1573251881Speter                               cancel_func, cancel_baton, iterpool);
1574251881Speter      if (err)
1575251881Speter        {
1576251881Speter          const char *skel = svn_skel__unparse(work_item, scratch_pool)->data;
1577251881Speter
1578251881Speter          return svn_error_createf(SVN_ERR_WC_BAD_ADM_LOG, err,
1579251881Speter                                   _("Failed to run the WC DB work queue "
1580251881Speter                                     "associated with '%s', work item %d %s"),
1581251881Speter                                   svn_dirent_local_style(wri_abspath,
1582251881Speter                                                          scratch_pool),
1583251881Speter                                   (int)id, skel);
1584251881Speter        }
1585251881Speter
1586251881Speter      /* The work item finished without error. Mark it completed
1587251881Speter         in the next loop.  */
1588251881Speter      last_id = id;
1589251881Speter    }
1590251881Speter
1591251881Speter  svn_pool_destroy(iterpool);
1592251881Speter  return SVN_NO_ERROR;
1593251881Speter}
1594251881Speter
1595251881Speter
1596251881Spetersvn_skel_t *
1597251881Spetersvn_wc__wq_merge(svn_skel_t *work_item1,
1598251881Speter                 svn_skel_t *work_item2,
1599251881Speter                 apr_pool_t *result_pool)
1600251881Speter{
1601251881Speter  /* If either argument is NULL, then just return the other.  */
1602251881Speter  if (work_item1 == NULL)
1603251881Speter    return work_item2;
1604251881Speter  if (work_item2 == NULL)
1605251881Speter    return work_item1;
1606251881Speter
1607251881Speter  /* We have two items. Figure out how to join them.  */
1608251881Speter  if (SVN_WC__SINGLE_WORK_ITEM(work_item1))
1609251881Speter    {
1610251881Speter      if (SVN_WC__SINGLE_WORK_ITEM(work_item2))
1611251881Speter        {
1612251881Speter          /* Both are singular work items. Construct a list, then put
1613251881Speter             both work items into it (in the proper order).  */
1614251881Speter
1615251881Speter          svn_skel_t *result = svn_skel__make_empty_list(result_pool);
1616251881Speter
1617251881Speter          svn_skel__prepend(work_item2, result);
1618251881Speter          svn_skel__prepend(work_item1, result);
1619251881Speter          return result;
1620251881Speter        }
1621251881Speter
1622251881Speter      /* WORK_ITEM2 is a list of work items. We can simply shove WORK_ITEM1
1623251881Speter         in the front to keep the ordering.  */
1624251881Speter      svn_skel__prepend(work_item1, work_item2);
1625251881Speter      return work_item2;
1626251881Speter    }
1627251881Speter  /* WORK_ITEM1 is a list of work items.  */
1628251881Speter
1629251881Speter  if (SVN_WC__SINGLE_WORK_ITEM(work_item2))
1630251881Speter    {
1631251881Speter      /* Put WORK_ITEM2 onto the end of the WORK_ITEM1 list.  */
1632251881Speter      svn_skel__append(work_item1, work_item2);
1633251881Speter      return work_item1;
1634251881Speter    }
1635251881Speter
1636251881Speter  /* We have two lists of work items. We need to chain all of the work
1637251881Speter     items into one big list. We will leave behind the WORK_ITEM2 skel,
1638251881Speter     as we only want its children.  */
1639251881Speter  svn_skel__append(work_item1, work_item2->children);
1640251881Speter  return work_item1;
1641251881Speter}
1642251881Speter
1643251881Speter
1644251881Speterstatic svn_error_t *
1645251881Speterget_and_record_fileinfo(work_item_baton_t *wqb,
1646251881Speter                        const char *local_abspath,
1647251881Speter                        svn_boolean_t ignore_enoent,
1648251881Speter                        apr_pool_t *scratch_pool)
1649251881Speter{
1650251881Speter  const svn_io_dirent2_t *dirent;
1651251881Speter
1652251881Speter  SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, ignore_enoent,
1653251881Speter                              wqb->result_pool, scratch_pool));
1654251881Speter
1655251881Speter  if (dirent->kind != svn_node_file)
1656251881Speter    return SVN_NO_ERROR;
1657251881Speter
1658251881Speter  wqb->used = TRUE;
1659251881Speter
1660251881Speter  if (! wqb->record_map)
1661251881Speter    wqb->record_map = apr_hash_make(wqb->result_pool);
1662251881Speter
1663251881Speter  svn_hash_sets(wqb->record_map, apr_pstrdup(wqb->result_pool, local_abspath),
1664251881Speter                dirent);
1665251881Speter
1666251881Speter  return SVN_NO_ERROR;
1667251881Speter}
1668