| /* |
| * 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_private_config.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 "svn_path.h" |
| |
| #include "wc.h" |
| #include "wc_db.h" |
| #include "workqueue.h" |
| #include "adm_files.h" |
| #include "conflicts.h" |
| #include "translate.h" |
| |
| #include "private/svn_io_private.h" |
| #include "private/svn_wc_private.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 |
| { |
| apr_pool_t *result_pool; /* Pool to allocate result in */ |
| |
| svn_boolean_t used; /* needs reset */ |
| |
| apr_hash_t *record_map; /* const char * -> svn_wc__db_fileinfo_t map */ |
| } 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); |
| }; |
| |
| static void |
| wq_record_fileinfo(work_item_baton_t *wqb, |
| const char *local_abspath, |
| apr_time_t mtime, |
| svn_filesize_t size) |
| { |
| svn_wc__db_fileinfo_t *info; |
| |
| wqb->used = TRUE; |
| |
| if (! wqb->record_map) |
| wqb->record_map = apr_hash_make(wqb->result_pool); |
| |
| info = apr_pcalloc(wqb->result_pool, sizeof(*info)); |
| info->mtime = mtime; |
| info->size = size; |
| |
| svn_hash_sets(wqb->record_map, apr_pstrdup(wqb->result_pool, local_abspath), |
| info); |
| } |
| |
| /* ------------------------------------------------------------------------ */ |
| /* 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, |
| ¬_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 */, |
| SVN_IS_VALID_REVNUM(not_present_rev), FALSE, |
| 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_rename2(tmp_wfile, file_abspath, FALSE, |
| 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; |
| 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)); |
| |
| 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_atomic2(format_path, SVN_WC__NON_ENTRIES_STRING, |
| sizeof(SVN_WC__NON_ENTRIES_STRING) - 1, |
| NULL, TRUE, scratch_pool)); |
| |
| SVN_ERR(svn_io_write_atomic2(entries_path, SVN_WC__NON_ENTRIES_STRING, |
| sizeof(SVN_WC__NON_ENTRIES_STRING) - 1, |
| NULL, TRUE, 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_stream_t *src_stream; |
| const char *temp_dir_abspath; |
| apr_int64_t val; |
| const char *wcroot_abspath; |
| const char *source_abspath; |
| const svn_checksum_t *checksum; |
| apr_hash_t *props; |
| svn_boolean_t is_special; |
| svn_boolean_t is_executable; |
| svn_boolean_t needs_lock; |
| const char *eol_propval; |
| svn_subst_eol_style_t eol_style; |
| const char *eol; |
| const char *keywords_propval; |
| apr_hash_t *keywords; |
| apr_time_t changed_date; |
| svn_revnum_t changed_rev; |
| const char *changed_author; |
| apr_time_t final_mtime; |
| svn_wc__db_status_t status; |
| svn_wc__db_lock_t *lock; |
| const char *repos_relpath; |
| const char *repos_root_url; |
| svn_wc__working_file_writer_t *file_writer; |
| apr_time_t record_mtime; |
| apr_off_t record_size; |
| svn_boolean_t is_readonly; |
| |
| 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)); |
| } |
| |
| /* 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)); |
| |
| SVN_ERR(svn_wc__db_read_info(&status, NULL, NULL, &repos_relpath, |
| &repos_root_url, NULL, &changed_rev, NULL, |
| &changed_author, 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)); |
| /* Handle special statuses (e.g. added) */ |
| if (!repos_relpath) |
| SVN_ERR(svn_wc__db_read_repos_info(NULL, &repos_relpath, |
| &repos_root_url, NULL, |
| db, local_abspath, |
| scratch_pool, scratch_pool)); |
| |
| is_special = svn_prop_get_value(props, SVN_PROP_SPECIAL) != NULL; |
| is_executable = svn_prop_get_value(props, SVN_PROP_EXECUTABLE) != NULL; |
| needs_lock = svn_prop_get_value(props, SVN_PROP_NEEDS_LOCK) != NULL; |
| |
| eol_propval = svn_prop_get_value(props, SVN_PROP_EOL_STYLE); |
| svn_subst_eol_style_from_value(&eol_style, &eol, eol_propval); |
| |
| keywords_propval = svn_prop_get_value(props, SVN_PROP_KEYWORDS); |
| if (keywords_propval) |
| { |
| const char *url = |
| svn_path_url_add_component2(repos_root_url, repos_relpath, scratch_pool); |
| |
| SVN_ERR(svn_subst_build_keywords3(&keywords, keywords_propval, |
| apr_psprintf(scratch_pool, "%ld", |
| changed_rev), |
| url, repos_root_url, changed_date, |
| changed_author, scratch_pool)); |
| } |
| else |
| { |
| keywords = NULL; |
| } |
| |
| if (use_commit_times && changed_date) |
| final_mtime = changed_date; |
| else |
| final_mtime = -1; |
| |
| if (needs_lock && !lock && status != svn_wc__db_status_added) |
| is_readonly = TRUE; |
| else |
| is_readonly = FALSE; |
| |
| SVN_ERR(svn_wc__working_file_writer_open(&file_writer, |
| temp_dir_abspath, |
| final_mtime, |
| eol_style, |
| eol, |
| TRUE /* repair_eol */, |
| keywords, |
| is_special, |
| is_executable, |
| is_readonly, |
| scratch_pool, |
| scratch_pool)); |
| |
| SVN_ERR(svn_stream_open_readonly(&src_stream, source_abspath, |
| scratch_pool, scratch_pool)); |
| |
| SVN_ERR(svn_stream_copy3(src_stream, |
| svn_wc__working_file_writer_get_stream(file_writer), |
| cancel_func, cancel_baton, |
| scratch_pool)); |
| |
| if (record_fileinfo) |
| { |
| SVN_ERR(svn_wc__working_file_writer_finalize(&record_mtime, &record_size, |
| file_writer, scratch_pool)); |
| } |
| else |
| { |
| SVN_ERR(svn_wc__working_file_writer_finalize(NULL, NULL, file_writer, |
| scratch_pool)); |
| record_mtime = -1; |
| record_size = -1; |
| } |
| |
| SVN_ERR(svn_wc__working_file_writer_install(file_writer, local_abspath, |
| scratch_pool)); |
| |
| if (record_fileinfo) |
| { |
| wq_record_fileinfo(wqb, local_abspath, record_mtime, record_size); |
| } |
| |
| 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 *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_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, NULL, NULL, 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; /* Before Subversion 1.9 */ |
| else |
| prop_conflict_skel = NULL; /* Read from DB */ |
| |
| /* Construct a property reject file in the temporary area. */ |
| SVN_ERR(svn_wc__create_prejfile(&tmp_prejfile_abspath, |
| db, local_abspath, |
| prop_conflict_skel, |
| cancel_func, cancel_baton, |
| scratch_pool, scratch_pool)); |
| |
| /* ... and atomically move it into place. */ |
| SVN_ERR(svn_io_file_rename2(tmp_prejfile_abspath, |
| prejfile_abspath, FALSE, |
| 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); |
| |
| SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, |
| local_abspath, result_pool, scratch_pool)); |
| |
| /* ### In Subversion 1.7 and 1.8 we created a legacy property conflict skel |
| here: |
| 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; |
| const svn_io_dirent2_t *dirent; |
| |
| 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 */ |
| } |
| |
| SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, |
| FALSE /* verify_truename */, |
| TRUE /* ignore_enoent */, |
| scratch_pool, scratch_pool)); |
| |
| if (dirent->kind == svn_node_file) |
| { |
| wq_record_fileinfo(wqb, local_abspath, dirent->mtime, dirent->filesize); |
| } |
| |
| return SVN_NO_ERROR; |
| } |
| |
| /* ------------------------------------------------------------------------ */ |
| |
| /* 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, NULL, NULL, 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, NULL, NULL, |
| 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 } |
| }; |
| |
| 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"); |
| int count_env_val; |
| |
| SVN_ERR(svn_cstring_atoi(&count_env_val, count_env_var)); |
| |
| if (count_env_var && ++count == count_env_val) |
| 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; |
| } |