summaryrefslogtreecommitdiffstats
path: root/subversion/libsvn_wc/workqueue.c
diff options
context:
space:
mode:
authorpeter <peter@FreeBSD.org>2013-06-18 02:07:41 +0000
committerpeter <peter@FreeBSD.org>2013-06-18 02:07:41 +0000
commitd25dac7fcc6acc838b71bbda8916fd9665c709ab (patch)
tree135691142dc0e75a5e5d97b5074d03436435b8e0 /subversion/libsvn_wc/workqueue.c
downloadFreeBSD-src-d25dac7fcc6acc838b71bbda8916fd9665c709ab.zip
FreeBSD-src-d25dac7fcc6acc838b71bbda8916fd9665c709ab.tar.gz
Import trimmed svn-1.8.0-rc3
Diffstat (limited to 'subversion/libsvn_wc/workqueue.c')
-rw-r--r--subversion/libsvn_wc/workqueue.c1666
1 files changed, 1666 insertions, 0 deletions
diff --git a/subversion/libsvn_wc/workqueue.c b/subversion/libsvn_wc/workqueue.c
new file mode 100644
index 0000000..ddbac15
--- /dev/null
+++ b/subversion/libsvn_wc/workqueue.c
@@ -0,0 +1,1666 @@
+/*
+ * workqueue.c : manipulating work queue items
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+#include <apr_pools.h>
+
+#include "svn_types.h"
+#include "svn_pools.h"
+#include "svn_dirent_uri.h"
+#include "svn_subst.h"
+#include "svn_hash.h"
+#include "svn_io.h"
+
+#include "wc.h"
+#include "wc_db.h"
+#include "workqueue.h"
+#include "adm_files.h"
+#include "conflicts.h"
+#include "translate.h"
+
+#include "svn_private_config.h"
+#include "private/svn_skel.h"
+
+
+/* Workqueue operation names. */
+#define OP_FILE_COMMIT "file-commit"
+#define OP_FILE_INSTALL "file-install"
+#define OP_FILE_REMOVE "file-remove"
+#define OP_FILE_MOVE "file-move"
+#define OP_FILE_COPY_TRANSLATED "file-translate"
+#define OP_SYNC_FILE_FLAGS "sync-file-flags"
+#define OP_PREJ_INSTALL "prej-install"
+#define OP_DIRECTORY_REMOVE "dir-remove"
+#define OP_DIRECTORY_INSTALL "dir-install"
+
+#define OP_POSTUPGRADE "postupgrade"
+
+/* Legacy items */
+#define OP_BASE_REMOVE "base-remove"
+#define OP_RECORD_FILEINFO "record-fileinfo"
+#define OP_TMP_SET_TEXT_CONFLICT_MARKERS "tmp-set-text-conflict-markers"
+#define OP_TMP_SET_PROPERTY_CONFLICT_MARKER "tmp-set-property-conflict-marker"
+
+/* For work queue debugging. Generates output about its operation. */
+/* #define SVN_DEBUG_WORK_QUEUE */
+
+typedef struct work_item_baton_t work_item_baton_t;
+
+struct work_item_dispatch {
+ const char *name;
+ svn_error_t *(*func)(work_item_baton_t *wqb,
+ svn_wc__db_t *db,
+ const svn_skel_t *work_item,
+ const char *wri_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool);
+};
+
+/* Forward definition */
+static svn_error_t *
+get_and_record_fileinfo(work_item_baton_t *wqb,
+ const char *local_abspath,
+ svn_boolean_t ignore_enoent,
+ apr_pool_t *scratch_pool);
+
+/* ------------------------------------------------------------------------ */
+/* OP_REMOVE_BASE */
+
+/* Removes a BASE_NODE and all it's data, leaving any adds and copies as is.
+ Do this as a depth first traversal to make sure than any parent still exists
+ on error conditions.
+ */
+
+/* Process the OP_REMOVE_BASE work item WORK_ITEM.
+ * See svn_wc__wq_build_remove_base() which generates this work item.
+ * Implements (struct work_item_dispatch).func. */
+static svn_error_t *
+run_base_remove(work_item_baton_t *wqb,
+ svn_wc__db_t *db,
+ const svn_skel_t *work_item,
+ const char *wri_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool)
+{
+ const svn_skel_t *arg1 = work_item->children->next;
+ const char *local_relpath;
+ const char *local_abspath;
+ svn_revnum_t not_present_rev = SVN_INVALID_REVNUM;
+ apr_int64_t val;
+
+ local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
+ SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
+ local_relpath, scratch_pool, scratch_pool));
+ SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
+
+ if (arg1->next->next)
+ {
+ not_present_rev = (svn_revnum_t)val;
+
+ SVN_ERR(svn_skel__parse_int(&val, arg1->next->next, scratch_pool));
+ }
+ else
+ {
+ svn_boolean_t keep_not_present;
+
+ SVN_ERR_ASSERT(SVN_WC__VERSION <= 28); /* Case unused in later versions*/
+
+ keep_not_present = (val != 0);
+
+ if (keep_not_present)
+ {
+ SVN_ERR(svn_wc__db_base_get_info(NULL, NULL,
+ &not_present_rev, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ db, local_abspath,
+ scratch_pool, scratch_pool));
+ }
+ }
+
+ SVN_ERR(svn_wc__db_base_remove(db, local_abspath,
+ FALSE /* keep_as_working */,
+ TRUE /* queue_deletes */,
+ not_present_rev,
+ NULL, NULL, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* ------------------------------------------------------------------------ */
+
+/* ------------------------------------------------------------------------ */
+
+/* OP_FILE_COMMIT */
+
+
+/* FILE_ABSPATH is the new text base of the newly-committed versioned file,
+ * in repository-normal form (aka "detranslated" form). Adjust the working
+ * file accordingly.
+ *
+ * If eol and/or keyword translation would cause the working file to
+ * change, then overwrite the working file with a translated copy of
+ * the new text base (but only if the translated copy differs from the
+ * current working file -- if they are the same, do nothing, to avoid
+ * clobbering timestamps unnecessarily).
+ *
+ * Set the working file's executability according to its svn:executable
+ * property.
+ *
+ * Set the working file's read-only attribute according to its properties
+ * and lock status (see svn_wc__maybe_set_read_only()).
+ *
+ * If the working file was re-translated or had its executability or
+ * read-only state changed,
+ * then set OVERWROTE_WORKING to TRUE. If the working file isn't
+ * touched at all, then set to FALSE.
+ *
+ * Use SCRATCH_POOL for any temporary allocation.
+ */
+static svn_error_t *
+install_committed_file(svn_boolean_t *overwrote_working,
+ svn_wc__db_t *db,
+ const char *file_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool)
+{
+ svn_boolean_t same;
+ const char *tmp_wfile;
+ svn_boolean_t special;
+
+ /* start off assuming that the working file isn't touched. */
+ *overwrote_working = FALSE;
+
+ /* In the commit, newlines and keywords may have been
+ * canonicalized and/or contracted... Or they may not have
+ * been. It's kind of hard to know. Here's how we find out:
+ *
+ * 1. Make a translated tmp copy of the committed text base,
+ * translated according to the versioned file's properties.
+ * Or, if no committed text base exists (the commit must have
+ * been a propchange only), make a translated tmp copy of the
+ * working file.
+ * 2. Compare the translated tmpfile to the working file.
+ * 3. If different, copy the tmpfile over working file.
+ *
+ * This means we only rewrite the working file if we absolutely
+ * have to, which is good because it avoids changing the file's
+ * timestamp unless necessary, so editors aren't tempted to
+ * reread the file if they don't really need to.
+ */
+
+ /* Copy and translate the new base-to-be file (if found, else the working
+ * file) from repository-normal form to working form, writing a new
+ * temporary file if any translation was actually done. Set TMP_WFILE to
+ * the translated file's path, which may be the source file's path if no
+ * translation was done. Set SAME to indicate whether the new working
+ * text is the same as the old working text (or TRUE if it's a special
+ * file). */
+ {
+ const char *tmp = file_abspath;
+
+ /* Copy and translate, if necessary. The output file will be deleted at
+ * scratch_pool cleanup.
+ * ### That's not quite safe: we might rename the file and then maybe
+ * its path will get re-used for another temp file before pool clean-up.
+ * Instead, we should take responsibility for deleting it. */
+ SVN_ERR(svn_wc__internal_translated_file(&tmp_wfile, tmp, db,
+ file_abspath,
+ SVN_WC_TRANSLATE_FROM_NF,
+ cancel_func, cancel_baton,
+ scratch_pool, scratch_pool));
+
+ /* If the translation is a no-op, the text base and the working copy
+ * file contain the same content, because we use the same props here
+ * as were used to detranslate from working file to text base.
+ *
+ * In that case: don't replace the working file, but make sure
+ * it has the right executable and read_write attributes set.
+ */
+
+ SVN_ERR(svn_wc__get_translate_info(NULL, NULL,
+ NULL,
+ &special,
+ db, file_abspath, NULL, FALSE,
+ scratch_pool, scratch_pool));
+ /* Translated file returns the exact pointer if not translated. */
+ if (! special && tmp != tmp_wfile)
+ SVN_ERR(svn_io_files_contents_same_p(&same, tmp_wfile,
+ file_abspath, scratch_pool));
+ else
+ same = TRUE;
+ }
+
+ if (! same)
+ {
+ SVN_ERR(svn_io_file_rename(tmp_wfile, file_abspath, scratch_pool));
+ *overwrote_working = TRUE;
+ }
+
+ /* ### should be using OP_SYNC_FILE_FLAGS, or an internal version of
+ ### that here. do we need to set *OVERWROTE_WORKING? */
+
+ /* ### Re: OVERWROTE_WORKING, the following function is rather liberal
+ ### with setting that flag, so we should probably decide if we really
+ ### care about it when syncing flags. */
+ SVN_ERR(svn_wc__sync_flags_with_props(overwrote_working, db, file_abspath,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+process_commit_file_install(svn_wc__db_t *db,
+ const char *local_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool)
+{
+ svn_boolean_t overwrote_working;
+
+ /* Install the new file, which may involve expanding keywords.
+ A copy of this file should have been dropped into our `tmp/text-base'
+ directory during the commit process. Part of this process
+ involves recording the textual timestamp for this entry. We'd like
+ to just use the timestamp of the working file, but it is possible
+ that at some point during the commit, the real working file might
+ have changed again.
+ */
+
+ SVN_ERR(install_committed_file(&overwrote_working, db,
+ local_abspath,
+ cancel_func, cancel_baton,
+ scratch_pool));
+
+ /* We will compute and modify the size and timestamp */
+ if (overwrote_working)
+ {
+ apr_finfo_t finfo;
+
+ SVN_ERR(svn_io_stat(&finfo, local_abspath,
+ APR_FINFO_MIN | APR_FINFO_LINK, scratch_pool));
+ SVN_ERR(svn_wc__db_global_record_fileinfo(db, local_abspath,
+ finfo.size, finfo.mtime,
+ scratch_pool));
+ }
+ else
+ {
+ svn_boolean_t modified;
+
+ /* The working copy file hasn't been overwritten. We just
+ removed the recorded size and modification time from the nodes
+ record by calling svn_wc__db_global_commit().
+
+ Now we have some file in our working copy that might be what
+ we just committed, but we are not certain at this point.
+
+ We still have a write lock here, so we check if the file is
+ what we expect it to be and if it is the right file we update
+ the recorded information. (If it isn't we keep the null data).
+
+ Instead of reimplementing all this here, we just call a function
+ that already does implement this when it notices that we have the
+ right kind of lock (and we ignore the result)
+ */
+ SVN_ERR(svn_wc__internal_file_modified_p(&modified,
+ db, local_abspath, FALSE,
+ scratch_pool));
+ }
+ return SVN_NO_ERROR;
+}
+
+
+static svn_error_t *
+run_file_commit(work_item_baton_t *wqb,
+ svn_wc__db_t *db,
+ const svn_skel_t *work_item,
+ const char *wri_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool)
+{
+ const svn_skel_t *arg1 = work_item->children->next;
+ const char *local_relpath;
+ const char *local_abspath;
+
+ local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
+ SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
+ local_relpath, scratch_pool, scratch_pool));
+
+ /* We don't both parsing the other two values in the skel. */
+
+ return svn_error_trace(
+ process_commit_file_install(db, local_abspath,
+ cancel_func, cancel_baton,
+ scratch_pool));
+}
+
+svn_error_t *
+svn_wc__wq_build_file_commit(svn_skel_t **work_item,
+ svn_wc__db_t *db,
+ const char *local_abspath,
+ svn_boolean_t props_mod,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *local_relpath;
+ *work_item = svn_skel__make_empty_list(result_pool);
+
+ SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
+ local_abspath, result_pool, scratch_pool));
+
+ svn_skel__prepend_str(local_relpath, *work_item, result_pool);
+
+ svn_skel__prepend_str(OP_FILE_COMMIT, *work_item, result_pool);
+
+ return SVN_NO_ERROR;
+}
+
+/* ------------------------------------------------------------------------ */
+/* OP_POSTUPGRADE */
+
+static svn_error_t *
+run_postupgrade(work_item_baton_t *wqb,
+ svn_wc__db_t *db,
+ const svn_skel_t *work_item,
+ const char *wri_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool)
+{
+ const char *entries_path;
+ const char *format_path;
+ const char *wcroot_abspath;
+ const char *adm_path;
+ const char *temp_path;
+ svn_error_t *err;
+
+ err = svn_wc__wipe_postupgrade(wri_abspath, FALSE,
+ cancel_func, cancel_baton, scratch_pool);
+ if (err && err->apr_err == SVN_ERR_ENTRY_NOT_FOUND)
+ /* No entry, this can happen when the wq item is rerun. */
+ svn_error_clear(err);
+ else
+ SVN_ERR(err);
+
+ SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, db, wri_abspath,
+ scratch_pool, scratch_pool));
+
+ adm_path = svn_wc__adm_child(wcroot_abspath, NULL, scratch_pool);
+ entries_path = svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_ENTRIES,
+ scratch_pool);
+ format_path = svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_FORMAT,
+ scratch_pool);
+
+ /* Write the 'format' and 'entries' files.
+
+ ### The order may matter for some sufficiently old clients.. but
+ ### this code only runs during upgrade after the files had been
+ ### removed earlier during the upgrade. */
+ SVN_ERR(svn_io_write_unique(&temp_path, adm_path, SVN_WC__NON_ENTRIES_STRING,
+ sizeof(SVN_WC__NON_ENTRIES_STRING) - 1,
+ svn_io_file_del_none, scratch_pool));
+ SVN_ERR(svn_io_file_rename(temp_path, format_path, scratch_pool));
+
+ SVN_ERR(svn_io_write_unique(&temp_path, adm_path, SVN_WC__NON_ENTRIES_STRING,
+ sizeof(SVN_WC__NON_ENTRIES_STRING) - 1,
+ svn_io_file_del_none, scratch_pool));
+ SVN_ERR(svn_io_file_rename(temp_path, entries_path, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__wq_build_postupgrade(svn_skel_t **work_item,
+ apr_pool_t *result_pool)
+{
+ *work_item = svn_skel__make_empty_list(result_pool);
+
+ svn_skel__prepend_str(OP_POSTUPGRADE, *work_item, result_pool);
+
+ return SVN_NO_ERROR;
+}
+
+/* ------------------------------------------------------------------------ */
+
+/* OP_FILE_INSTALL */
+
+/* Process the OP_FILE_INSTALL work item WORK_ITEM.
+ * See svn_wc__wq_build_file_install() which generates this work item.
+ * Implements (struct work_item_dispatch).func. */
+static svn_error_t *
+run_file_install(work_item_baton_t *wqb,
+ svn_wc__db_t *db,
+ const svn_skel_t *work_item,
+ const char *wri_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool)
+{
+ const svn_skel_t *arg1 = work_item->children->next;
+ const svn_skel_t *arg4 = arg1->next->next->next;
+ const char *local_relpath;
+ const char *local_abspath;
+ svn_boolean_t use_commit_times;
+ svn_boolean_t record_fileinfo;
+ svn_boolean_t special;
+ svn_stream_t *src_stream;
+ svn_subst_eol_style_t style;
+ const char *eol;
+ apr_hash_t *keywords;
+ const char *temp_dir_abspath;
+ svn_stream_t *dst_stream;
+ const char *dst_abspath;
+ apr_int64_t val;
+ const char *wcroot_abspath;
+ const char *source_abspath;
+ const svn_checksum_t *checksum;
+ apr_hash_t *props;
+ apr_time_t changed_date;
+
+ local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
+ SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
+ local_relpath, scratch_pool, scratch_pool));
+
+ SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
+ use_commit_times = (val != 0);
+ SVN_ERR(svn_skel__parse_int(&val, arg1->next->next, scratch_pool));
+ record_fileinfo = (val != 0);
+
+ SVN_ERR(svn_wc__db_read_node_install_info(&wcroot_abspath,
+ &checksum, &props,
+ &changed_date,
+ db, local_abspath, wri_abspath,
+ scratch_pool, scratch_pool));
+
+ if (arg4 != NULL)
+ {
+ /* Use the provided path for the source. */
+ local_relpath = apr_pstrmemdup(scratch_pool, arg4->data, arg4->len);
+ SVN_ERR(svn_wc__db_from_relpath(&source_abspath, db, wri_abspath,
+ local_relpath,
+ scratch_pool, scratch_pool));
+ }
+ else if (! checksum)
+ {
+ /* This error replaces a previous assertion. Reporting an error from here
+ leaves the workingqueue operation in place, so the working copy is
+ still broken!
+
+ But when we report this error the user at least knows what node has
+ this specific problem, so maybe we can find out why users see this
+ error */
+ return svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
+ _("Can't install '%s' from pristine store, "
+ "because no checksum is recorded for this "
+ "file"),
+ svn_dirent_local_style(local_abspath,
+ scratch_pool));
+ }
+ else
+ {
+ SVN_ERR(svn_wc__db_pristine_get_future_path(&source_abspath,
+ wcroot_abspath,
+ checksum,
+ scratch_pool, scratch_pool));
+ }
+
+ SVN_ERR(svn_stream_open_readonly(&src_stream, source_abspath,
+ scratch_pool, scratch_pool));
+
+ /* Fetch all the translation bits. */
+ SVN_ERR(svn_wc__get_translate_info(&style, &eol,
+ &keywords,
+ &special, db, local_abspath,
+ props, FALSE,
+ scratch_pool, scratch_pool));
+ if (special)
+ {
+ /* When this stream is closed, the resulting special file will
+ atomically be created/moved into place at LOCAL_ABSPATH. */
+ SVN_ERR(svn_subst_create_specialfile(&dst_stream, local_abspath,
+ scratch_pool, scratch_pool));
+
+ /* Copy the "repository normal" form of the special file into the
+ special stream. */
+ SVN_ERR(svn_stream_copy3(src_stream, dst_stream,
+ cancel_func, cancel_baton,
+ scratch_pool));
+
+ /* No need to set exec or read-only flags on special files. */
+
+ /* ### Shouldn't this record a timestamp and size, etc.? */
+ return SVN_NO_ERROR;
+ }
+
+ if (svn_subst_translation_required(style, eol, keywords,
+ FALSE /* special */,
+ TRUE /* force_eol_check */))
+ {
+ /* Wrap it in a translating (expanding) stream. */
+ src_stream = svn_subst_stream_translated(src_stream, eol,
+ TRUE /* repair */,
+ keywords,
+ TRUE /* expand */,
+ scratch_pool);
+ }
+
+ /* Where is the Right Place to put a temp file in this working copy? */
+ SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath,
+ db, wcroot_abspath,
+ scratch_pool, scratch_pool));
+
+ /* Translate to a temporary file. We don't want the user seeing a partial
+ file, nor let them muck with it while we translate. We may also need to
+ get its TRANSLATED_SIZE before the user can monkey it. */
+ SVN_ERR(svn_stream_open_unique(&dst_stream, &dst_abspath,
+ temp_dir_abspath,
+ svn_io_file_del_none,
+ scratch_pool, scratch_pool));
+
+ /* Copy from the source to the dest, translating as we go. This will also
+ close both streams. */
+ SVN_ERR(svn_stream_copy3(src_stream, dst_stream,
+ cancel_func, cancel_baton,
+ scratch_pool));
+
+ /* All done. Move the file into place. */
+
+ {
+ svn_error_t *err;
+
+ err = svn_io_file_rename(dst_abspath, local_abspath, scratch_pool);
+
+ /* With a single db we might want to install files in a missing directory.
+ Simply trying this scenario on error won't do any harm and at least
+ one user reported this problem on IRC. */
+ if (err && APR_STATUS_IS_ENOENT(err->apr_err))
+ {
+ svn_error_t *err2;
+
+ err2 = svn_io_make_dir_recursively(svn_dirent_dirname(local_abspath,
+ scratch_pool),
+ scratch_pool);
+
+ if (err2)
+ /* Creating directory didn't work: Return all errors */
+ return svn_error_trace(svn_error_compose_create(err, err2));
+ else
+ /* We could create a directory: retry install */
+ svn_error_clear(err);
+
+ SVN_ERR(svn_io_file_rename(dst_abspath, local_abspath, scratch_pool));
+ }
+ else
+ SVN_ERR(err);
+ }
+
+ /* Tweak the on-disk file according to its properties. */
+#ifndef WIN32
+ if (props && svn_hash_gets(props, SVN_PROP_EXECUTABLE))
+ SVN_ERR(svn_io_set_file_executable(local_abspath, TRUE, FALSE,
+ scratch_pool));
+#endif
+
+ /* Note that this explicitly checks the pristine properties, to make sure
+ that when the lock is locally set (=modification) it is not read only */
+ if (props && svn_hash_gets(props, SVN_PROP_NEEDS_LOCK))
+ {
+ svn_wc__db_status_t status;
+ svn_wc__db_lock_t *lock;
+ SVN_ERR(svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, &lock, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ db, local_abspath,
+ scratch_pool, scratch_pool));
+
+ if (!lock && status != svn_wc__db_status_added)
+ SVN_ERR(svn_io_set_file_read_only(local_abspath, FALSE, scratch_pool));
+ }
+
+ if (use_commit_times)
+ {
+ if (changed_date)
+ SVN_ERR(svn_io_set_file_affected_time(changed_date,
+ local_abspath,
+ scratch_pool));
+ }
+
+ /* ### this should happen before we rename the file into place. */
+ if (record_fileinfo)
+ {
+ SVN_ERR(get_and_record_fileinfo(wqb, local_abspath,
+ FALSE /* ignore_enoent */,
+ scratch_pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_wc__wq_build_file_install(svn_skel_t **work_item,
+ svn_wc__db_t *db,
+ const char *local_abspath,
+ const char *source_abspath,
+ svn_boolean_t use_commit_times,
+ svn_boolean_t record_fileinfo,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *local_relpath;
+ const char *wri_abspath;
+ *work_item = svn_skel__make_empty_list(result_pool);
+
+ /* Use the directory of the file to install as wri_abspath to avoid
+ filestats on just obtaining the wc-root */
+ wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
+
+ /* If a SOURCE_ABSPATH was provided, then put it into the skel. If this
+ value is not provided, then the file's pristine contents will be used. */
+ if (source_abspath != NULL)
+ {
+ SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
+ source_abspath,
+ result_pool, scratch_pool));
+
+ svn_skel__prepend_str(local_relpath, *work_item, result_pool);
+ }
+
+ SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
+ local_abspath, result_pool, scratch_pool));
+
+ svn_skel__prepend_int(record_fileinfo, *work_item, result_pool);
+ svn_skel__prepend_int(use_commit_times, *work_item, result_pool);
+ svn_skel__prepend_str(local_relpath, *work_item, result_pool);
+ svn_skel__prepend_str(OP_FILE_INSTALL, *work_item, result_pool);
+
+ return SVN_NO_ERROR;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+/* OP_FILE_REMOVE */
+
+/* Process the OP_FILE_REMOVE work item WORK_ITEM.
+ * See svn_wc__wq_build_file_remove() which generates this work item.
+ * Implements (struct work_item_dispatch).func. */
+static svn_error_t *
+run_file_remove(work_item_baton_t *wqb,
+ svn_wc__db_t *db,
+ const svn_skel_t *work_item,
+ const char *wri_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool)
+{
+ const svn_skel_t *arg1 = work_item->children->next;
+ const char *local_relpath;
+ const char *local_abspath;
+
+ local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
+ SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
+ local_relpath, scratch_pool, scratch_pool));
+
+ /* Remove the path, no worrying if it isn't there. */
+ return svn_error_trace(svn_io_remove_file2(local_abspath, TRUE,
+ scratch_pool));
+}
+
+
+svn_error_t *
+svn_wc__wq_build_file_remove(svn_skel_t **work_item,
+ svn_wc__db_t *db,
+ const char *wri_abspath,
+ const char *local_abspath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *local_relpath;
+ *work_item = svn_skel__make_empty_list(result_pool);
+
+ SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
+ local_abspath, result_pool, scratch_pool));
+
+ svn_skel__prepend_str(local_relpath, *work_item, result_pool);
+ svn_skel__prepend_str(OP_FILE_REMOVE, *work_item, result_pool);
+
+ return SVN_NO_ERROR;
+}
+
+/* ------------------------------------------------------------------------ */
+
+/* OP_DIRECTORY_REMOVE */
+
+/* Process the OP_FILE_REMOVE work item WORK_ITEM.
+ * See svn_wc__wq_build_file_remove() which generates this work item.
+ * Implements (struct work_item_dispatch).func. */
+static svn_error_t *
+run_dir_remove(work_item_baton_t *wqb,
+ svn_wc__db_t *db,
+ const svn_skel_t *work_item,
+ const char *wri_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool)
+{
+ const svn_skel_t *arg1 = work_item->children->next;
+ const char *local_relpath;
+ const char *local_abspath;
+ svn_boolean_t recursive;
+
+ local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
+ SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
+ local_relpath, scratch_pool, scratch_pool));
+
+ recursive = FALSE;
+ if (arg1->next)
+ {
+ apr_int64_t val;
+ SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
+
+ recursive = (val != 0);
+ }
+
+ /* Remove the path, no worrying if it isn't there. */
+ if (recursive)
+ return svn_error_trace(
+ svn_io_remove_dir2(local_abspath, TRUE,
+ cancel_func, cancel_baton,
+ scratch_pool));
+ else
+ {
+ svn_error_t *err;
+
+ err = svn_io_dir_remove_nonrecursive(local_abspath, scratch_pool);
+
+ if (err && (APR_STATUS_IS_ENOENT(err->apr_err)
+ || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)
+ || APR_STATUS_IS_ENOTEMPTY(err->apr_err)))
+ {
+ svn_error_clear(err);
+ err = NULL;
+ }
+
+ return svn_error_trace(err);
+ }
+}
+
+svn_error_t *
+svn_wc__wq_build_dir_remove(svn_skel_t **work_item,
+ svn_wc__db_t *db,
+ const char *wri_abspath,
+ const char *local_abspath,
+ svn_boolean_t recursive,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *local_relpath;
+ *work_item = svn_skel__make_empty_list(result_pool);
+
+ SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
+ local_abspath, result_pool, scratch_pool));
+
+ if (recursive)
+ svn_skel__prepend_int(TRUE, *work_item, result_pool);
+
+ svn_skel__prepend_str(local_relpath, *work_item, result_pool);
+ svn_skel__prepend_str(OP_DIRECTORY_REMOVE, *work_item, result_pool);
+
+ return SVN_NO_ERROR;
+}
+
+/* ------------------------------------------------------------------------ */
+
+/* OP_FILE_MOVE */
+
+/* Process the OP_FILE_MOVE work item WORK_ITEM.
+ * See svn_wc__wq_build_file_move() which generates this work item.
+ * Implements (struct work_item_dispatch).func. */
+static svn_error_t *
+run_file_move(work_item_baton_t *wqb,
+ svn_wc__db_t *db,
+ const svn_skel_t *work_item,
+ const char *wri_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool)
+{
+ const svn_skel_t *arg1 = work_item->children->next;
+ const char *src_abspath, *dst_abspath;
+ const char *local_relpath;
+ svn_error_t *err;
+
+ local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
+ SVN_ERR(svn_wc__db_from_relpath(&src_abspath, db, wri_abspath, local_relpath,
+ scratch_pool, scratch_pool));
+ local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->data,
+ arg1->next->len);
+ SVN_ERR(svn_wc__db_from_relpath(&dst_abspath, db, wri_abspath, local_relpath,
+ scratch_pool, scratch_pool));
+
+ /* Use svn_io_file_move() instead of svn_io_file_rename() to allow cross
+ device copies. We should not fail in the workqueue. */
+
+ err = svn_io_file_move(src_abspath, dst_abspath, scratch_pool);
+
+ /* If the source is not found, we assume the wq op is already handled */
+ if (err && APR_STATUS_IS_ENOENT(err->apr_err))
+ svn_error_clear(err);
+ else
+ SVN_ERR(err);
+
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_wc__wq_build_file_move(svn_skel_t **work_item,
+ svn_wc__db_t *db,
+ const char *wri_abspath,
+ const char *src_abspath,
+ const char *dst_abspath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_node_kind_t kind;
+ const char *local_relpath;
+ *work_item = svn_skel__make_empty_list(result_pool);
+
+ SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
+ SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
+ SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
+
+ /* File must exist */
+ SVN_ERR(svn_io_check_path(src_abspath, &kind, result_pool));
+
+ if (kind == svn_node_none)
+ return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
+ _("'%s' not found"),
+ svn_dirent_local_style(src_abspath,
+ scratch_pool));
+
+ SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath, dst_abspath,
+ result_pool, scratch_pool));
+ svn_skel__prepend_str(local_relpath, *work_item, result_pool);
+
+ SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath, src_abspath,
+ result_pool, scratch_pool));
+ svn_skel__prepend_str(local_relpath, *work_item, result_pool);
+
+ svn_skel__prepend_str(OP_FILE_MOVE, *work_item, result_pool);
+
+ return SVN_NO_ERROR;
+}
+
+/* ------------------------------------------------------------------------ */
+
+/* OP_FILE_COPY_TRANSLATED */
+
+/* Process the OP_FILE_COPY_TRANSLATED work item WORK_ITEM.
+ * See run_file_copy_translated() which generates this work item.
+ * Implements (struct work_item_dispatch).func. */
+static svn_error_t *
+run_file_copy_translated(work_item_baton_t *wqb,
+ svn_wc__db_t *db,
+ const svn_skel_t *work_item,
+ const char *wri_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool)
+{
+ const svn_skel_t *arg1 = work_item->children->next;
+ const char *local_abspath, *src_abspath, *dst_abspath;
+ const char *local_relpath;
+ svn_subst_eol_style_t style;
+ const char *eol;
+ apr_hash_t *keywords;
+ svn_boolean_t special;
+
+ local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
+ SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
+ local_relpath, scratch_pool, scratch_pool));
+
+ local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->data,
+ arg1->next->len);
+ SVN_ERR(svn_wc__db_from_relpath(&src_abspath, db, wri_abspath,
+ local_relpath, scratch_pool, scratch_pool));
+
+ local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->next->data,
+ arg1->next->next->len);
+ SVN_ERR(svn_wc__db_from_relpath(&dst_abspath, db, wri_abspath,
+ local_relpath, scratch_pool, scratch_pool));
+
+ SVN_ERR(svn_wc__get_translate_info(&style, &eol,
+ &keywords,
+ &special,
+ db, local_abspath, NULL, FALSE,
+ scratch_pool, scratch_pool));
+
+ SVN_ERR(svn_subst_copy_and_translate4(src_abspath, dst_abspath,
+ eol, TRUE /* repair */,
+ keywords, TRUE /* expand */,
+ special,
+ cancel_func, cancel_baton,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_wc__wq_build_file_copy_translated(svn_skel_t **work_item,
+ svn_wc__db_t *db,
+ const char *local_abspath,
+ const char *src_abspath,
+ const char *dst_abspath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_node_kind_t kind;
+ const char *local_relpath;
+
+ *work_item = svn_skel__make_empty_list(result_pool);
+
+ SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
+ SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
+ SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
+
+ /* File must exist */
+ SVN_ERR(svn_io_check_path(src_abspath, &kind, result_pool));
+
+ if (kind == svn_node_none)
+ return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
+ _("'%s' not found"),
+ svn_dirent_local_style(src_abspath,
+ scratch_pool));
+
+ SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, dst_abspath,
+ result_pool, scratch_pool));
+ svn_skel__prepend_str(local_relpath, *work_item, result_pool);
+
+ SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, src_abspath,
+ result_pool, scratch_pool));
+ svn_skel__prepend_str(local_relpath, *work_item, result_pool);
+
+ SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
+ local_abspath, result_pool, scratch_pool));
+ svn_skel__prepend_str(local_relpath, *work_item, result_pool);
+
+ svn_skel__prepend_str(OP_FILE_COPY_TRANSLATED, *work_item, result_pool);
+
+ return SVN_NO_ERROR;
+}
+
+/* ------------------------------------------------------------------------ */
+
+/* OP_DIRECTORY_INSTALL */
+
+static svn_error_t *
+run_dir_install(work_item_baton_t *wqb,
+ svn_wc__db_t *db,
+ const svn_skel_t *work_item,
+ const char *wri_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool)
+{
+ const svn_skel_t *arg1 = work_item->children->next;
+ const char *local_relpath;
+ const char *local_abspath;
+
+ local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
+ SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
+ local_relpath, scratch_pool, scratch_pool));
+
+ SVN_ERR(svn_wc__ensure_directory(local_abspath, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__wq_build_dir_install(svn_skel_t **work_item,
+ svn_wc__db_t *db,
+ const char *local_abspath,
+ apr_pool_t *scratch_pool,
+ apr_pool_t *result_pool)
+{
+ const char *local_relpath;
+
+ *work_item = svn_skel__make_empty_list(result_pool);
+
+ SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
+ local_abspath, result_pool, scratch_pool));
+ svn_skel__prepend_str(local_relpath, *work_item, result_pool);
+
+ svn_skel__prepend_str(OP_DIRECTORY_INSTALL, *work_item, result_pool);
+
+ return SVN_NO_ERROR;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+/* OP_SYNC_FILE_FLAGS */
+
+/* Process the OP_SYNC_FILE_FLAGS work item WORK_ITEM.
+ * See svn_wc__wq_build_sync_file_flags() which generates this work item.
+ * Implements (struct work_item_dispatch).func. */
+static svn_error_t *
+run_sync_file_flags(work_item_baton_t *wqb,
+ svn_wc__db_t *db,
+ const svn_skel_t *work_item,
+ const char *wri_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool)
+{
+ const svn_skel_t *arg1 = work_item->children->next;
+ const char *local_relpath;
+ const char *local_abspath;
+
+ local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
+ SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
+ local_relpath, scratch_pool, scratch_pool));
+
+ return svn_error_trace(svn_wc__sync_flags_with_props(NULL, db,
+ local_abspath, scratch_pool));
+}
+
+
+svn_error_t *
+svn_wc__wq_build_sync_file_flags(svn_skel_t **work_item,
+ svn_wc__db_t *db,
+ const char *local_abspath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *local_relpath;
+ *work_item = svn_skel__make_empty_list(result_pool);
+
+ SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
+ local_abspath, result_pool, scratch_pool));
+
+ svn_skel__prepend_str(local_relpath, *work_item, result_pool);
+ svn_skel__prepend_str(OP_SYNC_FILE_FLAGS, *work_item, result_pool);
+
+ return SVN_NO_ERROR;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+/* OP_PREJ_INSTALL */
+
+static svn_error_t *
+run_prej_install(work_item_baton_t *wqb,
+ svn_wc__db_t *db,
+ const svn_skel_t *work_item,
+ const char *wri_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool)
+{
+ const svn_skel_t *arg1 = work_item->children->next;
+ const char *local_relpath;
+ const char *local_abspath;
+ svn_skel_t *conflicts;
+ const svn_skel_t *prop_conflict_skel;
+ const char *tmp_prejfile_abspath;
+ const char *prejfile_abspath;
+
+ local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
+ SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
+ local_relpath, scratch_pool, scratch_pool));
+
+ SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath,
+ scratch_pool, scratch_pool));
+
+ SVN_ERR(svn_wc__conflict_read_prop_conflict(&prejfile_abspath,
+ NULL, NULL, NULL, NULL,
+ db, local_abspath, conflicts,
+ scratch_pool, scratch_pool));
+
+ if (arg1->next != NULL)
+ prop_conflict_skel = arg1->next;
+ else
+ SVN_ERR_MALFUNCTION(); /* ### wc_db can't provide it ... yet. */
+
+ /* Construct a property reject file in the temporary area. */
+ SVN_ERR(svn_wc__create_prejfile(&tmp_prejfile_abspath,
+ db, local_abspath,
+ prop_conflict_skel,
+ scratch_pool, scratch_pool));
+
+ /* ... and atomically move it into place. */
+ SVN_ERR(svn_io_file_rename(tmp_prejfile_abspath,
+ prejfile_abspath,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_wc__wq_build_prej_install(svn_skel_t **work_item,
+ svn_wc__db_t *db,
+ const char *local_abspath,
+ svn_skel_t *conflict_skel,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *local_relpath;
+ *work_item = svn_skel__make_empty_list(result_pool);
+
+ /* ### gotta have this, today */
+ SVN_ERR_ASSERT(conflict_skel != NULL);
+
+ SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
+ local_abspath, result_pool, scratch_pool));
+
+ if (conflict_skel != NULL)
+ svn_skel__prepend(conflict_skel, *work_item);
+ svn_skel__prepend_str(local_relpath, *work_item, result_pool);
+ svn_skel__prepend_str(OP_PREJ_INSTALL, *work_item, result_pool);
+
+ return SVN_NO_ERROR;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+/* OP_RECORD_FILEINFO */
+
+
+static svn_error_t *
+run_record_fileinfo(work_item_baton_t *wqb,
+ svn_wc__db_t *db,
+ const svn_skel_t *work_item,
+ const char *wri_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool)
+{
+ const svn_skel_t *arg1 = work_item->children->next;
+ const char *local_relpath;
+ const char *local_abspath;
+ apr_time_t set_time = 0;
+
+ local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
+
+ SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
+ local_relpath, scratch_pool, scratch_pool));
+
+ if (arg1->next)
+ {
+ apr_int64_t val;
+
+ SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
+ set_time = (apr_time_t)val;
+ }
+
+ if (set_time != 0)
+ {
+ svn_node_kind_t kind;
+ svn_boolean_t is_special;
+
+ /* Do not set the timestamp on special files. */
+ SVN_ERR(svn_io_check_special_path(local_abspath, &kind, &is_special,
+ scratch_pool));
+
+ /* Don't set affected time when local_abspath does not exist or is
+ a special file */
+ if (kind == svn_node_file && !is_special)
+ SVN_ERR(svn_io_set_file_affected_time(set_time, local_abspath,
+ scratch_pool));
+
+ /* Note that we can't use the value we get here for recording as the
+ filesystem might have a different timestamp granularity */
+ }
+
+
+ return svn_error_trace(get_and_record_fileinfo(wqb, local_abspath,
+ TRUE /* ignore_enoent */,
+ scratch_pool));
+}
+
+/* ------------------------------------------------------------------------ */
+
+/* OP_TMP_SET_TEXT_CONFLICT_MARKERS */
+
+
+static svn_error_t *
+run_set_text_conflict_markers(work_item_baton_t *wqb,
+ svn_wc__db_t *db,
+ const svn_skel_t *work_item,
+ const char *wri_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool)
+{
+ const svn_skel_t *arg = work_item->children->next;
+ const char *local_relpath;
+ const char *local_abspath;
+ const char *old_abspath = NULL;
+ const char *new_abspath = NULL;
+ const char *wrk_abspath = NULL;
+
+ local_relpath = apr_pstrmemdup(scratch_pool, arg->data, arg->len);
+ SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
+ local_relpath, scratch_pool, scratch_pool));
+
+ arg = arg->next;
+ local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
+ : NULL;
+
+ if (local_relpath)
+ {
+ SVN_ERR(svn_wc__db_from_relpath(&old_abspath, db, wri_abspath,
+ local_relpath,
+ scratch_pool, scratch_pool));
+ }
+
+ arg = arg->next;
+ local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
+ : NULL;
+ if (local_relpath)
+ {
+ SVN_ERR(svn_wc__db_from_relpath(&new_abspath, db, wri_abspath,
+ local_relpath,
+ scratch_pool, scratch_pool));
+ }
+
+ arg = arg->next;
+ local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
+ : NULL;
+
+ if (local_relpath)
+ {
+ SVN_ERR(svn_wc__db_from_relpath(&wrk_abspath, db, wri_abspath,
+ local_relpath,
+ scratch_pool, scratch_pool));
+ }
+
+ /* Upgrade scenario: We have a workqueue item that describes how to install a
+ non skel conflict. Fetch all the information we can to create a new style
+ conflict. */
+ /* ### Before format 30 this is/was a common code path as we didn't install
+ ### the conflict directly in the db. It just calls the wc_db code
+ ### to set the right fields. */
+
+ {
+ /* Check if we should combine with a property conflict... */
+ svn_skel_t *conflicts;
+
+ SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath,
+ scratch_pool, scratch_pool));
+
+ if (! conflicts)
+ {
+ /* No conflict exists, create a basic skel */
+ conflicts = svn_wc__conflict_skel_create(scratch_pool);
+
+ SVN_ERR(svn_wc__conflict_skel_set_op_update(conflicts, NULL, NULL,
+ scratch_pool,
+ scratch_pool));
+ }
+
+ /* Add the text conflict to the existing onflict */
+ SVN_ERR(svn_wc__conflict_skel_add_text_conflict(conflicts, db,
+ local_abspath,
+ wrk_abspath,
+ old_abspath,
+ new_abspath,
+ scratch_pool,
+ scratch_pool));
+
+ SVN_ERR(svn_wc__db_op_mark_conflict(db, local_abspath, conflicts,
+ NULL, scratch_pool));
+ }
+ return SVN_NO_ERROR;
+}
+
+/* ------------------------------------------------------------------------ */
+
+/* OP_TMP_SET_PROPERTY_CONFLICT_MARKER */
+
+static svn_error_t *
+run_set_property_conflict_marker(work_item_baton_t *wqb,
+ svn_wc__db_t *db,
+ const svn_skel_t *work_item,
+ const char *wri_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool)
+{
+ const svn_skel_t *arg = work_item->children->next;
+ const char *local_relpath;
+ const char *local_abspath;
+ const char *prej_abspath = NULL;
+
+ local_relpath = apr_pstrmemdup(scratch_pool, arg->data, arg->len);
+
+ SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
+ local_relpath, scratch_pool, scratch_pool));
+
+
+ arg = arg->next;
+ local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
+ : NULL;
+
+ if (local_relpath)
+ SVN_ERR(svn_wc__db_from_relpath(&prej_abspath, db, wri_abspath,
+ local_relpath,
+ scratch_pool, scratch_pool));
+
+ {
+ /* Check if we should combine with a text conflict... */
+ svn_skel_t *conflicts;
+ apr_hash_t *prop_names;
+
+ SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath,
+ scratch_pool, scratch_pool));
+
+ if (! conflicts)
+ {
+ /* No conflict exists, create a basic skel */
+ conflicts = svn_wc__conflict_skel_create(scratch_pool);
+
+ SVN_ERR(svn_wc__conflict_skel_set_op_update(conflicts, NULL, NULL,
+ scratch_pool,
+ scratch_pool));
+ }
+
+ prop_names = apr_hash_make(scratch_pool);
+ SVN_ERR(svn_wc__conflict_skel_add_prop_conflict(conflicts, db,
+ local_abspath,
+ prej_abspath,
+ NULL, NULL, NULL,
+ prop_names,
+ scratch_pool,
+ scratch_pool));
+
+ SVN_ERR(svn_wc__db_op_mark_conflict(db, local_abspath, conflicts,
+ NULL, scratch_pool));
+ }
+ return SVN_NO_ERROR;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static const struct work_item_dispatch dispatch_table[] = {
+ { OP_FILE_COMMIT, run_file_commit },
+ { OP_FILE_INSTALL, run_file_install },
+ { OP_FILE_REMOVE, run_file_remove },
+ { OP_FILE_MOVE, run_file_move },
+ { OP_FILE_COPY_TRANSLATED, run_file_copy_translated },
+ { OP_SYNC_FILE_FLAGS, run_sync_file_flags },
+ { OP_PREJ_INSTALL, run_prej_install },
+ { OP_DIRECTORY_REMOVE, run_dir_remove },
+ { OP_DIRECTORY_INSTALL, run_dir_install },
+
+ /* Upgrade steps */
+ { OP_POSTUPGRADE, run_postupgrade },
+
+ /* Legacy workqueue items. No longer created */
+ { OP_BASE_REMOVE, run_base_remove },
+ { OP_RECORD_FILEINFO, run_record_fileinfo },
+ { OP_TMP_SET_TEXT_CONFLICT_MARKERS, run_set_text_conflict_markers },
+ { OP_TMP_SET_PROPERTY_CONFLICT_MARKER, run_set_property_conflict_marker },
+
+ /* Sentinel. */
+ { NULL }
+};
+
+struct work_item_baton_t
+{
+ apr_pool_t *result_pool; /* Pool to allocate result in */
+
+ svn_boolean_t used; /* needs reset */
+
+ apr_hash_t *record_map; /* const char * -> svn_io_dirent2_t map */
+};
+
+
+static svn_error_t *
+dispatch_work_item(work_item_baton_t *wqb,
+ svn_wc__db_t *db,
+ const char *wri_abspath,
+ const svn_skel_t *work_item,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool)
+{
+ const struct work_item_dispatch *scan;
+
+ /* Scan the dispatch table for a function to handle this work item. */
+ for (scan = &dispatch_table[0]; scan->name != NULL; ++scan)
+ {
+ if (svn_skel__matches_atom(work_item->children, scan->name))
+ {
+
+#ifdef SVN_DEBUG_WORK_QUEUE
+ SVN_DBG(("dispatch: operation='%s'\n", scan->name));
+#endif
+ SVN_ERR((*scan->func)(wqb, db, work_item, wri_abspath,
+ cancel_func, cancel_baton,
+ scratch_pool));
+
+#ifdef SVN_RUN_WORK_QUEUE_TWICE
+#ifdef SVN_DEBUG_WORK_QUEUE
+ SVN_DBG(("dispatch: operation='%s'\n", scan->name));
+#endif
+ /* Being able to run every workqueue item twice is one
+ requirement for workqueues to be restartable. */
+ SVN_ERR((*scan->func)(db, work_item, wri_abspath,
+ cancel_func, cancel_baton,
+ scratch_pool));
+#endif
+
+ break;
+ }
+ }
+
+ if (scan->name == NULL)
+ {
+ /* We should know about ALL possible work items here. If we do not,
+ then something is wrong. Most likely, some kind of format/code
+ skew. There is nothing more we can do. Erasing or ignoring this
+ work item could leave the WC in an even more broken state.
+
+ Contrary to issue #1581, we cannot simply remove work items and
+ continue, so bail out with an error. */
+ return svn_error_createf(SVN_ERR_WC_BAD_ADM_LOG, NULL,
+ _("Unrecognized work item in the queue"));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_wc__wq_run(svn_wc__db_t *db,
+ const char *wri_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool)
+{
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ apr_uint64_t last_id = 0;
+ work_item_baton_t wib = { 0 };
+ wib.result_pool = svn_pool_create(scratch_pool);
+
+#ifdef SVN_DEBUG_WORK_QUEUE
+ SVN_DBG(("wq_run: wri='%s'\n", wri_abspath));
+ {
+ static int count = 0;
+ const char *count_env_var = getenv("SVN_DEBUG_WORK_QUEUE");
+
+ if (count_env_var && ++count == atoi(count_env_var))
+ return svn_error_create(SVN_ERR_CANCELLED, NULL, "fake cancel");
+ }
+#endif
+
+ while (TRUE)
+ {
+ apr_uint64_t id;
+ svn_skel_t *work_item;
+ svn_error_t *err;
+
+ svn_pool_clear(iterpool);
+
+ if (! wib.used)
+ {
+ /* Make sure to do this *early* in the loop iteration. There may
+ be a LAST_ID that needs to be marked as completed, *before* we
+ start worrying about anything else. */
+ SVN_ERR(svn_wc__db_wq_fetch_next(&id, &work_item, db, wri_abspath,
+ last_id, iterpool, iterpool));
+ }
+ else
+ {
+ /* Make sure to do this *early* in the loop iteration. There may
+ be a LAST_ID that needs to be marked as completed, *before* we
+ start worrying about anything else. */
+ SVN_ERR(svn_wc__db_wq_record_and_fetch_next(&id, &work_item,
+ db, wri_abspath,
+ last_id, wib.record_map,
+ iterpool,
+ wib.result_pool));
+
+ svn_pool_clear(wib.result_pool);
+ wib.record_map = NULL;
+ wib.used = FALSE;
+ }
+
+ /* Stop work queue processing, if requested. A future 'svn cleanup'
+ should be able to continue the processing. Note that we may
+ have WORK_ITEM, but we'll just skip its processing for now. */
+ if (cancel_func)
+ SVN_ERR(cancel_func(cancel_baton));
+
+ /* If we have a WORK_ITEM, then process the sucker. Otherwise,
+ we're done. */
+ if (work_item == NULL)
+ break;
+
+ err = dispatch_work_item(&wib, db, wri_abspath, work_item,
+ cancel_func, cancel_baton, iterpool);
+ if (err)
+ {
+ const char *skel = svn_skel__unparse(work_item, scratch_pool)->data;
+
+ return svn_error_createf(SVN_ERR_WC_BAD_ADM_LOG, err,
+ _("Failed to run the WC DB work queue "
+ "associated with '%s', work item %d %s"),
+ svn_dirent_local_style(wri_abspath,
+ scratch_pool),
+ (int)id, skel);
+ }
+
+ /* The work item finished without error. Mark it completed
+ in the next loop. */
+ last_id = id;
+ }
+
+ svn_pool_destroy(iterpool);
+ return SVN_NO_ERROR;
+}
+
+
+svn_skel_t *
+svn_wc__wq_merge(svn_skel_t *work_item1,
+ svn_skel_t *work_item2,
+ apr_pool_t *result_pool)
+{
+ /* If either argument is NULL, then just return the other. */
+ if (work_item1 == NULL)
+ return work_item2;
+ if (work_item2 == NULL)
+ return work_item1;
+
+ /* We have two items. Figure out how to join them. */
+ if (SVN_WC__SINGLE_WORK_ITEM(work_item1))
+ {
+ if (SVN_WC__SINGLE_WORK_ITEM(work_item2))
+ {
+ /* Both are singular work items. Construct a list, then put
+ both work items into it (in the proper order). */
+
+ svn_skel_t *result = svn_skel__make_empty_list(result_pool);
+
+ svn_skel__prepend(work_item2, result);
+ svn_skel__prepend(work_item1, result);
+ return result;
+ }
+
+ /* WORK_ITEM2 is a list of work items. We can simply shove WORK_ITEM1
+ in the front to keep the ordering. */
+ svn_skel__prepend(work_item1, work_item2);
+ return work_item2;
+ }
+ /* WORK_ITEM1 is a list of work items. */
+
+ if (SVN_WC__SINGLE_WORK_ITEM(work_item2))
+ {
+ /* Put WORK_ITEM2 onto the end of the WORK_ITEM1 list. */
+ svn_skel__append(work_item1, work_item2);
+ return work_item1;
+ }
+
+ /* We have two lists of work items. We need to chain all of the work
+ items into one big list. We will leave behind the WORK_ITEM2 skel,
+ as we only want its children. */
+ svn_skel__append(work_item1, work_item2->children);
+ return work_item1;
+}
+
+
+static svn_error_t *
+get_and_record_fileinfo(work_item_baton_t *wqb,
+ const char *local_abspath,
+ svn_boolean_t ignore_enoent,
+ apr_pool_t *scratch_pool)
+{
+ const svn_io_dirent2_t *dirent;
+
+ SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, ignore_enoent,
+ wqb->result_pool, scratch_pool));
+
+ if (dirent->kind != svn_node_file)
+ return SVN_NO_ERROR;
+
+ wqb->used = TRUE;
+
+ if (! wqb->record_map)
+ wqb->record_map = apr_hash_make(wqb->result_pool);
+
+ svn_hash_sets(wqb->record_map, apr_pstrdup(wqb->result_pool, local_abspath),
+ dirent);
+
+ return SVN_NO_ERROR;
+}
OpenPOWER on IntegriCloud