1/* 2 * workqueue.c : manipulating work queue items 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24#include <apr_pools.h> 25 26#include "svn_private_config.h" 27#include "svn_types.h" 28#include "svn_pools.h" 29#include "svn_dirent_uri.h" 30#include "svn_subst.h" 31#include "svn_hash.h" 32#include "svn_io.h" 33 34#include "wc.h" 35#include "wc_db.h" 36#include "workqueue.h" 37#include "adm_files.h" 38#include "conflicts.h" 39#include "translate.h" 40 41#include "private/svn_io_private.h" 42#include "private/svn_skel.h" 43 44 45/* Workqueue operation names. */ 46#define OP_FILE_COMMIT "file-commit" 47#define OP_FILE_INSTALL "file-install" 48#define OP_FILE_REMOVE "file-remove" 49#define OP_FILE_MOVE "file-move" 50#define OP_FILE_COPY_TRANSLATED "file-translate" 51#define OP_SYNC_FILE_FLAGS "sync-file-flags" 52#define OP_PREJ_INSTALL "prej-install" 53#define OP_DIRECTORY_REMOVE "dir-remove" 54#define OP_DIRECTORY_INSTALL "dir-install" 55 56#define OP_POSTUPGRADE "postupgrade" 57 58/* Legacy items */ 59#define OP_BASE_REMOVE "base-remove" 60#define OP_RECORD_FILEINFO "record-fileinfo" 61#define OP_TMP_SET_TEXT_CONFLICT_MARKERS "tmp-set-text-conflict-markers" 62#define OP_TMP_SET_PROPERTY_CONFLICT_MARKER "tmp-set-property-conflict-marker" 63 64/* For work queue debugging. Generates output about its operation. */ 65/* #define SVN_DEBUG_WORK_QUEUE */ 66 67typedef struct work_item_baton_t work_item_baton_t; 68 69struct work_item_dispatch { 70 const char *name; 71 svn_error_t *(*func)(work_item_baton_t *wqb, 72 svn_wc__db_t *db, 73 const svn_skel_t *work_item, 74 const char *wri_abspath, 75 svn_cancel_func_t cancel_func, 76 void *cancel_baton, 77 apr_pool_t *scratch_pool); 78}; 79 80/* Forward definition */ 81static svn_error_t * 82get_and_record_fileinfo(work_item_baton_t *wqb, 83 const char *local_abspath, 84 svn_boolean_t ignore_enoent, 85 apr_pool_t *scratch_pool); 86 87/* ------------------------------------------------------------------------ */ 88/* OP_REMOVE_BASE */ 89 90/* Removes a BASE_NODE and all it's data, leaving any adds and copies as is. 91 Do this as a depth first traversal to make sure than any parent still exists 92 on error conditions. 93 */ 94 95/* Process the OP_REMOVE_BASE work item WORK_ITEM. 96 * See svn_wc__wq_build_remove_base() which generates this work item. 97 * Implements (struct work_item_dispatch).func. */ 98static svn_error_t * 99run_base_remove(work_item_baton_t *wqb, 100 svn_wc__db_t *db, 101 const svn_skel_t *work_item, 102 const char *wri_abspath, 103 svn_cancel_func_t cancel_func, 104 void *cancel_baton, 105 apr_pool_t *scratch_pool) 106{ 107 const svn_skel_t *arg1 = work_item->children->next; 108 const char *local_relpath; 109 const char *local_abspath; 110 svn_revnum_t not_present_rev = SVN_INVALID_REVNUM; 111 apr_int64_t val; 112 113 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len); 114 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath, 115 local_relpath, scratch_pool, scratch_pool)); 116 SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool)); 117 118 if (arg1->next->next) 119 { 120 not_present_rev = (svn_revnum_t)val; 121 122 SVN_ERR(svn_skel__parse_int(&val, arg1->next->next, scratch_pool)); 123 } 124 else 125 { 126 svn_boolean_t keep_not_present; 127 128 SVN_ERR_ASSERT(SVN_WC__VERSION <= 28); /* Case unused in later versions*/ 129 130 keep_not_present = (val != 0); 131 132 if (keep_not_present) 133 { 134 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, 135 ¬_present_rev, NULL, 136 NULL, NULL, NULL, 137 NULL, NULL, NULL, NULL, NULL, NULL, 138 NULL, NULL, NULL, 139 db, local_abspath, 140 scratch_pool, scratch_pool)); 141 } 142 } 143 144 SVN_ERR(svn_wc__db_base_remove(db, local_abspath, 145 FALSE /* keep_as_working */, 146 SVN_IS_VALID_REVNUM(not_present_rev), FALSE, 147 not_present_rev, 148 NULL, NULL, scratch_pool)); 149 150 return SVN_NO_ERROR; 151} 152 153/* ------------------------------------------------------------------------ */ 154 155/* ------------------------------------------------------------------------ */ 156 157/* OP_FILE_COMMIT */ 158 159 160/* FILE_ABSPATH is the new text base of the newly-committed versioned file, 161 * in repository-normal form (aka "detranslated" form). Adjust the working 162 * file accordingly. 163 * 164 * If eol and/or keyword translation would cause the working file to 165 * change, then overwrite the working file with a translated copy of 166 * the new text base (but only if the translated copy differs from the 167 * current working file -- if they are the same, do nothing, to avoid 168 * clobbering timestamps unnecessarily). 169 * 170 * Set the working file's executability according to its svn:executable 171 * property. 172 * 173 * Set the working file's read-only attribute according to its properties 174 * and lock status (see svn_wc__maybe_set_read_only()). 175 * 176 * If the working file was re-translated or had its executability or 177 * read-only state changed, 178 * then set OVERWROTE_WORKING to TRUE. If the working file isn't 179 * touched at all, then set to FALSE. 180 * 181 * Use SCRATCH_POOL for any temporary allocation. 182 */ 183static svn_error_t * 184install_committed_file(svn_boolean_t *overwrote_working, 185 svn_wc__db_t *db, 186 const char *file_abspath, 187 svn_cancel_func_t cancel_func, 188 void *cancel_baton, 189 apr_pool_t *scratch_pool) 190{ 191 svn_boolean_t same; 192 const char *tmp_wfile; 193 svn_boolean_t special; 194 195 /* start off assuming that the working file isn't touched. */ 196 *overwrote_working = FALSE; 197 198 /* In the commit, newlines and keywords may have been 199 * canonicalized and/or contracted... Or they may not have 200 * been. It's kind of hard to know. Here's how we find out: 201 * 202 * 1. Make a translated tmp copy of the committed text base, 203 * translated according to the versioned file's properties. 204 * Or, if no committed text base exists (the commit must have 205 * been a propchange only), make a translated tmp copy of the 206 * working file. 207 * 2. Compare the translated tmpfile to the working file. 208 * 3. If different, copy the tmpfile over working file. 209 * 210 * This means we only rewrite the working file if we absolutely 211 * have to, which is good because it avoids changing the file's 212 * timestamp unless necessary, so editors aren't tempted to 213 * reread the file if they don't really need to. 214 */ 215 216 /* Copy and translate the new base-to-be file (if found, else the working 217 * file) from repository-normal form to working form, writing a new 218 * temporary file if any translation was actually done. Set TMP_WFILE to 219 * the translated file's path, which may be the source file's path if no 220 * translation was done. Set SAME to indicate whether the new working 221 * text is the same as the old working text (or TRUE if it's a special 222 * file). */ 223 { 224 const char *tmp = file_abspath; 225 226 /* Copy and translate, if necessary. The output file will be deleted at 227 * scratch_pool cleanup. 228 * ### That's not quite safe: we might rename the file and then maybe 229 * its path will get re-used for another temp file before pool clean-up. 230 * Instead, we should take responsibility for deleting it. */ 231 SVN_ERR(svn_wc__internal_translated_file(&tmp_wfile, tmp, db, 232 file_abspath, 233 SVN_WC_TRANSLATE_FROM_NF, 234 cancel_func, cancel_baton, 235 scratch_pool, scratch_pool)); 236 237 /* If the translation is a no-op, the text base and the working copy 238 * file contain the same content, because we use the same props here 239 * as were used to detranslate from working file to text base. 240 * 241 * In that case: don't replace the working file, but make sure 242 * it has the right executable and read_write attributes set. 243 */ 244 245 SVN_ERR(svn_wc__get_translate_info(NULL, NULL, 246 NULL, 247 &special, 248 db, file_abspath, NULL, FALSE, 249 scratch_pool, scratch_pool)); 250 /* Translated file returns the exact pointer if not translated. */ 251 if (! special && tmp != tmp_wfile) 252 SVN_ERR(svn_io_files_contents_same_p(&same, tmp_wfile, 253 file_abspath, scratch_pool)); 254 else 255 same = TRUE; 256 } 257 258 if (! same) 259 { 260 SVN_ERR(svn_io_file_rename2(tmp_wfile, file_abspath, FALSE, 261 scratch_pool)); 262 *overwrote_working = TRUE; 263 } 264 265 /* ### should be using OP_SYNC_FILE_FLAGS, or an internal version of 266 ### that here. do we need to set *OVERWROTE_WORKING? */ 267 268 /* ### Re: OVERWROTE_WORKING, the following function is rather liberal 269 ### with setting that flag, so we should probably decide if we really 270 ### care about it when syncing flags. */ 271 SVN_ERR(svn_wc__sync_flags_with_props(overwrote_working, db, file_abspath, 272 scratch_pool)); 273 274 return SVN_NO_ERROR; 275} 276 277static svn_error_t * 278process_commit_file_install(svn_wc__db_t *db, 279 const char *local_abspath, 280 svn_cancel_func_t cancel_func, 281 void *cancel_baton, 282 apr_pool_t *scratch_pool) 283{ 284 svn_boolean_t overwrote_working; 285 286 /* Install the new file, which may involve expanding keywords. 287 A copy of this file should have been dropped into our `tmp/text-base' 288 directory during the commit process. Part of this process 289 involves recording the textual timestamp for this entry. We'd like 290 to just use the timestamp of the working file, but it is possible 291 that at some point during the commit, the real working file might 292 have changed again. 293 */ 294 295 SVN_ERR(install_committed_file(&overwrote_working, db, 296 local_abspath, 297 cancel_func, cancel_baton, 298 scratch_pool)); 299 300 /* We will compute and modify the size and timestamp */ 301 if (overwrote_working) 302 { 303 apr_finfo_t finfo; 304 305 SVN_ERR(svn_io_stat(&finfo, local_abspath, 306 APR_FINFO_MIN | APR_FINFO_LINK, scratch_pool)); 307 SVN_ERR(svn_wc__db_global_record_fileinfo(db, local_abspath, 308 finfo.size, finfo.mtime, 309 scratch_pool)); 310 } 311 else 312 { 313 svn_boolean_t modified; 314 315 /* The working copy file hasn't been overwritten. We just 316 removed the recorded size and modification time from the nodes 317 record by calling svn_wc__db_global_commit(). 318 319 Now we have some file in our working copy that might be what 320 we just committed, but we are not certain at this point. 321 322 We still have a write lock here, so we check if the file is 323 what we expect it to be and if it is the right file we update 324 the recorded information. (If it isn't we keep the null data). 325 326 Instead of reimplementing all this here, we just call a function 327 that already does implement this when it notices that we have the 328 right kind of lock (and we ignore the result) 329 */ 330 SVN_ERR(svn_wc__internal_file_modified_p(&modified, 331 db, local_abspath, FALSE, 332 scratch_pool)); 333 } 334 return SVN_NO_ERROR; 335} 336 337 338static svn_error_t * 339run_file_commit(work_item_baton_t *wqb, 340 svn_wc__db_t *db, 341 const svn_skel_t *work_item, 342 const char *wri_abspath, 343 svn_cancel_func_t cancel_func, 344 void *cancel_baton, 345 apr_pool_t *scratch_pool) 346{ 347 const svn_skel_t *arg1 = work_item->children->next; 348 const char *local_relpath; 349 const char *local_abspath; 350 351 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len); 352 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath, 353 local_relpath, scratch_pool, scratch_pool)); 354 355 /* We don't both parsing the other two values in the skel. */ 356 357 return svn_error_trace( 358 process_commit_file_install(db, local_abspath, 359 cancel_func, cancel_baton, 360 scratch_pool)); 361} 362 363svn_error_t * 364svn_wc__wq_build_file_commit(svn_skel_t **work_item, 365 svn_wc__db_t *db, 366 const char *local_abspath, 367 svn_boolean_t props_mod, 368 apr_pool_t *result_pool, 369 apr_pool_t *scratch_pool) 370{ 371 const char *local_relpath; 372 *work_item = svn_skel__make_empty_list(result_pool); 373 374 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, 375 local_abspath, result_pool, scratch_pool)); 376 377 svn_skel__prepend_str(local_relpath, *work_item, result_pool); 378 379 svn_skel__prepend_str(OP_FILE_COMMIT, *work_item, result_pool); 380 381 return SVN_NO_ERROR; 382} 383 384/* ------------------------------------------------------------------------ */ 385/* OP_POSTUPGRADE */ 386 387static svn_error_t * 388run_postupgrade(work_item_baton_t *wqb, 389 svn_wc__db_t *db, 390 const svn_skel_t *work_item, 391 const char *wri_abspath, 392 svn_cancel_func_t cancel_func, 393 void *cancel_baton, 394 apr_pool_t *scratch_pool) 395{ 396 const char *entries_path; 397 const char *format_path; 398 const char *wcroot_abspath; 399 svn_error_t *err; 400 401 err = svn_wc__wipe_postupgrade(wri_abspath, FALSE, 402 cancel_func, cancel_baton, scratch_pool); 403 if (err && err->apr_err == SVN_ERR_ENTRY_NOT_FOUND) 404 /* No entry, this can happen when the wq item is rerun. */ 405 svn_error_clear(err); 406 else 407 SVN_ERR(err); 408 409 SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, db, wri_abspath, 410 scratch_pool, scratch_pool)); 411 412 entries_path = svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_ENTRIES, 413 scratch_pool); 414 format_path = svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_FORMAT, 415 scratch_pool); 416 417 /* Write the 'format' and 'entries' files. 418 419 ### The order may matter for some sufficiently old clients.. but 420 ### this code only runs during upgrade after the files had been 421 ### removed earlier during the upgrade. */ 422 SVN_ERR(svn_io_write_atomic2(format_path, SVN_WC__NON_ENTRIES_STRING, 423 sizeof(SVN_WC__NON_ENTRIES_STRING) - 1, 424 NULL, TRUE, scratch_pool)); 425 426 SVN_ERR(svn_io_write_atomic2(entries_path, SVN_WC__NON_ENTRIES_STRING, 427 sizeof(SVN_WC__NON_ENTRIES_STRING) - 1, 428 NULL, TRUE, scratch_pool)); 429 430 return SVN_NO_ERROR; 431} 432 433svn_error_t * 434svn_wc__wq_build_postupgrade(svn_skel_t **work_item, 435 apr_pool_t *result_pool) 436{ 437 *work_item = svn_skel__make_empty_list(result_pool); 438 439 svn_skel__prepend_str(OP_POSTUPGRADE, *work_item, result_pool); 440 441 return SVN_NO_ERROR; 442} 443 444/* ------------------------------------------------------------------------ */ 445 446/* OP_FILE_INSTALL */ 447 448/* Process the OP_FILE_INSTALL work item WORK_ITEM. 449 * See svn_wc__wq_build_file_install() which generates this work item. 450 * Implements (struct work_item_dispatch).func. */ 451static svn_error_t * 452run_file_install(work_item_baton_t *wqb, 453 svn_wc__db_t *db, 454 const svn_skel_t *work_item, 455 const char *wri_abspath, 456 svn_cancel_func_t cancel_func, 457 void *cancel_baton, 458 apr_pool_t *scratch_pool) 459{ 460 const svn_skel_t *arg1 = work_item->children->next; 461 const svn_skel_t *arg4 = arg1->next->next->next; 462 const char *local_relpath; 463 const char *local_abspath; 464 svn_boolean_t use_commit_times; 465 svn_boolean_t record_fileinfo; 466 svn_boolean_t special; 467 svn_stream_t *src_stream; 468 svn_subst_eol_style_t style; 469 const char *eol; 470 apr_hash_t *keywords; 471 const char *temp_dir_abspath; 472 svn_stream_t *dst_stream; 473 apr_int64_t val; 474 const char *wcroot_abspath; 475 const char *source_abspath; 476 const svn_checksum_t *checksum; 477 apr_hash_t *props; 478 apr_time_t changed_date; 479 480 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len); 481 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath, 482 local_relpath, scratch_pool, scratch_pool)); 483 484 SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool)); 485 use_commit_times = (val != 0); 486 SVN_ERR(svn_skel__parse_int(&val, arg1->next->next, scratch_pool)); 487 record_fileinfo = (val != 0); 488 489 SVN_ERR(svn_wc__db_read_node_install_info(&wcroot_abspath, 490 &checksum, &props, 491 &changed_date, 492 db, local_abspath, wri_abspath, 493 scratch_pool, scratch_pool)); 494 495 if (arg4 != NULL) 496 { 497 /* Use the provided path for the source. */ 498 local_relpath = apr_pstrmemdup(scratch_pool, arg4->data, arg4->len); 499 SVN_ERR(svn_wc__db_from_relpath(&source_abspath, db, wri_abspath, 500 local_relpath, 501 scratch_pool, scratch_pool)); 502 } 503 else if (! checksum) 504 { 505 /* This error replaces a previous assertion. Reporting an error from here 506 leaves the workingqueue operation in place, so the working copy is 507 still broken! 508 509 But when we report this error the user at least knows what node has 510 this specific problem, so maybe we can find out why users see this 511 error */ 512 return svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL, 513 _("Can't install '%s' from pristine store, " 514 "because no checksum is recorded for this " 515 "file"), 516 svn_dirent_local_style(local_abspath, 517 scratch_pool)); 518 } 519 else 520 { 521 SVN_ERR(svn_wc__db_pristine_get_future_path(&source_abspath, 522 wcroot_abspath, 523 checksum, 524 scratch_pool, scratch_pool)); 525 } 526 527 SVN_ERR(svn_stream_open_readonly(&src_stream, source_abspath, 528 scratch_pool, scratch_pool)); 529 530 /* Fetch all the translation bits. */ 531 SVN_ERR(svn_wc__get_translate_info(&style, &eol, 532 &keywords, 533 &special, db, local_abspath, 534 props, FALSE, 535 scratch_pool, scratch_pool)); 536 if (special) 537 { 538 /* When this stream is closed, the resulting special file will 539 atomically be created/moved into place at LOCAL_ABSPATH. */ 540 SVN_ERR(svn_subst_create_specialfile(&dst_stream, local_abspath, 541 scratch_pool, scratch_pool)); 542 543 /* Copy the "repository normal" form of the special file into the 544 special stream. */ 545 SVN_ERR(svn_stream_copy3(src_stream, dst_stream, 546 cancel_func, cancel_baton, 547 scratch_pool)); 548 549 /* No need to set exec or read-only flags on special files. */ 550 551 /* ### Shouldn't this record a timestamp and size, etc.? */ 552 return SVN_NO_ERROR; 553 } 554 555 if (svn_subst_translation_required(style, eol, keywords, 556 FALSE /* special */, 557 TRUE /* force_eol_check */)) 558 { 559 /* Wrap it in a translating (expanding) stream. */ 560 src_stream = svn_subst_stream_translated(src_stream, eol, 561 TRUE /* repair */, 562 keywords, 563 TRUE /* expand */, 564 scratch_pool); 565 } 566 567 /* Where is the Right Place to put a temp file in this working copy? */ 568 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, 569 db, wcroot_abspath, 570 scratch_pool, scratch_pool)); 571 572 /* Translate to a temporary file. We don't want the user seeing a partial 573 file, nor let them muck with it while we translate. We may also need to 574 get its TRANSLATED_SIZE before the user can monkey it. */ 575 SVN_ERR(svn_stream__create_for_install(&dst_stream, temp_dir_abspath, 576 scratch_pool, scratch_pool)); 577 578 /* Copy from the source to the dest, translating as we go. This will also 579 close both streams. */ 580 SVN_ERR(svn_stream_copy3(src_stream, dst_stream, 581 cancel_func, cancel_baton, 582 scratch_pool)); 583 584 /* All done. Move the file into place. */ 585 /* With a single db we might want to install files in a missing directory. 586 Simply trying this scenario on error won't do any harm and at least 587 one user reported this problem on IRC. */ 588 SVN_ERR(svn_stream__install_stream(dst_stream, local_abspath, 589 TRUE /* make_parents*/, scratch_pool)); 590 591 /* Tweak the on-disk file according to its properties. */ 592#ifndef WIN32 593 if (props && svn_hash_gets(props, SVN_PROP_EXECUTABLE)) 594 SVN_ERR(svn_io_set_file_executable(local_abspath, TRUE, FALSE, 595 scratch_pool)); 596#endif 597 598 /* Note that this explicitly checks the pristine properties, to make sure 599 that when the lock is locally set (=modification) it is not read only */ 600 if (props && svn_hash_gets(props, SVN_PROP_NEEDS_LOCK)) 601 { 602 svn_wc__db_status_t status; 603 svn_wc__db_lock_t *lock; 604 SVN_ERR(svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL, 605 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 606 NULL, NULL, &lock, NULL, NULL, NULL, NULL, 607 NULL, NULL, NULL, NULL, NULL, NULL, 608 db, local_abspath, 609 scratch_pool, scratch_pool)); 610 611 if (!lock && status != svn_wc__db_status_added) 612 SVN_ERR(svn_io_set_file_read_only(local_abspath, FALSE, scratch_pool)); 613 } 614 615 if (use_commit_times) 616 { 617 if (changed_date) 618 SVN_ERR(svn_io_set_file_affected_time(changed_date, 619 local_abspath, 620 scratch_pool)); 621 } 622 623 /* ### this should happen before we rename the file into place. */ 624 if (record_fileinfo) 625 { 626 SVN_ERR(get_and_record_fileinfo(wqb, local_abspath, 627 FALSE /* ignore_enoent */, 628 scratch_pool)); 629 } 630 631 return SVN_NO_ERROR; 632} 633 634 635svn_error_t * 636svn_wc__wq_build_file_install(svn_skel_t **work_item, 637 svn_wc__db_t *db, 638 const char *local_abspath, 639 const char *source_abspath, 640 svn_boolean_t use_commit_times, 641 svn_boolean_t record_fileinfo, 642 apr_pool_t *result_pool, 643 apr_pool_t *scratch_pool) 644{ 645 const char *local_relpath; 646 const char *wri_abspath; 647 *work_item = svn_skel__make_empty_list(result_pool); 648 649 /* Use the directory of the file to install as wri_abspath to avoid 650 filestats on just obtaining the wc-root */ 651 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool); 652 653 /* If a SOURCE_ABSPATH was provided, then put it into the skel. If this 654 value is not provided, then the file's pristine contents will be used. */ 655 if (source_abspath != NULL) 656 { 657 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath, 658 source_abspath, 659 result_pool, scratch_pool)); 660 661 svn_skel__prepend_str(local_relpath, *work_item, result_pool); 662 } 663 664 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath, 665 local_abspath, result_pool, scratch_pool)); 666 667 svn_skel__prepend_int(record_fileinfo, *work_item, result_pool); 668 svn_skel__prepend_int(use_commit_times, *work_item, result_pool); 669 svn_skel__prepend_str(local_relpath, *work_item, result_pool); 670 svn_skel__prepend_str(OP_FILE_INSTALL, *work_item, result_pool); 671 672 return SVN_NO_ERROR; 673} 674 675 676/* ------------------------------------------------------------------------ */ 677 678/* OP_FILE_REMOVE */ 679 680/* Process the OP_FILE_REMOVE work item WORK_ITEM. 681 * See svn_wc__wq_build_file_remove() which generates this work item. 682 * Implements (struct work_item_dispatch).func. */ 683static svn_error_t * 684run_file_remove(work_item_baton_t *wqb, 685 svn_wc__db_t *db, 686 const svn_skel_t *work_item, 687 const char *wri_abspath, 688 svn_cancel_func_t cancel_func, 689 void *cancel_baton, 690 apr_pool_t *scratch_pool) 691{ 692 const svn_skel_t *arg1 = work_item->children->next; 693 const char *local_relpath; 694 const char *local_abspath; 695 696 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len); 697 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath, 698 local_relpath, scratch_pool, scratch_pool)); 699 700 /* Remove the path, no worrying if it isn't there. */ 701 return svn_error_trace(svn_io_remove_file2(local_abspath, TRUE, 702 scratch_pool)); 703} 704 705 706svn_error_t * 707svn_wc__wq_build_file_remove(svn_skel_t **work_item, 708 svn_wc__db_t *db, 709 const char *wri_abspath, 710 const char *local_abspath, 711 apr_pool_t *result_pool, 712 apr_pool_t *scratch_pool) 713{ 714 const char *local_relpath; 715 *work_item = svn_skel__make_empty_list(result_pool); 716 717 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath, 718 local_abspath, result_pool, scratch_pool)); 719 720 svn_skel__prepend_str(local_relpath, *work_item, result_pool); 721 svn_skel__prepend_str(OP_FILE_REMOVE, *work_item, result_pool); 722 723 return SVN_NO_ERROR; 724} 725 726/* ------------------------------------------------------------------------ */ 727 728/* OP_DIRECTORY_REMOVE */ 729 730/* Process the OP_FILE_REMOVE work item WORK_ITEM. 731 * See svn_wc__wq_build_file_remove() which generates this work item. 732 * Implements (struct work_item_dispatch).func. */ 733static svn_error_t * 734run_dir_remove(work_item_baton_t *wqb, 735 svn_wc__db_t *db, 736 const svn_skel_t *work_item, 737 const char *wri_abspath, 738 svn_cancel_func_t cancel_func, 739 void *cancel_baton, 740 apr_pool_t *scratch_pool) 741{ 742 const svn_skel_t *arg1 = work_item->children->next; 743 const char *local_relpath; 744 const char *local_abspath; 745 svn_boolean_t recursive; 746 747 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len); 748 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath, 749 local_relpath, scratch_pool, scratch_pool)); 750 751 recursive = FALSE; 752 if (arg1->next) 753 { 754 apr_int64_t val; 755 SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool)); 756 757 recursive = (val != 0); 758 } 759 760 /* Remove the path, no worrying if it isn't there. */ 761 if (recursive) 762 return svn_error_trace( 763 svn_io_remove_dir2(local_abspath, TRUE, 764 cancel_func, cancel_baton, 765 scratch_pool)); 766 else 767 { 768 svn_error_t *err; 769 770 err = svn_io_dir_remove_nonrecursive(local_abspath, scratch_pool); 771 772 if (err && (APR_STATUS_IS_ENOENT(err->apr_err) 773 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err) 774 || APR_STATUS_IS_ENOTEMPTY(err->apr_err))) 775 { 776 svn_error_clear(err); 777 err = NULL; 778 } 779 780 return svn_error_trace(err); 781 } 782} 783 784svn_error_t * 785svn_wc__wq_build_dir_remove(svn_skel_t **work_item, 786 svn_wc__db_t *db, 787 const char *wri_abspath, 788 const char *local_abspath, 789 svn_boolean_t recursive, 790 apr_pool_t *result_pool, 791 apr_pool_t *scratch_pool) 792{ 793 const char *local_relpath; 794 *work_item = svn_skel__make_empty_list(result_pool); 795 796 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath, 797 local_abspath, result_pool, scratch_pool)); 798 799 if (recursive) 800 svn_skel__prepend_int(TRUE, *work_item, result_pool); 801 802 svn_skel__prepend_str(local_relpath, *work_item, result_pool); 803 svn_skel__prepend_str(OP_DIRECTORY_REMOVE, *work_item, result_pool); 804 805 return SVN_NO_ERROR; 806} 807 808/* ------------------------------------------------------------------------ */ 809 810/* OP_FILE_MOVE */ 811 812/* Process the OP_FILE_MOVE work item WORK_ITEM. 813 * See svn_wc__wq_build_file_move() which generates this work item. 814 * Implements (struct work_item_dispatch).func. */ 815static svn_error_t * 816run_file_move(work_item_baton_t *wqb, 817 svn_wc__db_t *db, 818 const svn_skel_t *work_item, 819 const char *wri_abspath, 820 svn_cancel_func_t cancel_func, 821 void *cancel_baton, 822 apr_pool_t *scratch_pool) 823{ 824 const svn_skel_t *arg1 = work_item->children->next; 825 const char *src_abspath, *dst_abspath; 826 const char *local_relpath; 827 svn_error_t *err; 828 829 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len); 830 SVN_ERR(svn_wc__db_from_relpath(&src_abspath, db, wri_abspath, local_relpath, 831 scratch_pool, scratch_pool)); 832 local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->data, 833 arg1->next->len); 834 SVN_ERR(svn_wc__db_from_relpath(&dst_abspath, db, wri_abspath, local_relpath, 835 scratch_pool, scratch_pool)); 836 837 /* Use svn_io_file_move() instead of svn_io_file_rename() to allow cross 838 device copies. We should not fail in the workqueue. */ 839 840 err = svn_io_file_move(src_abspath, dst_abspath, scratch_pool); 841 842 /* If the source is not found, we assume the wq op is already handled */ 843 if (err && APR_STATUS_IS_ENOENT(err->apr_err)) 844 svn_error_clear(err); 845 else 846 SVN_ERR(err); 847 848 return SVN_NO_ERROR; 849} 850 851 852svn_error_t * 853svn_wc__wq_build_file_move(svn_skel_t **work_item, 854 svn_wc__db_t *db, 855 const char *wri_abspath, 856 const char *src_abspath, 857 const char *dst_abspath, 858 apr_pool_t *result_pool, 859 apr_pool_t *scratch_pool) 860{ 861 svn_node_kind_t kind; 862 const char *local_relpath; 863 *work_item = svn_skel__make_empty_list(result_pool); 864 865 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath)); 866 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath)); 867 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath)); 868 869 /* File must exist */ 870 SVN_ERR(svn_io_check_path(src_abspath, &kind, result_pool)); 871 872 if (kind == svn_node_none) 873 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, 874 _("'%s' not found"), 875 svn_dirent_local_style(src_abspath, 876 scratch_pool)); 877 878 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath, dst_abspath, 879 result_pool, scratch_pool)); 880 svn_skel__prepend_str(local_relpath, *work_item, result_pool); 881 882 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath, src_abspath, 883 result_pool, scratch_pool)); 884 svn_skel__prepend_str(local_relpath, *work_item, result_pool); 885 886 svn_skel__prepend_str(OP_FILE_MOVE, *work_item, result_pool); 887 888 return SVN_NO_ERROR; 889} 890 891/* ------------------------------------------------------------------------ */ 892 893/* OP_FILE_COPY_TRANSLATED */ 894 895/* Process the OP_FILE_COPY_TRANSLATED work item WORK_ITEM. 896 * See run_file_copy_translated() which generates this work item. 897 * Implements (struct work_item_dispatch).func. */ 898static svn_error_t * 899run_file_copy_translated(work_item_baton_t *wqb, 900 svn_wc__db_t *db, 901 const svn_skel_t *work_item, 902 const char *wri_abspath, 903 svn_cancel_func_t cancel_func, 904 void *cancel_baton, 905 apr_pool_t *scratch_pool) 906{ 907 const svn_skel_t *arg1 = work_item->children->next; 908 const char *local_abspath, *src_abspath, *dst_abspath; 909 const char *local_relpath; 910 svn_subst_eol_style_t style; 911 const char *eol; 912 apr_hash_t *keywords; 913 svn_boolean_t special; 914 915 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len); 916 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath, 917 local_relpath, scratch_pool, scratch_pool)); 918 919 local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->data, 920 arg1->next->len); 921 SVN_ERR(svn_wc__db_from_relpath(&src_abspath, db, wri_abspath, 922 local_relpath, scratch_pool, scratch_pool)); 923 924 local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->next->data, 925 arg1->next->next->len); 926 SVN_ERR(svn_wc__db_from_relpath(&dst_abspath, db, wri_abspath, 927 local_relpath, scratch_pool, scratch_pool)); 928 929 SVN_ERR(svn_wc__get_translate_info(&style, &eol, 930 &keywords, 931 &special, 932 db, local_abspath, NULL, FALSE, 933 scratch_pool, scratch_pool)); 934 935 SVN_ERR(svn_subst_copy_and_translate4(src_abspath, dst_abspath, 936 eol, TRUE /* repair */, 937 keywords, TRUE /* expand */, 938 special, 939 cancel_func, cancel_baton, 940 scratch_pool)); 941 return SVN_NO_ERROR; 942} 943 944 945svn_error_t * 946svn_wc__wq_build_file_copy_translated(svn_skel_t **work_item, 947 svn_wc__db_t *db, 948 const char *local_abspath, 949 const char *src_abspath, 950 const char *dst_abspath, 951 apr_pool_t *result_pool, 952 apr_pool_t *scratch_pool) 953{ 954 svn_node_kind_t kind; 955 const char *local_relpath; 956 957 *work_item = svn_skel__make_empty_list(result_pool); 958 959 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 960 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath)); 961 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath)); 962 963 /* File must exist */ 964 SVN_ERR(svn_io_check_path(src_abspath, &kind, result_pool)); 965 966 if (kind == svn_node_none) 967 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, 968 _("'%s' not found"), 969 svn_dirent_local_style(src_abspath, 970 scratch_pool)); 971 972 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, dst_abspath, 973 result_pool, scratch_pool)); 974 svn_skel__prepend_str(local_relpath, *work_item, result_pool); 975 976 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, src_abspath, 977 result_pool, scratch_pool)); 978 svn_skel__prepend_str(local_relpath, *work_item, result_pool); 979 980 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, 981 local_abspath, result_pool, scratch_pool)); 982 svn_skel__prepend_str(local_relpath, *work_item, result_pool); 983 984 svn_skel__prepend_str(OP_FILE_COPY_TRANSLATED, *work_item, result_pool); 985 986 return SVN_NO_ERROR; 987} 988 989/* ------------------------------------------------------------------------ */ 990 991/* OP_DIRECTORY_INSTALL */ 992 993static svn_error_t * 994run_dir_install(work_item_baton_t *wqb, 995 svn_wc__db_t *db, 996 const svn_skel_t *work_item, 997 const char *wri_abspath, 998 svn_cancel_func_t cancel_func, 999 void *cancel_baton, 1000 apr_pool_t *scratch_pool) 1001{ 1002 const svn_skel_t *arg1 = work_item->children->next; 1003 const char *local_relpath; 1004 const char *local_abspath; 1005 1006 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len); 1007 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath, 1008 local_relpath, scratch_pool, scratch_pool)); 1009 1010 SVN_ERR(svn_wc__ensure_directory(local_abspath, scratch_pool)); 1011 1012 return SVN_NO_ERROR; 1013} 1014 1015svn_error_t * 1016svn_wc__wq_build_dir_install(svn_skel_t **work_item, 1017 svn_wc__db_t *db, 1018 const char *local_abspath, 1019 apr_pool_t *result_pool, 1020 apr_pool_t *scratch_pool) 1021{ 1022 const char *local_relpath; 1023 1024 *work_item = svn_skel__make_empty_list(result_pool); 1025 1026 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, 1027 local_abspath, result_pool, scratch_pool)); 1028 svn_skel__prepend_str(local_relpath, *work_item, result_pool); 1029 1030 svn_skel__prepend_str(OP_DIRECTORY_INSTALL, *work_item, result_pool); 1031 1032 return SVN_NO_ERROR; 1033} 1034 1035 1036/* ------------------------------------------------------------------------ */ 1037 1038/* OP_SYNC_FILE_FLAGS */ 1039 1040/* Process the OP_SYNC_FILE_FLAGS work item WORK_ITEM. 1041 * See svn_wc__wq_build_sync_file_flags() which generates this work item. 1042 * Implements (struct work_item_dispatch).func. */ 1043static svn_error_t * 1044run_sync_file_flags(work_item_baton_t *wqb, 1045 svn_wc__db_t *db, 1046 const svn_skel_t *work_item, 1047 const char *wri_abspath, 1048 svn_cancel_func_t cancel_func, 1049 void *cancel_baton, 1050 apr_pool_t *scratch_pool) 1051{ 1052 const svn_skel_t *arg1 = work_item->children->next; 1053 const char *local_relpath; 1054 const char *local_abspath; 1055 1056 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len); 1057 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath, 1058 local_relpath, scratch_pool, scratch_pool)); 1059 1060 return svn_error_trace(svn_wc__sync_flags_with_props(NULL, db, 1061 local_abspath, scratch_pool)); 1062} 1063 1064 1065svn_error_t * 1066svn_wc__wq_build_sync_file_flags(svn_skel_t **work_item, 1067 svn_wc__db_t *db, 1068 const char *local_abspath, 1069 apr_pool_t *result_pool, 1070 apr_pool_t *scratch_pool) 1071{ 1072 const char *local_relpath; 1073 *work_item = svn_skel__make_empty_list(result_pool); 1074 1075 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, 1076 local_abspath, result_pool, scratch_pool)); 1077 1078 svn_skel__prepend_str(local_relpath, *work_item, result_pool); 1079 svn_skel__prepend_str(OP_SYNC_FILE_FLAGS, *work_item, result_pool); 1080 1081 return SVN_NO_ERROR; 1082} 1083 1084 1085/* ------------------------------------------------------------------------ */ 1086 1087/* OP_PREJ_INSTALL */ 1088 1089static svn_error_t * 1090run_prej_install(work_item_baton_t *wqb, 1091 svn_wc__db_t *db, 1092 const svn_skel_t *work_item, 1093 const char *wri_abspath, 1094 svn_cancel_func_t cancel_func, 1095 void *cancel_baton, 1096 apr_pool_t *scratch_pool) 1097{ 1098 const svn_skel_t *arg1 = work_item->children->next; 1099 const char *local_relpath; 1100 const char *local_abspath; 1101 svn_skel_t *conflicts; 1102 const svn_skel_t *prop_conflict_skel; 1103 const char *tmp_prejfile_abspath; 1104 const char *prejfile_abspath; 1105 1106 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len); 1107 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath, 1108 local_relpath, scratch_pool, scratch_pool)); 1109 1110 SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL, db, local_abspath, 1111 scratch_pool, scratch_pool)); 1112 1113 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prejfile_abspath, 1114 NULL, NULL, NULL, NULL, 1115 db, local_abspath, conflicts, 1116 scratch_pool, scratch_pool)); 1117 1118 if (arg1->next != NULL) 1119 prop_conflict_skel = arg1->next; /* Before Subversion 1.9 */ 1120 else 1121 prop_conflict_skel = NULL; /* Read from DB */ 1122 1123 /* Construct a property reject file in the temporary area. */ 1124 SVN_ERR(svn_wc__create_prejfile(&tmp_prejfile_abspath, 1125 db, local_abspath, 1126 prop_conflict_skel, 1127 cancel_func, cancel_baton, 1128 scratch_pool, scratch_pool)); 1129 1130 /* ... and atomically move it into place. */ 1131 SVN_ERR(svn_io_file_rename2(tmp_prejfile_abspath, 1132 prejfile_abspath, FALSE, 1133 scratch_pool)); 1134 1135 return SVN_NO_ERROR; 1136} 1137 1138 1139svn_error_t * 1140svn_wc__wq_build_prej_install(svn_skel_t **work_item, 1141 svn_wc__db_t *db, 1142 const char *local_abspath, 1143 /*svn_skel_t *conflict_skel,*/ 1144 apr_pool_t *result_pool, 1145 apr_pool_t *scratch_pool) 1146{ 1147 const char *local_relpath; 1148 *work_item = svn_skel__make_empty_list(result_pool); 1149 1150 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, 1151 local_abspath, result_pool, scratch_pool)); 1152 1153 /* ### In Subversion 1.7 and 1.8 we created a legacy property conflict skel 1154 here: 1155 if (conflict_skel != NULL) 1156 svn_skel__prepend(conflict_skel, *work_item); 1157 */ 1158 svn_skel__prepend_str(local_relpath, *work_item, result_pool); 1159 svn_skel__prepend_str(OP_PREJ_INSTALL, *work_item, result_pool); 1160 1161 return SVN_NO_ERROR; 1162} 1163 1164 1165/* ------------------------------------------------------------------------ */ 1166 1167/* OP_RECORD_FILEINFO */ 1168 1169 1170static svn_error_t * 1171run_record_fileinfo(work_item_baton_t *wqb, 1172 svn_wc__db_t *db, 1173 const svn_skel_t *work_item, 1174 const char *wri_abspath, 1175 svn_cancel_func_t cancel_func, 1176 void *cancel_baton, 1177 apr_pool_t *scratch_pool) 1178{ 1179 const svn_skel_t *arg1 = work_item->children->next; 1180 const char *local_relpath; 1181 const char *local_abspath; 1182 apr_time_t set_time = 0; 1183 1184 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len); 1185 1186 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath, 1187 local_relpath, scratch_pool, scratch_pool)); 1188 1189 if (arg1->next) 1190 { 1191 apr_int64_t val; 1192 1193 SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool)); 1194 set_time = (apr_time_t)val; 1195 } 1196 1197 if (set_time != 0) 1198 { 1199 svn_node_kind_t kind; 1200 svn_boolean_t is_special; 1201 1202 /* Do not set the timestamp on special files. */ 1203 SVN_ERR(svn_io_check_special_path(local_abspath, &kind, &is_special, 1204 scratch_pool)); 1205 1206 /* Don't set affected time when local_abspath does not exist or is 1207 a special file */ 1208 if (kind == svn_node_file && !is_special) 1209 SVN_ERR(svn_io_set_file_affected_time(set_time, local_abspath, 1210 scratch_pool)); 1211 1212 /* Note that we can't use the value we get here for recording as the 1213 filesystem might have a different timestamp granularity */ 1214 } 1215 1216 1217 return svn_error_trace(get_and_record_fileinfo(wqb, local_abspath, 1218 TRUE /* ignore_enoent */, 1219 scratch_pool)); 1220} 1221 1222/* ------------------------------------------------------------------------ */ 1223 1224/* OP_TMP_SET_TEXT_CONFLICT_MARKERS */ 1225 1226 1227static svn_error_t * 1228run_set_text_conflict_markers(work_item_baton_t *wqb, 1229 svn_wc__db_t *db, 1230 const svn_skel_t *work_item, 1231 const char *wri_abspath, 1232 svn_cancel_func_t cancel_func, 1233 void *cancel_baton, 1234 apr_pool_t *scratch_pool) 1235{ 1236 const svn_skel_t *arg = work_item->children->next; 1237 const char *local_relpath; 1238 const char *local_abspath; 1239 const char *old_abspath = NULL; 1240 const char *new_abspath = NULL; 1241 const char *wrk_abspath = NULL; 1242 1243 local_relpath = apr_pstrmemdup(scratch_pool, arg->data, arg->len); 1244 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath, 1245 local_relpath, scratch_pool, scratch_pool)); 1246 1247 arg = arg->next; 1248 local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len) 1249 : NULL; 1250 1251 if (local_relpath) 1252 { 1253 SVN_ERR(svn_wc__db_from_relpath(&old_abspath, db, wri_abspath, 1254 local_relpath, 1255 scratch_pool, scratch_pool)); 1256 } 1257 1258 arg = arg->next; 1259 local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len) 1260 : NULL; 1261 if (local_relpath) 1262 { 1263 SVN_ERR(svn_wc__db_from_relpath(&new_abspath, db, wri_abspath, 1264 local_relpath, 1265 scratch_pool, scratch_pool)); 1266 } 1267 1268 arg = arg->next; 1269 local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len) 1270 : NULL; 1271 1272 if (local_relpath) 1273 { 1274 SVN_ERR(svn_wc__db_from_relpath(&wrk_abspath, db, wri_abspath, 1275 local_relpath, 1276 scratch_pool, scratch_pool)); 1277 } 1278 1279 /* Upgrade scenario: We have a workqueue item that describes how to install a 1280 non skel conflict. Fetch all the information we can to create a new style 1281 conflict. */ 1282 /* ### Before format 30 this is/was a common code path as we didn't install 1283 ### the conflict directly in the db. It just calls the wc_db code 1284 ### to set the right fields. */ 1285 1286 { 1287 /* Check if we should combine with a property conflict... */ 1288 svn_skel_t *conflicts; 1289 1290 SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL, db, local_abspath, 1291 scratch_pool, scratch_pool)); 1292 1293 if (! conflicts) 1294 { 1295 /* No conflict exists, create a basic skel */ 1296 conflicts = svn_wc__conflict_skel_create(scratch_pool); 1297 1298 SVN_ERR(svn_wc__conflict_skel_set_op_update(conflicts, NULL, NULL, 1299 scratch_pool, 1300 scratch_pool)); 1301 } 1302 1303 /* Add the text conflict to the existing onflict */ 1304 SVN_ERR(svn_wc__conflict_skel_add_text_conflict(conflicts, db, 1305 local_abspath, 1306 wrk_abspath, 1307 old_abspath, 1308 new_abspath, 1309 scratch_pool, 1310 scratch_pool)); 1311 1312 SVN_ERR(svn_wc__db_op_mark_conflict(db, local_abspath, conflicts, 1313 NULL, scratch_pool)); 1314 } 1315 return SVN_NO_ERROR; 1316} 1317 1318/* ------------------------------------------------------------------------ */ 1319 1320/* OP_TMP_SET_PROPERTY_CONFLICT_MARKER */ 1321 1322static svn_error_t * 1323run_set_property_conflict_marker(work_item_baton_t *wqb, 1324 svn_wc__db_t *db, 1325 const svn_skel_t *work_item, 1326 const char *wri_abspath, 1327 svn_cancel_func_t cancel_func, 1328 void *cancel_baton, 1329 apr_pool_t *scratch_pool) 1330{ 1331 const svn_skel_t *arg = work_item->children->next; 1332 const char *local_relpath; 1333 const char *local_abspath; 1334 const char *prej_abspath = NULL; 1335 1336 local_relpath = apr_pstrmemdup(scratch_pool, arg->data, arg->len); 1337 1338 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath, 1339 local_relpath, scratch_pool, scratch_pool)); 1340 1341 1342 arg = arg->next; 1343 local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len) 1344 : NULL; 1345 1346 if (local_relpath) 1347 SVN_ERR(svn_wc__db_from_relpath(&prej_abspath, db, wri_abspath, 1348 local_relpath, 1349 scratch_pool, scratch_pool)); 1350 1351 { 1352 /* Check if we should combine with a text conflict... */ 1353 svn_skel_t *conflicts; 1354 apr_hash_t *prop_names; 1355 1356 SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL, 1357 db, local_abspath, 1358 scratch_pool, scratch_pool)); 1359 1360 if (! conflicts) 1361 { 1362 /* No conflict exists, create a basic skel */ 1363 conflicts = svn_wc__conflict_skel_create(scratch_pool); 1364 1365 SVN_ERR(svn_wc__conflict_skel_set_op_update(conflicts, NULL, NULL, 1366 scratch_pool, 1367 scratch_pool)); 1368 } 1369 1370 prop_names = apr_hash_make(scratch_pool); 1371 SVN_ERR(svn_wc__conflict_skel_add_prop_conflict(conflicts, db, 1372 local_abspath, 1373 prej_abspath, 1374 NULL, NULL, NULL, 1375 prop_names, 1376 scratch_pool, 1377 scratch_pool)); 1378 1379 SVN_ERR(svn_wc__db_op_mark_conflict(db, local_abspath, conflicts, 1380 NULL, scratch_pool)); 1381 } 1382 return SVN_NO_ERROR; 1383} 1384 1385/* ------------------------------------------------------------------------ */ 1386 1387static const struct work_item_dispatch dispatch_table[] = { 1388 { OP_FILE_COMMIT, run_file_commit }, 1389 { OP_FILE_INSTALL, run_file_install }, 1390 { OP_FILE_REMOVE, run_file_remove }, 1391 { OP_FILE_MOVE, run_file_move }, 1392 { OP_FILE_COPY_TRANSLATED, run_file_copy_translated }, 1393 { OP_SYNC_FILE_FLAGS, run_sync_file_flags }, 1394 { OP_PREJ_INSTALL, run_prej_install }, 1395 { OP_DIRECTORY_REMOVE, run_dir_remove }, 1396 { OP_DIRECTORY_INSTALL, run_dir_install }, 1397 1398 /* Upgrade steps */ 1399 { OP_POSTUPGRADE, run_postupgrade }, 1400 1401 /* Legacy workqueue items. No longer created */ 1402 { OP_BASE_REMOVE, run_base_remove }, 1403 { OP_RECORD_FILEINFO, run_record_fileinfo }, 1404 { OP_TMP_SET_TEXT_CONFLICT_MARKERS, run_set_text_conflict_markers }, 1405 { OP_TMP_SET_PROPERTY_CONFLICT_MARKER, run_set_property_conflict_marker }, 1406 1407 /* Sentinel. */ 1408 { NULL } 1409}; 1410 1411struct work_item_baton_t 1412{ 1413 apr_pool_t *result_pool; /* Pool to allocate result in */ 1414 1415 svn_boolean_t used; /* needs reset */ 1416 1417 apr_hash_t *record_map; /* const char * -> svn_io_dirent2_t map */ 1418}; 1419 1420 1421static svn_error_t * 1422dispatch_work_item(work_item_baton_t *wqb, 1423 svn_wc__db_t *db, 1424 const char *wri_abspath, 1425 const svn_skel_t *work_item, 1426 svn_cancel_func_t cancel_func, 1427 void *cancel_baton, 1428 apr_pool_t *scratch_pool) 1429{ 1430 const struct work_item_dispatch *scan; 1431 1432 /* Scan the dispatch table for a function to handle this work item. */ 1433 for (scan = &dispatch_table[0]; scan->name != NULL; ++scan) 1434 { 1435 if (svn_skel__matches_atom(work_item->children, scan->name)) 1436 { 1437 1438#ifdef SVN_DEBUG_WORK_QUEUE 1439 SVN_DBG(("dispatch: operation='%s'\n", scan->name)); 1440#endif 1441 SVN_ERR((*scan->func)(wqb, db, work_item, wri_abspath, 1442 cancel_func, cancel_baton, 1443 scratch_pool)); 1444 1445#ifdef SVN_RUN_WORK_QUEUE_TWICE 1446#ifdef SVN_DEBUG_WORK_QUEUE 1447 SVN_DBG(("dispatch: operation='%s'\n", scan->name)); 1448#endif 1449 /* Being able to run every workqueue item twice is one 1450 requirement for workqueues to be restartable. */ 1451 SVN_ERR((*scan->func)(db, work_item, wri_abspath, 1452 cancel_func, cancel_baton, 1453 scratch_pool)); 1454#endif 1455 1456 break; 1457 } 1458 } 1459 1460 if (scan->name == NULL) 1461 { 1462 /* We should know about ALL possible work items here. If we do not, 1463 then something is wrong. Most likely, some kind of format/code 1464 skew. There is nothing more we can do. Erasing or ignoring this 1465 work item could leave the WC in an even more broken state. 1466 1467 Contrary to issue #1581, we cannot simply remove work items and 1468 continue, so bail out with an error. */ 1469 return svn_error_createf(SVN_ERR_WC_BAD_ADM_LOG, NULL, 1470 _("Unrecognized work item in the queue")); 1471 } 1472 1473 return SVN_NO_ERROR; 1474} 1475 1476 1477svn_error_t * 1478svn_wc__wq_run(svn_wc__db_t *db, 1479 const char *wri_abspath, 1480 svn_cancel_func_t cancel_func, 1481 void *cancel_baton, 1482 apr_pool_t *scratch_pool) 1483{ 1484 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1485 apr_uint64_t last_id = 0; 1486 work_item_baton_t wib = { 0 }; 1487 wib.result_pool = svn_pool_create(scratch_pool); 1488 1489#ifdef SVN_DEBUG_WORK_QUEUE 1490 SVN_DBG(("wq_run: wri='%s'\n", wri_abspath)); 1491 { 1492 static int count = 0; 1493 const char *count_env_var = getenv("SVN_DEBUG_WORK_QUEUE"); 1494 int count_env_val; 1495 1496 SVN_ERR(svn_cstring_atoi(&count_env_val, count_env_var)); 1497 1498 if (count_env_var && ++count == count_env_val) 1499 return svn_error_create(SVN_ERR_CANCELLED, NULL, "fake cancel"); 1500 } 1501#endif 1502 1503 while (TRUE) 1504 { 1505 apr_uint64_t id; 1506 svn_skel_t *work_item; 1507 svn_error_t *err; 1508 1509 svn_pool_clear(iterpool); 1510 1511 if (! wib.used) 1512 { 1513 /* Make sure to do this *early* in the loop iteration. There may 1514 be a LAST_ID that needs to be marked as completed, *before* we 1515 start worrying about anything else. */ 1516 SVN_ERR(svn_wc__db_wq_fetch_next(&id, &work_item, db, wri_abspath, 1517 last_id, iterpool, iterpool)); 1518 } 1519 else 1520 { 1521 /* Make sure to do this *early* in the loop iteration. There may 1522 be a LAST_ID that needs to be marked as completed, *before* we 1523 start worrying about anything else. */ 1524 SVN_ERR(svn_wc__db_wq_record_and_fetch_next(&id, &work_item, 1525 db, wri_abspath, 1526 last_id, wib.record_map, 1527 iterpool, 1528 wib.result_pool)); 1529 1530 svn_pool_clear(wib.result_pool); 1531 wib.record_map = NULL; 1532 wib.used = FALSE; 1533 } 1534 1535 /* Stop work queue processing, if requested. A future 'svn cleanup' 1536 should be able to continue the processing. Note that we may 1537 have WORK_ITEM, but we'll just skip its processing for now. */ 1538 if (cancel_func) 1539 SVN_ERR(cancel_func(cancel_baton)); 1540 1541 /* If we have a WORK_ITEM, then process the sucker. Otherwise, 1542 we're done. */ 1543 if (work_item == NULL) 1544 break; 1545 1546 err = dispatch_work_item(&wib, db, wri_abspath, work_item, 1547 cancel_func, cancel_baton, iterpool); 1548 if (err) 1549 { 1550 const char *skel = svn_skel__unparse(work_item, scratch_pool)->data; 1551 1552 return svn_error_createf(SVN_ERR_WC_BAD_ADM_LOG, err, 1553 _("Failed to run the WC DB work queue " 1554 "associated with '%s', work item %d %s"), 1555 svn_dirent_local_style(wri_abspath, 1556 scratch_pool), 1557 (int)id, skel); 1558 } 1559 1560 /* The work item finished without error. Mark it completed 1561 in the next loop. */ 1562 last_id = id; 1563 } 1564 1565 svn_pool_destroy(iterpool); 1566 return SVN_NO_ERROR; 1567} 1568 1569 1570svn_skel_t * 1571svn_wc__wq_merge(svn_skel_t *work_item1, 1572 svn_skel_t *work_item2, 1573 apr_pool_t *result_pool) 1574{ 1575 /* If either argument is NULL, then just return the other. */ 1576 if (work_item1 == NULL) 1577 return work_item2; 1578 if (work_item2 == NULL) 1579 return work_item1; 1580 1581 /* We have two items. Figure out how to join them. */ 1582 if (SVN_WC__SINGLE_WORK_ITEM(work_item1)) 1583 { 1584 if (SVN_WC__SINGLE_WORK_ITEM(work_item2)) 1585 { 1586 /* Both are singular work items. Construct a list, then put 1587 both work items into it (in the proper order). */ 1588 1589 svn_skel_t *result = svn_skel__make_empty_list(result_pool); 1590 1591 svn_skel__prepend(work_item2, result); 1592 svn_skel__prepend(work_item1, result); 1593 return result; 1594 } 1595 1596 /* WORK_ITEM2 is a list of work items. We can simply shove WORK_ITEM1 1597 in the front to keep the ordering. */ 1598 svn_skel__prepend(work_item1, work_item2); 1599 return work_item2; 1600 } 1601 /* WORK_ITEM1 is a list of work items. */ 1602 1603 if (SVN_WC__SINGLE_WORK_ITEM(work_item2)) 1604 { 1605 /* Put WORK_ITEM2 onto the end of the WORK_ITEM1 list. */ 1606 svn_skel__append(work_item1, work_item2); 1607 return work_item1; 1608 } 1609 1610 /* We have two lists of work items. We need to chain all of the work 1611 items into one big list. We will leave behind the WORK_ITEM2 skel, 1612 as we only want its children. */ 1613 svn_skel__append(work_item1, work_item2->children); 1614 return work_item1; 1615} 1616 1617 1618static svn_error_t * 1619get_and_record_fileinfo(work_item_baton_t *wqb, 1620 const char *local_abspath, 1621 svn_boolean_t ignore_enoent, 1622 apr_pool_t *scratch_pool) 1623{ 1624 const svn_io_dirent2_t *dirent; 1625 1626 SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, ignore_enoent, 1627 wqb->result_pool, scratch_pool)); 1628 1629 if (dirent->kind != svn_node_file) 1630 return SVN_NO_ERROR; 1631 1632 wqb->used = TRUE; 1633 1634 if (! wqb->record_map) 1635 wqb->record_map = apr_hash_make(wqb->result_pool); 1636 1637 svn_hash_sets(wqb->record_map, apr_pstrdup(wqb->result_pool, local_abspath), 1638 dirent); 1639 1640 return SVN_NO_ERROR; 1641} 1642