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