1/* 2 * editor.c : editing trees of versioned resources 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_types.h" 27#include "svn_error.h" 28#include "svn_pools.h" 29#include "svn_dirent_uri.h" 30 31#include "private/svn_editor.h" 32 33#ifdef SVN_DEBUG 34/* This enables runtime checks of the editor API constraints. This may 35 introduce additional memory and runtime overhead, and should not be used 36 in production builds. 37 38 ### Remove before release? 39 40 ### Disabled for now. If I call svn_editor_alter_directory(A) then 41 svn_editor_add_file(A/f) the latter fails on SHOULD_ALLOW_ADD. 42 If I modify svn_editor_alter_directory to MARK_ALLOW_ADD(child) 43 then if I call svn_editor_alter_directory(A) followed by 44 svn_editor_alter_directory(A/B/C) the latter fails on 45 VERIFY_PARENT_MAY_EXIST. */ 46#if 0 47#define ENABLE_ORDERING_CHECK 48#endif 49#endif 50 51 52struct svn_editor_t 53{ 54 void *baton; 55 56 /* Standard cancellation function. Called before each callback. */ 57 svn_cancel_func_t cancel_func; 58 void *cancel_baton; 59 60 /* Our callback functions match that of the set-many structure, so 61 just use that. */ 62 svn_editor_cb_many_t funcs; 63 64 /* This pool is used as the scratch_pool for all callbacks. */ 65 apr_pool_t *scratch_pool; 66 67#ifdef ENABLE_ORDERING_CHECK 68 svn_boolean_t within_callback; 69 70 apr_hash_t *pending_incomplete_children; 71 apr_hash_t *completed_nodes; 72 svn_boolean_t finished; 73 74 apr_pool_t *state_pool; 75#endif 76}; 77 78 79#ifdef ENABLE_ORDERING_CHECK 80 81#define START_CALLBACK(editor) \ 82 do { \ 83 svn_editor_t *editor__tmp_e = (editor); \ 84 SVN_ERR_ASSERT(!editor__tmp_e->within_callback); \ 85 editor__tmp_e->within_callback = TRUE; \ 86 } while (0) 87#define END_CALLBACK(editor) ((editor)->within_callback = FALSE) 88 89/* Marker to indicate no further changes are allowed on this node. */ 90static const int marker_done = 0; 91#define MARKER_DONE (&marker_done) 92 93/* Marker indicating that add_* may be called for this path, or that it 94 can be the destination of a copy or move. For copy/move, the path 95 will switch to MARKER_ALLOW_ALTER, to enable further tweaks. */ 96static const int marker_allow_add = 0; 97#define MARKER_ALLOW_ADD (&marker_allow_add) 98 99/* Marker indicating that alter_* may be called for this path. */ 100static const int marker_allow_alter = 0; 101#define MARKER_ALLOW_ALTER (&marker_allow_alter) 102 103/* Just like MARKER_DONE, but also indicates that the node was created 104 via add_directory(). This allows us to verify that the CHILDREN param 105 was comprehensive. */ 106static const int marker_added_dir = 0; 107#define MARKER_ADDED_DIR (&marker_added_dir) 108 109#define MARK_FINISHED(editor) ((editor)->finished = TRUE) 110#define SHOULD_NOT_BE_FINISHED(editor) SVN_ERR_ASSERT(!(editor)->finished) 111 112#define CLEAR_INCOMPLETE(editor, relpath) \ 113 svn_hash_sets((editor)->pending_incomplete_children, relpath, NULL); 114 115#define MARK_RELPATH(editor, relpath, value) \ 116 svn_hash_sets((editor)->completed_nodes, \ 117 apr_pstrdup((editor)->state_pool, relpath), value) 118 119#define MARK_COMPLETED(editor, relpath) \ 120 MARK_RELPATH(editor, relpath, MARKER_DONE) 121#define SHOULD_NOT_BE_COMPLETED(editor, relpath) \ 122 SVN_ERR_ASSERT(svn_hash_gets((editor)->completed_nodes, relpath) == NULL) 123 124#define MARK_ALLOW_ADD(editor, relpath) \ 125 MARK_RELPATH(editor, relpath, MARKER_ALLOW_ADD) 126#define SHOULD_ALLOW_ADD(editor, relpath) \ 127 SVN_ERR_ASSERT(allow_either(editor, relpath, MARKER_ALLOW_ADD, NULL)) 128 129#define MARK_ALLOW_ALTER(editor, relpath) \ 130 MARK_RELPATH(editor, relpath, MARKER_ALLOW_ALTER) 131#define SHOULD_ALLOW_ALTER(editor, relpath) \ 132 SVN_ERR_ASSERT(allow_either(editor, relpath, MARKER_ALLOW_ALTER, NULL)) 133 134#define MARK_ADDED_DIR(editor, relpath) \ 135 MARK_RELPATH(editor, relpath, MARKER_ADDED_DIR) 136#define CHECK_UNKNOWN_CHILD(editor, relpath) \ 137 SVN_ERR_ASSERT(check_unknown_child(editor, relpath)) 138 139/* When a child is changed in some way, mark the parent directory as needing 140 to be "stable" (no future structural changes). IOW, only allow "alter" on 141 the parent. Prevents parent-add/delete/move after any child operation. */ 142#define MARK_PARENT_STABLE(editor, relpath) \ 143 mark_parent_stable(editor, relpath) 144 145/* If the parent is MARKER_ALLOW_ADD, then it has been moved-away, and we 146 know it does not exist. All other cases: it might exist. */ 147#define VERIFY_PARENT_MAY_EXIST(editor, relpath) \ 148 SVN_ERR_ASSERT(svn_hash_gets((editor)->completed_nodes, \ 149 svn_relpath_dirname(relpath, \ 150 (editor)->scratch_pool)) \ 151 != MARKER_ALLOW_ADD) 152 153/* If the parent is MARKER_ADDED_DIR, then we should not be deleting 154 children(*). If the parent is MARKER_ALLOW_ADD, then it has been 155 moved-away, so children cannot exist. That leaves MARKER_DONE, 156 MARKER_ALLOW_ALTER, and NULL as possible values. Just assert that 157 we didn't get either of the bad ones. 158 159 (*) if the child as added via add_*(), then it would have been marked 160 as completed and delete/move-away already test against completed nodes. 161 This test is to beware of trying to delete "children" that are not 162 actually (and can't possibly be) present. */ 163#define CHILD_DELETIONS_ALLOWED(editor, relpath) \ 164 SVN_ERR_ASSERT(!allow_either(editor, \ 165 svn_relpath_dirname(relpath, \ 166 (editor)->scratch_pool), \ 167 MARKER_ADDED_DIR, MARKER_ALLOW_ADD)) 168 169static svn_boolean_t 170allow_either(const svn_editor_t *editor, 171 const char *relpath, 172 const void *marker1, 173 const void *marker2) 174{ 175 void *value = svn_hash_gets(editor->completed_nodes, relpath); 176 return value == marker1 || value == marker2; 177} 178 179static svn_boolean_t 180check_unknown_child(const svn_editor_t *editor, 181 const char *relpath) 182{ 183 const char *parent; 184 185 /* If we already know about the new child, then exit early. */ 186 if (svn_hash_gets(editor->pending_incomplete_children, relpath) != NULL) 187 return TRUE; 188 189 parent = svn_relpath_dirname(relpath, editor->scratch_pool); 190 191 /* Was this parent created via svn_editor_add_directory() ? */ 192 if (svn_hash_gets(editor->completed_nodes, parent) 193 == MARKER_ADDED_DIR) 194 { 195 /* Whoops. This child should have been listed in that add call, 196 and placed into ->pending_incomplete_children. */ 197 return FALSE; 198 } 199 200 /* The parent was not added in this drive. */ 201 return TRUE; 202} 203 204static void 205mark_parent_stable(const svn_editor_t *editor, 206 const char *relpath) 207{ 208 const char *parent = svn_relpath_dirname(relpath, editor->scratch_pool); 209 const void *marker = svn_hash_gets(editor->completed_nodes, parent); 210 211 /* If RELPATH has already been marked (to disallow adds, or that it 212 has been fully-completed), then do nothing. */ 213 if (marker == MARKER_ALLOW_ALTER 214 || marker == MARKER_DONE 215 || marker == MARKER_ADDED_DIR) 216 return; 217 218 /* If the marker is MARKER_ALLOW_ADD, then that means the parent was 219 moved away. There is no way to work on a child. That should have 220 been tested before we got here by VERIFY_PARENT_MAY_EXIST(). */ 221 SVN_ERR_ASSERT_NO_RETURN(marker != MARKER_ALLOW_ADD); 222 223 /* MARKER is NULL. Upgrade it to MARKER_ALLOW_ALTER. */ 224 MARK_RELPATH(editor, parent, MARKER_ALLOW_ALTER); 225} 226 227#else 228 229/* Be wary with the definition of these macros so that we don't 230 end up with "statement with no effect" warnings. Obviously, this 231 depends upon particular usage, which is easy to verify. */ 232 233#define START_CALLBACK(editor) /* empty */ 234#define END_CALLBACK(editor) /* empty */ 235 236#define MARK_FINISHED(editor) /* empty */ 237#define SHOULD_NOT_BE_FINISHED(editor) /* empty */ 238 239#define CLEAR_INCOMPLETE(editor, relpath) /* empty */ 240 241#define MARK_COMPLETED(editor, relpath) /* empty */ 242#define SHOULD_NOT_BE_COMPLETED(editor, relpath) /* empty */ 243 244#define MARK_ALLOW_ADD(editor, relpath) /* empty */ 245#define SHOULD_ALLOW_ADD(editor, relpath) /* empty */ 246 247#define MARK_ALLOW_ALTER(editor, relpath) /* empty */ 248#define SHOULD_ALLOW_ALTER(editor, relpath) /* empty */ 249 250#define MARK_ADDED_DIR(editor, relpath) /* empty */ 251#define CHECK_UNKNOWN_CHILD(editor, relpath) /* empty */ 252 253#define MARK_PARENT_STABLE(editor, relpath) /* empty */ 254#define VERIFY_PARENT_MAY_EXIST(editor, relpath) /* empty */ 255#define CHILD_DELETIONS_ALLOWED(editor, relpath) /* empty */ 256 257#endif /* ENABLE_ORDERING_CHECK */ 258 259 260svn_error_t * 261svn_editor_create(svn_editor_t **editor, 262 void *editor_baton, 263 svn_cancel_func_t cancel_func, 264 void *cancel_baton, 265 apr_pool_t *result_pool, 266 apr_pool_t *scratch_pool) 267{ 268 *editor = apr_pcalloc(result_pool, sizeof(**editor)); 269 270 (*editor)->baton = editor_baton; 271 (*editor)->cancel_func = cancel_func; 272 (*editor)->cancel_baton = cancel_baton; 273 (*editor)->scratch_pool = svn_pool_create(result_pool); 274 275#ifdef ENABLE_ORDERING_CHECK 276 (*editor)->pending_incomplete_children = apr_hash_make(result_pool); 277 (*editor)->completed_nodes = apr_hash_make(result_pool); 278 (*editor)->finished = FALSE; 279 (*editor)->state_pool = result_pool; 280#endif 281 282 return SVN_NO_ERROR; 283} 284 285 286void * 287svn_editor_get_baton(const svn_editor_t *editor) 288{ 289 return editor->baton; 290} 291 292 293svn_error_t * 294svn_editor_setcb_add_directory(svn_editor_t *editor, 295 svn_editor_cb_add_directory_t callback, 296 apr_pool_t *scratch_pool) 297{ 298 editor->funcs.cb_add_directory = callback; 299 return SVN_NO_ERROR; 300} 301 302 303svn_error_t * 304svn_editor_setcb_add_file(svn_editor_t *editor, 305 svn_editor_cb_add_file_t callback, 306 apr_pool_t *scratch_pool) 307{ 308 editor->funcs.cb_add_file = callback; 309 return SVN_NO_ERROR; 310} 311 312 313svn_error_t * 314svn_editor_setcb_add_symlink(svn_editor_t *editor, 315 svn_editor_cb_add_symlink_t callback, 316 apr_pool_t *scratch_pool) 317{ 318 editor->funcs.cb_add_symlink = callback; 319 return SVN_NO_ERROR; 320} 321 322 323svn_error_t * 324svn_editor_setcb_add_absent(svn_editor_t *editor, 325 svn_editor_cb_add_absent_t callback, 326 apr_pool_t *scratch_pool) 327{ 328 editor->funcs.cb_add_absent = callback; 329 return SVN_NO_ERROR; 330} 331 332 333svn_error_t * 334svn_editor_setcb_alter_directory(svn_editor_t *editor, 335 svn_editor_cb_alter_directory_t callback, 336 apr_pool_t *scratch_pool) 337{ 338 editor->funcs.cb_alter_directory = callback; 339 return SVN_NO_ERROR; 340} 341 342 343svn_error_t * 344svn_editor_setcb_alter_file(svn_editor_t *editor, 345 svn_editor_cb_alter_file_t callback, 346 apr_pool_t *scratch_pool) 347{ 348 editor->funcs.cb_alter_file = callback; 349 return SVN_NO_ERROR; 350} 351 352 353svn_error_t * 354svn_editor_setcb_alter_symlink(svn_editor_t *editor, 355 svn_editor_cb_alter_symlink_t callback, 356 apr_pool_t *scratch_pool) 357{ 358 editor->funcs.cb_alter_symlink = callback; 359 return SVN_NO_ERROR; 360} 361 362 363svn_error_t * 364svn_editor_setcb_delete(svn_editor_t *editor, 365 svn_editor_cb_delete_t callback, 366 apr_pool_t *scratch_pool) 367{ 368 editor->funcs.cb_delete = callback; 369 return SVN_NO_ERROR; 370} 371 372 373svn_error_t * 374svn_editor_setcb_copy(svn_editor_t *editor, 375 svn_editor_cb_copy_t callback, 376 apr_pool_t *scratch_pool) 377{ 378 editor->funcs.cb_copy = callback; 379 return SVN_NO_ERROR; 380} 381 382 383svn_error_t * 384svn_editor_setcb_move(svn_editor_t *editor, 385 svn_editor_cb_move_t callback, 386 apr_pool_t *scratch_pool) 387{ 388 editor->funcs.cb_move = callback; 389 return SVN_NO_ERROR; 390} 391 392 393svn_error_t * 394svn_editor_setcb_complete(svn_editor_t *editor, 395 svn_editor_cb_complete_t callback, 396 apr_pool_t *scratch_pool) 397{ 398 editor->funcs.cb_complete = callback; 399 return SVN_NO_ERROR; 400} 401 402 403svn_error_t * 404svn_editor_setcb_abort(svn_editor_t *editor, 405 svn_editor_cb_abort_t callback, 406 apr_pool_t *scratch_pool) 407{ 408 editor->funcs.cb_abort = callback; 409 return SVN_NO_ERROR; 410} 411 412 413svn_error_t * 414svn_editor_setcb_many(svn_editor_t *editor, 415 const svn_editor_cb_many_t *many, 416 apr_pool_t *scratch_pool) 417{ 418#define COPY_CALLBACK(NAME) if (many->NAME) editor->funcs.NAME = many->NAME 419 420 COPY_CALLBACK(cb_add_directory); 421 COPY_CALLBACK(cb_add_file); 422 COPY_CALLBACK(cb_add_symlink); 423 COPY_CALLBACK(cb_add_absent); 424 COPY_CALLBACK(cb_alter_directory); 425 COPY_CALLBACK(cb_alter_file); 426 COPY_CALLBACK(cb_alter_symlink); 427 COPY_CALLBACK(cb_delete); 428 COPY_CALLBACK(cb_copy); 429 COPY_CALLBACK(cb_move); 430 COPY_CALLBACK(cb_complete); 431 COPY_CALLBACK(cb_abort); 432 433#undef COPY_CALLBACK 434 435 return SVN_NO_ERROR; 436} 437 438 439static svn_error_t * 440check_cancel(svn_editor_t *editor) 441{ 442 svn_error_t *err = NULL; 443 444 if (editor->cancel_func) 445 { 446 START_CALLBACK(editor); 447 err = editor->cancel_func(editor->cancel_baton); 448 END_CALLBACK(editor); 449 } 450 451 return svn_error_trace(err); 452} 453 454 455svn_error_t * 456svn_editor_add_directory(svn_editor_t *editor, 457 const char *relpath, 458 const apr_array_header_t *children, 459 apr_hash_t *props, 460 svn_revnum_t replaces_rev) 461{ 462 svn_error_t *err = SVN_NO_ERROR; 463 464 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath)); 465 SVN_ERR_ASSERT(children != NULL); 466 SVN_ERR_ASSERT(props != NULL); 467 /* ### validate children are just basenames? */ 468 SHOULD_NOT_BE_FINISHED(editor); 469 SHOULD_ALLOW_ADD(editor, relpath); 470 VERIFY_PARENT_MAY_EXIST(editor, relpath); 471 CHECK_UNKNOWN_CHILD(editor, relpath); 472 473 SVN_ERR(check_cancel(editor)); 474 475 if (editor->funcs.cb_add_directory) 476 { 477 START_CALLBACK(editor); 478 err = editor->funcs.cb_add_directory(editor->baton, relpath, children, 479 props, replaces_rev, 480 editor->scratch_pool); 481 END_CALLBACK(editor); 482 } 483 484 MARK_ADDED_DIR(editor, relpath); 485 MARK_PARENT_STABLE(editor, relpath); 486 CLEAR_INCOMPLETE(editor, relpath); 487 488#ifdef ENABLE_ORDERING_CHECK 489 { 490 int i; 491 for (i = 0; i < children->nelts; i++) 492 { 493 const char *child_basename = APR_ARRAY_IDX(children, i, const char *); 494 const char *child = svn_relpath_join(relpath, child_basename, 495 editor->state_pool); 496 497 svn_hash_sets(editor->pending_incomplete_children, child, ""); 498 } 499 } 500#endif 501 502 svn_pool_clear(editor->scratch_pool); 503 return svn_error_trace(err); 504} 505 506 507svn_error_t * 508svn_editor_add_file(svn_editor_t *editor, 509 const char *relpath, 510 const svn_checksum_t *checksum, 511 svn_stream_t *contents, 512 apr_hash_t *props, 513 svn_revnum_t replaces_rev) 514{ 515 svn_error_t *err = SVN_NO_ERROR; 516 517 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath)); 518 SVN_ERR_ASSERT(checksum != NULL 519 && checksum->kind == SVN_EDITOR_CHECKSUM_KIND); 520 SVN_ERR_ASSERT(contents != NULL); 521 SVN_ERR_ASSERT(props != NULL); 522 SHOULD_NOT_BE_FINISHED(editor); 523 SHOULD_ALLOW_ADD(editor, relpath); 524 VERIFY_PARENT_MAY_EXIST(editor, relpath); 525 CHECK_UNKNOWN_CHILD(editor, relpath); 526 527 SVN_ERR(check_cancel(editor)); 528 529 if (editor->funcs.cb_add_file) 530 { 531 START_CALLBACK(editor); 532 err = editor->funcs.cb_add_file(editor->baton, relpath, 533 checksum, contents, props, 534 replaces_rev, editor->scratch_pool); 535 END_CALLBACK(editor); 536 } 537 538 MARK_COMPLETED(editor, relpath); 539 MARK_PARENT_STABLE(editor, relpath); 540 CLEAR_INCOMPLETE(editor, relpath); 541 542 svn_pool_clear(editor->scratch_pool); 543 return svn_error_trace(err); 544} 545 546 547svn_error_t * 548svn_editor_add_symlink(svn_editor_t *editor, 549 const char *relpath, 550 const char *target, 551 apr_hash_t *props, 552 svn_revnum_t replaces_rev) 553{ 554 svn_error_t *err = SVN_NO_ERROR; 555 556 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath)); 557 SVN_ERR_ASSERT(props != NULL); 558 SHOULD_NOT_BE_FINISHED(editor); 559 SHOULD_ALLOW_ADD(editor, relpath); 560 VERIFY_PARENT_MAY_EXIST(editor, relpath); 561 CHECK_UNKNOWN_CHILD(editor, relpath); 562 563 SVN_ERR(check_cancel(editor)); 564 565 if (editor->funcs.cb_add_symlink) 566 { 567 START_CALLBACK(editor); 568 err = editor->funcs.cb_add_symlink(editor->baton, relpath, target, props, 569 replaces_rev, editor->scratch_pool); 570 END_CALLBACK(editor); 571 } 572 573 MARK_COMPLETED(editor, relpath); 574 MARK_PARENT_STABLE(editor, relpath); 575 CLEAR_INCOMPLETE(editor, relpath); 576 577 svn_pool_clear(editor->scratch_pool); 578 return svn_error_trace(err); 579} 580 581 582svn_error_t * 583svn_editor_add_absent(svn_editor_t *editor, 584 const char *relpath, 585 svn_node_kind_t kind, 586 svn_revnum_t replaces_rev) 587{ 588 svn_error_t *err = SVN_NO_ERROR; 589 590 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath)); 591 SHOULD_NOT_BE_FINISHED(editor); 592 SHOULD_ALLOW_ADD(editor, relpath); 593 VERIFY_PARENT_MAY_EXIST(editor, relpath); 594 CHECK_UNKNOWN_CHILD(editor, relpath); 595 596 SVN_ERR(check_cancel(editor)); 597 598 if (editor->funcs.cb_add_absent) 599 { 600 START_CALLBACK(editor); 601 err = editor->funcs.cb_add_absent(editor->baton, relpath, kind, 602 replaces_rev, editor->scratch_pool); 603 END_CALLBACK(editor); 604 } 605 606 MARK_COMPLETED(editor, relpath); 607 MARK_PARENT_STABLE(editor, relpath); 608 CLEAR_INCOMPLETE(editor, relpath); 609 610 svn_pool_clear(editor->scratch_pool); 611 return svn_error_trace(err); 612} 613 614 615svn_error_t * 616svn_editor_alter_directory(svn_editor_t *editor, 617 const char *relpath, 618 svn_revnum_t revision, 619 const apr_array_header_t *children, 620 apr_hash_t *props) 621{ 622 svn_error_t *err = SVN_NO_ERROR; 623 624 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath)); 625 SVN_ERR_ASSERT(children != NULL || props != NULL); 626 /* ### validate children are just basenames? */ 627 SHOULD_NOT_BE_FINISHED(editor); 628 SHOULD_ALLOW_ALTER(editor, relpath); 629 VERIFY_PARENT_MAY_EXIST(editor, relpath); 630 631 SVN_ERR(check_cancel(editor)); 632 633 if (editor->funcs.cb_alter_directory) 634 { 635 START_CALLBACK(editor); 636 err = editor->funcs.cb_alter_directory(editor->baton, 637 relpath, revision, 638 children, props, 639 editor->scratch_pool); 640 END_CALLBACK(editor); 641 } 642 643 MARK_COMPLETED(editor, relpath); 644 MARK_PARENT_STABLE(editor, relpath); 645 646#ifdef ENABLE_ORDERING_CHECK 647 /* ### this is not entirely correct. we probably need to adjust the 648 ### check_unknown_child() function for this scenario. */ 649#if 0 650 { 651 int i; 652 for (i = 0; i < children->nelts; i++) 653 { 654 const char *child_basename = APR_ARRAY_IDX(children, i, const char *); 655 const char *child = svn_relpath_join(relpath, child_basename, 656 editor->state_pool); 657 658 apr_hash_set(editor->pending_incomplete_children, child, 659 APR_HASH_KEY_STRING, ""); 660 /* Perhaps MARK_ALLOW_ADD(editor, child); ? */ 661 } 662 } 663#endif 664#endif 665 666 svn_pool_clear(editor->scratch_pool); 667 return svn_error_trace(err); 668} 669 670 671svn_error_t * 672svn_editor_alter_file(svn_editor_t *editor, 673 const char *relpath, 674 svn_revnum_t revision, 675 const svn_checksum_t *checksum, 676 svn_stream_t *contents, 677 apr_hash_t *props) 678{ 679 svn_error_t *err = SVN_NO_ERROR; 680 681 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath)); 682 SVN_ERR_ASSERT((checksum != NULL && contents != NULL) 683 || (checksum == NULL && contents == NULL)); 684 SVN_ERR_ASSERT(props != NULL || checksum != NULL); 685 if (checksum) 686 SVN_ERR_ASSERT(checksum->kind == SVN_EDITOR_CHECKSUM_KIND); 687 SHOULD_NOT_BE_FINISHED(editor); 688 SHOULD_ALLOW_ALTER(editor, relpath); 689 VERIFY_PARENT_MAY_EXIST(editor, relpath); 690 691 SVN_ERR(check_cancel(editor)); 692 693 if (editor->funcs.cb_alter_file) 694 { 695 START_CALLBACK(editor); 696 err = editor->funcs.cb_alter_file(editor->baton, 697 relpath, revision, 698 checksum, contents, props, 699 editor->scratch_pool); 700 END_CALLBACK(editor); 701 } 702 703 MARK_COMPLETED(editor, relpath); 704 MARK_PARENT_STABLE(editor, relpath); 705 706 svn_pool_clear(editor->scratch_pool); 707 return svn_error_trace(err); 708} 709 710 711svn_error_t * 712svn_editor_alter_symlink(svn_editor_t *editor, 713 const char *relpath, 714 svn_revnum_t revision, 715 const char *target, 716 apr_hash_t *props) 717{ 718 svn_error_t *err = SVN_NO_ERROR; 719 720 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath)); 721 SVN_ERR_ASSERT(props != NULL || target != NULL); 722 SHOULD_NOT_BE_FINISHED(editor); 723 SHOULD_ALLOW_ALTER(editor, relpath); 724 VERIFY_PARENT_MAY_EXIST(editor, relpath); 725 726 SVN_ERR(check_cancel(editor)); 727 728 if (editor->funcs.cb_alter_symlink) 729 { 730 START_CALLBACK(editor); 731 err = editor->funcs.cb_alter_symlink(editor->baton, 732 relpath, revision, 733 target, props, 734 editor->scratch_pool); 735 END_CALLBACK(editor); 736 } 737 738 MARK_COMPLETED(editor, relpath); 739 MARK_PARENT_STABLE(editor, relpath); 740 741 svn_pool_clear(editor->scratch_pool); 742 return svn_error_trace(err); 743} 744 745 746svn_error_t * 747svn_editor_delete(svn_editor_t *editor, 748 const char *relpath, 749 svn_revnum_t revision) 750{ 751 svn_error_t *err = SVN_NO_ERROR; 752 753 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath)); 754 SHOULD_NOT_BE_FINISHED(editor); 755 SHOULD_NOT_BE_COMPLETED(editor, relpath); 756 VERIFY_PARENT_MAY_EXIST(editor, relpath); 757 CHILD_DELETIONS_ALLOWED(editor, relpath); 758 759 SVN_ERR(check_cancel(editor)); 760 761 if (editor->funcs.cb_delete) 762 { 763 START_CALLBACK(editor); 764 err = editor->funcs.cb_delete(editor->baton, relpath, revision, 765 editor->scratch_pool); 766 END_CALLBACK(editor); 767 } 768 769 MARK_COMPLETED(editor, relpath); 770 MARK_PARENT_STABLE(editor, relpath); 771 772 svn_pool_clear(editor->scratch_pool); 773 return svn_error_trace(err); 774} 775 776 777svn_error_t * 778svn_editor_copy(svn_editor_t *editor, 779 const char *src_relpath, 780 svn_revnum_t src_revision, 781 const char *dst_relpath, 782 svn_revnum_t replaces_rev) 783{ 784 svn_error_t *err = SVN_NO_ERROR; 785 786 SVN_ERR_ASSERT(svn_relpath_is_canonical(src_relpath)); 787 SVN_ERR_ASSERT(svn_relpath_is_canonical(dst_relpath)); 788 SHOULD_NOT_BE_FINISHED(editor); 789 SHOULD_ALLOW_ADD(editor, dst_relpath); 790 VERIFY_PARENT_MAY_EXIST(editor, src_relpath); 791 VERIFY_PARENT_MAY_EXIST(editor, dst_relpath); 792 793 SVN_ERR(check_cancel(editor)); 794 795 if (editor->funcs.cb_copy) 796 { 797 START_CALLBACK(editor); 798 err = editor->funcs.cb_copy(editor->baton, src_relpath, src_revision, 799 dst_relpath, replaces_rev, 800 editor->scratch_pool); 801 END_CALLBACK(editor); 802 } 803 804 MARK_ALLOW_ALTER(editor, dst_relpath); 805 MARK_PARENT_STABLE(editor, dst_relpath); 806 CLEAR_INCOMPLETE(editor, dst_relpath); 807 808 svn_pool_clear(editor->scratch_pool); 809 return svn_error_trace(err); 810} 811 812 813svn_error_t * 814svn_editor_move(svn_editor_t *editor, 815 const char *src_relpath, 816 svn_revnum_t src_revision, 817 const char *dst_relpath, 818 svn_revnum_t replaces_rev) 819{ 820 svn_error_t *err = SVN_NO_ERROR; 821 822 SVN_ERR_ASSERT(svn_relpath_is_canonical(src_relpath)); 823 SVN_ERR_ASSERT(svn_relpath_is_canonical(dst_relpath)); 824 SHOULD_NOT_BE_FINISHED(editor); 825 SHOULD_NOT_BE_COMPLETED(editor, src_relpath); 826 SHOULD_ALLOW_ADD(editor, dst_relpath); 827 VERIFY_PARENT_MAY_EXIST(editor, src_relpath); 828 CHILD_DELETIONS_ALLOWED(editor, src_relpath); 829 VERIFY_PARENT_MAY_EXIST(editor, dst_relpath); 830 831 SVN_ERR(check_cancel(editor)); 832 833 if (editor->funcs.cb_move) 834 { 835 START_CALLBACK(editor); 836 err = editor->funcs.cb_move(editor->baton, src_relpath, src_revision, 837 dst_relpath, replaces_rev, 838 editor->scratch_pool); 839 END_CALLBACK(editor); 840 } 841 842 MARK_ALLOW_ADD(editor, src_relpath); 843 MARK_PARENT_STABLE(editor, src_relpath); 844 MARK_ALLOW_ALTER(editor, dst_relpath); 845 MARK_PARENT_STABLE(editor, dst_relpath); 846 CLEAR_INCOMPLETE(editor, dst_relpath); 847 848 svn_pool_clear(editor->scratch_pool); 849 return svn_error_trace(err); 850} 851 852 853svn_error_t * 854svn_editor_complete(svn_editor_t *editor) 855{ 856 svn_error_t *err = SVN_NO_ERROR; 857 858 SHOULD_NOT_BE_FINISHED(editor); 859#ifdef ENABLE_ORDERING_CHECK 860 SVN_ERR_ASSERT(apr_hash_count(editor->pending_incomplete_children) == 0); 861#endif 862 863 if (editor->funcs.cb_complete) 864 { 865 START_CALLBACK(editor); 866 err = editor->funcs.cb_complete(editor->baton, editor->scratch_pool); 867 END_CALLBACK(editor); 868 } 869 870 MARK_FINISHED(editor); 871 872 svn_pool_clear(editor->scratch_pool); 873 return svn_error_trace(err); 874} 875 876 877svn_error_t * 878svn_editor_abort(svn_editor_t *editor) 879{ 880 svn_error_t *err = SVN_NO_ERROR; 881 882 SHOULD_NOT_BE_FINISHED(editor); 883 884 if (editor->funcs.cb_abort) 885 { 886 START_CALLBACK(editor); 887 err = editor->funcs.cb_abort(editor->baton, editor->scratch_pool); 888 END_CALLBACK(editor); 889 } 890 891 MARK_FINISHED(editor); 892 893 svn_pool_clear(editor->scratch_pool); 894 return svn_error_trace(err); 895} 896