lock.c (302408) | lock.c (362181) |
---|---|
1/* lock.c : functions for manipulating filesystem locks. 2 * 3 * ==================================================================== 4 * Licensed to the Apache Software Foundation (ASF) under one 5 * or more contributor license agreements. See the NOTICE file 6 * distributed with this work for additional information 7 * regarding copyright ownership. The ASF licenses this file 8 * to you under the Apache License, Version 2.0 (the --- 99 unchanged lines hidden (view full) --- 108{ 109 svn_string_t *str = svn_hash_gets(hash, key); 110 return str ? str->data : NULL; 111} 112 113 114/* SVN_ERR_FS_CORRUPT: the lockfile for PATH in FS is corrupt. */ 115static svn_error_t * | 1/* lock.c : functions for manipulating filesystem locks. 2 * 3 * ==================================================================== 4 * Licensed to the Apache Software Foundation (ASF) under one 5 * or more contributor license agreements. See the NOTICE file 6 * distributed with this work for additional information 7 * regarding copyright ownership. The ASF licenses this file 8 * to you under the Apache License, Version 2.0 (the --- 99 unchanged lines hidden (view full) --- 108{ 109 svn_string_t *str = svn_hash_gets(hash, key); 110 return str ? str->data : NULL; 111} 112 113 114/* SVN_ERR_FS_CORRUPT: the lockfile for PATH in FS is corrupt. */ 115static svn_error_t * |
116err_corrupt_lockfile(const char *fs_path, const char *path) | 116err_corrupt_lockfile(const char *fs_path, 117 const char *path) |
117{ 118 return 119 svn_error_createf( 120 SVN_ERR_FS_CORRUPT, 0, 121 _("Corrupt lockfile for path '%s' in filesystem '%s'"), 122 path, fs_path); 123} 124 --- 104 unchanged lines hidden (view full) --- 229 SVN_ERR(svn_stream_open_unique(&stream, &tmp_path, 230 svn_dirent_dirname(digest_path, 231 scratch_pool), 232 svn_io_file_del_none, scratch_pool, 233 scratch_pool)); 234 if ((err = svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR, 235 scratch_pool))) 236 { | 118{ 119 return 120 svn_error_createf( 121 SVN_ERR_FS_CORRUPT, 0, 122 _("Corrupt lockfile for path '%s' in filesystem '%s'"), 123 path, fs_path); 124} 125 --- 104 unchanged lines hidden (view full) --- 230 SVN_ERR(svn_stream_open_unique(&stream, &tmp_path, 231 svn_dirent_dirname(digest_path, 232 scratch_pool), 233 svn_io_file_del_none, scratch_pool, 234 scratch_pool)); 235 if ((err = svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR, 236 scratch_pool))) 237 { |
237 svn_error_clear(svn_stream_close(stream)); | 238 err = svn_error_compose_create(err, svn_stream_close(stream)); |
238 return svn_error_createf(err->apr_err, 239 err, 240 _("Cannot write lock/entries hashfile '%s'"), 241 svn_dirent_local_style(tmp_path, 242 scratch_pool)); 243 } 244 245 SVN_ERR(svn_stream_close(stream)); | 239 return svn_error_createf(err->apr_err, 240 err, 241 _("Cannot write lock/entries hashfile '%s'"), 242 svn_dirent_local_style(tmp_path, 243 scratch_pool)); 244 } 245 246 SVN_ERR(svn_stream_close(stream)); |
246 SVN_ERR(svn_io_file_rename(tmp_path, digest_path, scratch_pool)); | 247 SVN_ERR(svn_io_file_rename2(tmp_path, digest_path, FALSE, scratch_pool)); |
247 SVN_ERR(svn_io_copy_perms(perms_reference, digest_path, scratch_pool)); 248 return SVN_NO_ERROR; 249} 250 251 252/* Parse the file at DIGEST_PATH, populating the lock LOCK_P in that 253 file (if it exists, and if *LOCK_P is non-NULL) and the hash of 254 CHILDREN_P (if any exist, and if *CHILDREN_P is non-NULL). Use POOL --- 26 unchanged lines hidden (view full) --- 281 if (kind == svn_node_file && !lock_p && !children_p) 282 return SVN_NO_ERROR; 283 284 SVN_ERR(svn_stream_open_readonly(&stream, digest_path, pool, pool)); 285 286 hash = apr_hash_make(pool); 287 if ((err = svn_hash_read2(hash, stream, SVN_HASH_TERMINATOR, pool))) 288 { | 248 SVN_ERR(svn_io_copy_perms(perms_reference, digest_path, scratch_pool)); 249 return SVN_NO_ERROR; 250} 251 252 253/* Parse the file at DIGEST_PATH, populating the lock LOCK_P in that 254 file (if it exists, and if *LOCK_P is non-NULL) and the hash of 255 CHILDREN_P (if any exist, and if *CHILDREN_P is non-NULL). Use POOL --- 26 unchanged lines hidden (view full) --- 282 if (kind == svn_node_file && !lock_p && !children_p) 283 return SVN_NO_ERROR; 284 285 SVN_ERR(svn_stream_open_readonly(&stream, digest_path, pool, pool)); 286 287 hash = apr_hash_make(pool); 288 if ((err = svn_hash_read2(hash, stream, SVN_HASH_TERMINATOR, pool))) 289 { |
289 svn_error_clear(svn_stream_close(stream)); | 290 err = svn_error_compose_create(err, svn_stream_close(stream)); |
290 return svn_error_createf(err->apr_err, 291 err, 292 _("Can't parse lock/entries hashfile '%s'"), 293 svn_dirent_local_style(digest_path, pool)); 294 } 295 SVN_ERR(svn_stream_close(stream)); 296 297 /* If our caller cares, see if we have a lock path in our hash. If --- 167 unchanged lines hidden (view full) --- 465 return SVN_NO_ERROR; 466} 467 468static svn_error_t * 469unlock_single(svn_fs_t *fs, 470 svn_lock_t *lock, 471 apr_pool_t *pool); 472 | 291 return svn_error_createf(err->apr_err, 292 err, 293 _("Can't parse lock/entries hashfile '%s'"), 294 svn_dirent_local_style(digest_path, pool)); 295 } 296 SVN_ERR(svn_stream_close(stream)); 297 298 /* If our caller cares, see if we have a lock path in our hash. If --- 167 unchanged lines hidden (view full) --- 466 return SVN_NO_ERROR; 467} 468 469static svn_error_t * 470unlock_single(svn_fs_t *fs, 471 svn_lock_t *lock, 472 apr_pool_t *pool); 473 |
474/* Check if LOCK has been already expired. */ 475static svn_boolean_t lock_expired(const svn_lock_t *lock) 476{ 477 return lock->expiration_date && (apr_time_now() > lock->expiration_date); 478} 479 |
|
473/* Set *LOCK_P to the lock for PATH in FS. HAVE_WRITE_LOCK should be 474 TRUE if the caller (or one of its callers) has taken out the 475 repository-wide write lock, FALSE otherwise. If MUST_EXIST is 476 not set, the function will simply return NULL in *LOCK_P instead 477 of creating an SVN_FS__ERR_NO_SUCH_LOCK error in case the lock 478 was not found (much faster). Use POOL for allocations. */ 479static svn_error_t * 480get_lock(svn_lock_t **lock_p, --- 13 unchanged lines hidden (view full) --- 494 *lock_p = NULL; 495 if (kind != svn_node_none) 496 SVN_ERR(read_digest_file(NULL, &lock, fs->path, digest_path, pool)); 497 498 if (! lock) 499 return must_exist ? SVN_FS__ERR_NO_SUCH_LOCK(fs, path) : SVN_NO_ERROR; 500 501 /* Don't return an expired lock. */ | 480/* Set *LOCK_P to the lock for PATH in FS. HAVE_WRITE_LOCK should be 481 TRUE if the caller (or one of its callers) has taken out the 482 repository-wide write lock, FALSE otherwise. If MUST_EXIST is 483 not set, the function will simply return NULL in *LOCK_P instead 484 of creating an SVN_FS__ERR_NO_SUCH_LOCK error in case the lock 485 was not found (much faster). Use POOL for allocations. */ 486static svn_error_t * 487get_lock(svn_lock_t **lock_p, --- 13 unchanged lines hidden (view full) --- 501 *lock_p = NULL; 502 if (kind != svn_node_none) 503 SVN_ERR(read_digest_file(NULL, &lock, fs->path, digest_path, pool)); 504 505 if (! lock) 506 return must_exist ? SVN_FS__ERR_NO_SUCH_LOCK(fs, path) : SVN_NO_ERROR; 507 508 /* Don't return an expired lock. */ |
502 if (lock->expiration_date && (apr_time_now() > lock->expiration_date)) | 509 if (lock_expired(lock)) |
503 { 504 /* Only remove the lock if we have the write lock. 505 Read operations shouldn't change the filesystem. */ 506 if (have_write_lock) 507 SVN_ERR(unlock_single(fs, lock, pool)); 508 return SVN_FS__ERR_LOCK_EXPIRED(fs, lock->token); 509 } 510 --- 30 unchanged lines hidden (view full) --- 541 else 542 SVN_ERR(err); 543 544 *lock_p = lock; 545 return SVN_NO_ERROR; 546} 547 548 | 510 { 511 /* Only remove the lock if we have the write lock. 512 Read operations shouldn't change the filesystem. */ 513 if (have_write_lock) 514 SVN_ERR(unlock_single(fs, lock, pool)); 515 return SVN_FS__ERR_LOCK_EXPIRED(fs, lock->token); 516 } 517 --- 30 unchanged lines hidden (view full) --- 548 else 549 SVN_ERR(err); 550 551 *lock_p = lock; 552 return SVN_NO_ERROR; 553} 554 555 |
549/* Baton for locks_walker(). */ 550typedef struct walk_locks_baton_t 551{ 552 svn_fs_get_locks_callback_t get_locks_func; 553 void *get_locks_baton; 554 svn_fs_t *fs; 555} walk_locks_baton_t; 556 557/* Implements walk_digests_callback_t. */ 558static svn_error_t * 559locks_walker(void *baton, 560 const char *fs_path, 561 const char *digest_path, 562 svn_lock_t *lock, 563 svn_boolean_t have_write_lock, 564 apr_pool_t *pool) 565{ 566 walk_locks_baton_t *wlb = baton; 567 568 if (lock) 569 { 570 /* Don't report an expired lock. */ 571 if (lock->expiration_date == 0 572 || (apr_time_now() <= lock->expiration_date)) 573 { 574 if (wlb->get_locks_func) 575 SVN_ERR(wlb->get_locks_func(wlb->get_locks_baton, lock, pool)); 576 } 577 else 578 { 579 /* Only remove the lock if we have the write lock. 580 Read operations shouldn't change the filesystem. */ 581 if (have_write_lock) 582 SVN_ERR(unlock_single(wlb->fs, lock, pool)); 583 } 584 } 585 586 return SVN_NO_ERROR; 587} 588 589/* Callback type for walk_digest_files(). 590 * 591 * LOCK come from a read_digest_file(digest_path) call. 592 */ 593typedef svn_error_t *(*walk_digests_callback_t)(void *baton, 594 const char *fs_path, 595 const char *digest_path, 596 svn_lock_t *lock, 597 svn_boolean_t have_write_lock, 598 apr_pool_t *pool); 599 600/* A function that calls WALK_DIGESTS_FUNC/WALK_DIGESTS_BATON for 601 all lock digest files in and under PATH in FS. | 556/* A function that calls GET_LOCKS_FUNC/GET_LOCKS_BATON for 557 all locks in and under PATH in FS. |
602 HAVE_WRITE_LOCK should be true if the caller (directly or indirectly) 603 has the FS write lock. */ 604static svn_error_t * | 558 HAVE_WRITE_LOCK should be true if the caller (directly or indirectly) 559 has the FS write lock. */ 560static svn_error_t * |
605walk_digest_files(const char *fs_path, 606 const char *digest_path, 607 walk_digests_callback_t walk_digests_func, 608 void *walk_digests_baton, 609 svn_boolean_t have_write_lock, 610 apr_pool_t *pool) | 561walk_locks(svn_fs_t *fs, 562 const char *digest_path, 563 svn_fs_get_locks_callback_t get_locks_func, 564 void *get_locks_baton, 565 svn_boolean_t have_write_lock, 566 apr_pool_t *pool) |
611{ 612 apr_hash_index_t *hi; 613 apr_hash_t *children; 614 apr_pool_t *subpool; 615 svn_lock_t *lock; 616 617 /* First, send up any locks in the current digest file. */ | 567{ 568 apr_hash_index_t *hi; 569 apr_hash_t *children; 570 apr_pool_t *subpool; 571 svn_lock_t *lock; 572 573 /* First, send up any locks in the current digest file. */ |
618 SVN_ERR(read_digest_file(&children, &lock, fs_path, digest_path, pool)); | 574 SVN_ERR(read_digest_file(&children, &lock, fs->path, digest_path, pool)); |
619 | 575 |
620 SVN_ERR(walk_digests_func(walk_digests_baton, fs_path, digest_path, lock, 621 have_write_lock, pool)); | 576 if (lock && lock_expired(lock)) 577 { 578 /* Only remove the lock if we have the write lock. 579 Read operations shouldn't change the filesystem. */ 580 if (have_write_lock) 581 SVN_ERR(unlock_single(fs, lock, pool)); 582 } 583 else if (lock) 584 { 585 SVN_ERR(get_locks_func(get_locks_baton, lock, pool)); 586 } |
622 623 /* Now, report all the child entries (if any; bail otherwise). */ 624 if (! apr_hash_count(children)) 625 return SVN_NO_ERROR; 626 subpool = svn_pool_create(pool); 627 for (hi = apr_hash_first(pool, children); hi; hi = apr_hash_next(hi)) 628 { 629 const char *digest = apr_hash_this_key(hi); 630 svn_pool_clear(subpool); 631 632 SVN_ERR(read_digest_file | 587 588 /* Now, report all the child entries (if any; bail otherwise). */ 589 if (! apr_hash_count(children)) 590 return SVN_NO_ERROR; 591 subpool = svn_pool_create(pool); 592 for (hi = apr_hash_first(pool, children); hi; hi = apr_hash_next(hi)) 593 { 594 const char *digest = apr_hash_this_key(hi); 595 svn_pool_clear(subpool); 596 597 SVN_ERR(read_digest_file |
633 (NULL, &lock, fs_path, 634 digest_path_from_digest(fs_path, digest, subpool), subpool)); | 598 (NULL, &lock, fs->path, 599 digest_path_from_digest(fs->path, digest, subpool), subpool)); |
635 | 600 |
636 SVN_ERR(walk_digests_func(walk_digests_baton, fs_path, digest_path, lock, 637 have_write_lock, subpool)); | 601 if (lock && lock_expired(lock)) 602 { 603 /* Only remove the lock if we have the write lock. 604 Read operations shouldn't change the filesystem. */ 605 if (have_write_lock) 606 SVN_ERR(unlock_single(fs, lock, pool)); 607 } 608 else if (lock) 609 { 610 SVN_ERR(get_locks_func(get_locks_baton, lock, pool)); 611 } |
638 } 639 svn_pool_destroy(subpool); 640 return SVN_NO_ERROR; 641} 642 | 612 } 613 svn_pool_destroy(subpool); 614 return SVN_NO_ERROR; 615} 616 |
643/* A function that calls GET_LOCKS_FUNC/GET_LOCKS_BATON for 644 all locks in and under PATH in FS. 645 HAVE_WRITE_LOCK should be true if the caller (directly or indirectly) 646 has the FS write lock. */ 647static svn_error_t * 648walk_locks(svn_fs_t *fs, 649 const char *digest_path, 650 svn_fs_get_locks_callback_t get_locks_func, 651 void *get_locks_baton, 652 svn_boolean_t have_write_lock, 653 apr_pool_t *pool) 654{ 655 walk_locks_baton_t wlb; 656 657 wlb.get_locks_func = get_locks_func; 658 wlb.get_locks_baton = get_locks_baton; 659 wlb.fs = fs; 660 SVN_ERR(walk_digest_files(fs->path, digest_path, locks_walker, &wlb, 661 have_write_lock, pool)); 662 return SVN_NO_ERROR; 663} 664 665 | |
666/* Utility function: verify that a lock can be used. Interesting 667 errors returned from this function: 668 669 SVN_ERR_FS_NO_USER: No username attached to FS. 670 SVN_ERR_FS_LOCK_OWNER_MISMATCH: FS's username doesn't match LOCK's owner. 671 SVN_ERR_FS_BAD_LOCK_TOKEN: FS doesn't hold matching lock-token for LOCK. 672 */ 673static svn_error_t * --- 58 unchanged lines hidden (view full) --- 732 SVN_ERR(get_lock_helper(fs, &lock, path, have_write_lock, 733 scratch_pool)); 734 if (lock) 735 SVN_ERR(verify_lock(fs, lock)); 736 } 737 return SVN_NO_ERROR; 738} 739 | 617/* Utility function: verify that a lock can be used. Interesting 618 errors returned from this function: 619 620 SVN_ERR_FS_NO_USER: No username attached to FS. 621 SVN_ERR_FS_LOCK_OWNER_MISMATCH: FS's username doesn't match LOCK's owner. 622 SVN_ERR_FS_BAD_LOCK_TOKEN: FS doesn't hold matching lock-token for LOCK. 623 */ 624static svn_error_t * --- 58 unchanged lines hidden (view full) --- 683 SVN_ERR(get_lock_helper(fs, &lock, path, have_write_lock, 684 scratch_pool)); 685 if (lock) 686 SVN_ERR(verify_lock(fs, lock)); 687 } 688 return SVN_NO_ERROR; 689} 690 |
691/* Helper function called from the lock and unlock code. 692 UPDATES is a map from "const char *" parent paths to "apr_array_header_t *" 693 arrays of child paths. For all of the parent paths of PATH this function 694 adds PATH to the corresponding array of child paths. */ 695static void 696schedule_index_update(apr_hash_t *updates, 697 const char *path, 698 apr_pool_t *scratch_pool) 699{ 700 apr_pool_t *hashpool = apr_hash_pool_get(updates); 701 const char *parent_path = path; 702 703 while (! svn_fspath__is_root(parent_path, strlen(parent_path))) 704 { 705 apr_array_header_t *children; 706 707 parent_path = svn_fspath__dirname(parent_path, scratch_pool); 708 children = svn_hash_gets(updates, parent_path); 709 710 if (! children) 711 { 712 children = apr_array_make(hashpool, 8, sizeof(const char *)); 713 svn_hash_sets(updates, apr_pstrdup(hashpool, parent_path), children); 714 } 715 716 APR_ARRAY_PUSH(children, const char *) = path; 717 } 718} 719 |
|
740/* The effective arguments for lock_body() below. */ 741typedef struct lock_baton_t { 742 svn_fs_t *fs; 743 apr_array_header_t *targets; 744 apr_array_header_t *infos; 745 const char *comment; 746 svn_boolean_t is_dav_comment; 747 apr_time_t expiration_date; --- 106 unchanged lines hidden (view full) --- 854 } 855 } 856 857 return SVN_NO_ERROR; 858} 859 860typedef struct lock_info_t { 861 const char *path; | 720/* The effective arguments for lock_body() below. */ 721typedef struct lock_baton_t { 722 svn_fs_t *fs; 723 apr_array_header_t *targets; 724 apr_array_header_t *infos; 725 const char *comment; 726 svn_boolean_t is_dav_comment; 727 apr_time_t expiration_date; --- 106 unchanged lines hidden (view full) --- 834 } 835 } 836 837 return SVN_NO_ERROR; 838} 839 840typedef struct lock_info_t { 841 const char *path; |
862 const char *component; | |
863 svn_lock_t *lock; 864 svn_error_t *fs_err; 865} lock_info_t; 866 867/* The body of svn_fs_x__lock(), which see. 868 869 BATON is a 'lock_baton_t *' holding the effective arguments. 870 BATON->targets is an array of 'svn_sort__item_t' targets, sorted by 871 path, mapping canonical path to 'svn_fs_lock_target_t'. Set 872 BATON->infos to an array of 'lock_info_t' holding the results. For 873 the other arguments, see svn_fs_lock_many(). 874 875 This implements the svn_fs_x__with_write_lock() 'body' callback 876 type, and assumes that the write lock is held. 877 */ 878static svn_error_t * | 842 svn_lock_t *lock; 843 svn_error_t *fs_err; 844} lock_info_t; 845 846/* The body of svn_fs_x__lock(), which see. 847 848 BATON is a 'lock_baton_t *' holding the effective arguments. 849 BATON->targets is an array of 'svn_sort__item_t' targets, sorted by 850 path, mapping canonical path to 'svn_fs_lock_target_t'. Set 851 BATON->infos to an array of 'lock_info_t' holding the results. For 852 the other arguments, see svn_fs_lock_many(). 853 854 This implements the svn_fs_x__with_write_lock() 'body' callback 855 type, and assumes that the write lock is held. 856 */ 857static svn_error_t * |
879lock_body(void *baton, apr_pool_t *pool) | 858lock_body(void *baton, 859 apr_pool_t *pool) |
880{ 881 lock_baton_t *lb = baton; 882 svn_fs_root_t *root; 883 svn_revnum_t youngest; 884 const char *rev_0_path; | 860{ 861 lock_baton_t *lb = baton; 862 svn_fs_root_t *root; 863 svn_revnum_t youngest; 864 const char *rev_0_path; |
885 int i, outstanding = 0; | 865 int i; 866 apr_hash_t *index_updates = apr_hash_make(pool); 867 apr_hash_index_t *hi; |
886 apr_pool_t *iterpool = svn_pool_create(pool); 887 | 868 apr_pool_t *iterpool = svn_pool_create(pool); 869 |
888 lb->infos = apr_array_make(lb->result_pool, lb->targets->nelts, 889 sizeof(lock_info_t)); 890 | |
891 /* Until we implement directory locks someday, we only allow locks | 870 /* Until we implement directory locks someday, we only allow locks |
892 on files or non-existent paths. */ | 871 on files. */ |
893 /* Use fs->vtable->foo instead of svn_fs_foo to avoid circular 894 library dependencies, which are not portable. */ 895 SVN_ERR(lb->fs->vtable->youngest_rev(&youngest, lb->fs, pool)); 896 SVN_ERR(lb->fs->vtable->revision_root(&root, lb->fs, youngest, pool)); 897 898 for (i = 0; i < lb->targets->nelts; ++i) 899 { 900 const svn_sort__item_t *item = &APR_ARRAY_IDX(lb->targets, i, 901 svn_sort__item_t); | 872 /* Use fs->vtable->foo instead of svn_fs_foo to avoid circular 873 library dependencies, which are not portable. */ 874 SVN_ERR(lb->fs->vtable->youngest_rev(&youngest, lb->fs, pool)); 875 SVN_ERR(lb->fs->vtable->revision_root(&root, lb->fs, youngest, pool)); 876 877 for (i = 0; i < lb->targets->nelts; ++i) 878 { 879 const svn_sort__item_t *item = &APR_ARRAY_IDX(lb->targets, i, 880 svn_sort__item_t); |
902 const svn_fs_lock_target_t *target = item->value; | |
903 lock_info_t info; 904 905 svn_pool_clear(iterpool); 906 907 info.path = item->key; | 881 lock_info_t info; 882 883 svn_pool_clear(iterpool); 884 885 info.path = item->key; |
908 SVN_ERR(check_lock(&info.fs_err, info.path, target, lb, root, 909 youngest, iterpool)); | |
910 info.lock = NULL; | 886 info.lock = NULL; |
911 info.component = NULL; 912 APR_ARRAY_PUSH(lb->infos, lock_info_t) = info; | 887 info.fs_err = SVN_NO_ERROR; 888 889 SVN_ERR(check_lock(&info.fs_err, info.path, item->value, lb, root, 890 youngest, iterpool)); 891 892 /* If no error occurred while pre-checking, schedule the index updates for 893 this path. */ |
913 if (!info.fs_err) | 894 if (!info.fs_err) |
914 ++outstanding; | 895 schedule_index_update(index_updates, info.path, iterpool); 896 897 APR_ARRAY_PUSH(lb->infos, lock_info_t) = info; |
915 } 916 917 rev_0_path = svn_fs_x__path_rev_absolute(lb->fs, 0, pool); 918 | 898 } 899 900 rev_0_path = svn_fs_x__path_rev_absolute(lb->fs, 0, pool); 901 |
919 /* Given the paths: | 902 /* We apply the scheduled index updates before writing the actual locks. |
920 | 903 |
921 /foo/bar/f 922 /foo/bar/g 923 /zig/x 924 925 we loop through repeatedly. The first pass sees '/' on all paths 926 and writes the '/' index. The second pass sees '/foo' twice and 927 writes that index followed by '/zig' and that index. The third 928 pass sees '/foo/bar' twice and writes that index, and then writes 929 the lock for '/zig/x'. The fourth pass writes the locks for 930 '/foo/bar/f' and '/foo/bar/g'. 931 | |
932 Writing indices before locks is correct: if interrupted it leaves 933 indices without locks rather than locks without indices. An 934 index without a lock is consistent in that it always shows up as 935 unlocked in svn_fs_x__allow_locked_operation. A lock without an 936 index is inconsistent, svn_fs_x__allow_locked_operation will 937 show locked on the file but unlocked on the parent. */ 938 | 904 Writing indices before locks is correct: if interrupted it leaves 905 indices without locks rather than locks without indices. An 906 index without a lock is consistent in that it always shows up as 907 unlocked in svn_fs_x__allow_locked_operation. A lock without an 908 index is inconsistent, svn_fs_x__allow_locked_operation will 909 show locked on the file but unlocked on the parent. */ 910 |
911 for (hi = apr_hash_first(pool, index_updates); hi; hi = apr_hash_next(hi)) 912 { 913 const char *path = apr_hash_this_key(hi); 914 apr_array_header_t *children = apr_hash_this_val(hi); |
|
939 | 915 |
940 while (outstanding) | 916 svn_pool_clear(iterpool); 917 SVN_ERR(add_to_digest(lb->fs->path, children, path, rev_0_path, 918 iterpool)); 919 } 920 921 for (i = 0; i < lb->infos->nelts; ++i) |
941 { | 922 { |
942 const char *last_path = NULL; 943 apr_array_header_t *paths; | 923 struct lock_info_t *info = &APR_ARRAY_IDX(lb->infos, i, 924 struct lock_info_t); 925 svn_sort__item_t *item = &APR_ARRAY_IDX(lb->targets, i, svn_sort__item_t); 926 svn_fs_lock_target_t *target = item->value; |
944 945 svn_pool_clear(iterpool); | 927 928 svn_pool_clear(iterpool); |
946 paths = apr_array_make(iterpool, 1, sizeof(const char *)); | |
947 | 929 |
948 for (i = 0; i < lb->infos->nelts; ++i) | 930 if (! info->fs_err) |
949 { | 931 { |
950 lock_info_t *info = &APR_ARRAY_IDX(lb->infos, i, lock_info_t); 951 const svn_sort__item_t *item = &APR_ARRAY_IDX(lb->targets, i, 952 svn_sort__item_t); 953 const svn_fs_lock_target_t *target = item->value; | 932 info->lock = svn_lock_create(lb->result_pool); 933 if (target->token) 934 info->lock->token = apr_pstrdup(lb->result_pool, target->token); 935 else 936 SVN_ERR(svn_fs_x__generate_lock_token(&(info->lock->token), lb->fs, 937 lb->result_pool)); |
954 | 938 |
955 if (!info->fs_err && !info->lock) 956 { 957 if (!info->component) 958 { 959 info->component = info->path; 960 APR_ARRAY_PUSH(paths, const char *) = info->path; 961 last_path = "/"; 962 } 963 else 964 { 965 info->component = strchr(info->component + 1, '/'); 966 if (!info->component) 967 { 968 /* The component is a path to lock, this cannot 969 match a previous path that need to be indexed. */ 970 if (paths->nelts) 971 { 972 SVN_ERR(add_to_digest(lb->fs->path, paths, last_path, 973 rev_0_path, iterpool)); 974 apr_array_clear(paths); 975 last_path = NULL; 976 } | 939 /* The INFO->PATH is already allocated in LB->RESULT_POOL as a result 940 of svn_fspath__canonicalize() (see svn_fs_x__lock()). */ 941 info->lock->path = info->path; 942 info->lock->owner = apr_pstrdup(lb->result_pool, 943 lb->fs->access_ctx->username); 944 info->lock->comment = apr_pstrdup(lb->result_pool, lb->comment); 945 info->lock->is_dav_comment = lb->is_dav_comment; 946 info->lock->creation_date = apr_time_now(); 947 info->lock->expiration_date = lb->expiration_date; |
977 | 948 |
978 info->lock = svn_lock_create(lb->result_pool); 979 if (target->token) 980 info->lock->token = target->token; 981 else 982 SVN_ERR(svn_fs_x__generate_lock_token( 983 &(info->lock->token), lb->fs, 984 lb->result_pool)); 985 info->lock->path = info->path; 986 info->lock->owner = lb->fs->access_ctx->username; 987 info->lock->comment = lb->comment; 988 info->lock->is_dav_comment = lb->is_dav_comment; 989 info->lock->creation_date = apr_time_now(); 990 info->lock->expiration_date = lb->expiration_date; 991 992 info->fs_err = set_lock(lb->fs->path, info->lock, 993 rev_0_path, iterpool); 994 --outstanding; 995 } 996 else 997 { 998 /* The component is a path to an index. */ 999 apr_size_t len = info->component - info->path; 1000 1001 if (last_path 1002 && (strncmp(last_path, info->path, len) 1003 || strlen(last_path) != len)) 1004 { 1005 /* No match to the previous paths to index. */ 1006 SVN_ERR(add_to_digest(lb->fs->path, paths, last_path, 1007 rev_0_path, iterpool)); 1008 apr_array_clear(paths); 1009 last_path = NULL; 1010 } 1011 APR_ARRAY_PUSH(paths, const char *) = info->path; 1012 if (!last_path) 1013 last_path = apr_pstrndup(iterpool, info->path, len); 1014 } 1015 } 1016 } 1017 1018 if (last_path && i == lb->infos->nelts - 1) 1019 SVN_ERR(add_to_digest(lb->fs->path, paths, last_path, 1020 rev_0_path, iterpool)); | 949 info->fs_err = set_lock(lb->fs->path, info->lock, rev_0_path, 950 iterpool); |
1021 } 1022 } 1023 | 951 } 952 } 953 |
954 svn_pool_destroy(iterpool); |
|
1024 return SVN_NO_ERROR; 1025} 1026 1027/* The effective arguments for unlock_body() below. */ 1028typedef struct unlock_baton_t { 1029 svn_fs_t *fs; 1030 apr_array_header_t *targets; 1031 apr_array_header_t *infos; --- 24 unchanged lines hidden (view full) --- 1056 lock->owner); 1057 } 1058 1059 return SVN_NO_ERROR; 1060} 1061 1062typedef struct unlock_info_t { 1063 const char *path; | 955 return SVN_NO_ERROR; 956} 957 958/* The effective arguments for unlock_body() below. */ 959typedef struct unlock_baton_t { 960 svn_fs_t *fs; 961 apr_array_header_t *targets; 962 apr_array_header_t *infos; --- 24 unchanged lines hidden (view full) --- 987 lock->owner); 988 } 989 990 return SVN_NO_ERROR; 991} 992 993typedef struct unlock_info_t { 994 const char *path; |
1064 const char *component; | |
1065 svn_error_t *fs_err; 1066 svn_boolean_t done; | 995 svn_error_t *fs_err; 996 svn_boolean_t done; |
1067 int components; | |
1068} unlock_info_t; 1069 1070/* The body of svn_fs_x__unlock(), which see. 1071 1072 BATON is a 'unlock_baton_t *' holding the effective arguments. 1073 BATON->targets is an array of 'svn_sort__item_t' targets, sorted by 1074 path, mapping canonical path to (const char *) token. Set 1075 BATON->infos to an array of 'unlock_info_t' results. For the other 1076 arguments, see svn_fs_unlock_many(). 1077 1078 This implements the svn_fs_x__with_write_lock() 'body' callback 1079 type, and assumes that the write lock is held. 1080 */ 1081static svn_error_t * | 997} unlock_info_t; 998 999/* The body of svn_fs_x__unlock(), which see. 1000 1001 BATON is a 'unlock_baton_t *' holding the effective arguments. 1002 BATON->targets is an array of 'svn_sort__item_t' targets, sorted by 1003 path, mapping canonical path to (const char *) token. Set 1004 BATON->infos to an array of 'unlock_info_t' results. For the other 1005 arguments, see svn_fs_unlock_many(). 1006 1007 This implements the svn_fs_x__with_write_lock() 'body' callback 1008 type, and assumes that the write lock is held. 1009 */ 1010static svn_error_t * |
1082unlock_body(void *baton, apr_pool_t *pool) | 1011unlock_body(void *baton, 1012 apr_pool_t *pool) |
1083{ 1084 unlock_baton_t *ub = baton; 1085 svn_fs_root_t *root; 1086 svn_revnum_t youngest; 1087 const char *rev_0_path; | 1013{ 1014 unlock_baton_t *ub = baton; 1015 svn_fs_root_t *root; 1016 svn_revnum_t youngest; 1017 const char *rev_0_path; |
1088 int i, max_components = 0, outstanding = 0; | 1018 int i; 1019 apr_hash_t *indices_updates = apr_hash_make(pool); 1020 apr_hash_index_t *hi; |
1089 apr_pool_t *iterpool = svn_pool_create(pool); 1090 | 1021 apr_pool_t *iterpool = svn_pool_create(pool); 1022 |
1091 ub->infos = apr_array_make(ub->result_pool, ub->targets->nelts, 1092 sizeof( unlock_info_t)); 1093 | |
1094 SVN_ERR(ub->fs->vtable->youngest_rev(&youngest, ub->fs, pool)); 1095 SVN_ERR(ub->fs->vtable->revision_root(&root, ub->fs, youngest, pool)); 1096 1097 for (i = 0; i < ub->targets->nelts; ++i) 1098 { 1099 const svn_sort__item_t *item = &APR_ARRAY_IDX(ub->targets, i, 1100 svn_sort__item_t); 1101 const char *token = item->value; | 1023 SVN_ERR(ub->fs->vtable->youngest_rev(&youngest, ub->fs, pool)); 1024 SVN_ERR(ub->fs->vtable->revision_root(&root, ub->fs, youngest, pool)); 1025 1026 for (i = 0; i < ub->targets->nelts; ++i) 1027 { 1028 const svn_sort__item_t *item = &APR_ARRAY_IDX(ub->targets, i, 1029 svn_sort__item_t); 1030 const char *token = item->value; |
1102 unlock_info_t info = { 0 }; | 1031 unlock_info_t info; |
1103 1104 svn_pool_clear(iterpool); 1105 1106 info.path = item->key; | 1032 1033 svn_pool_clear(iterpool); 1034 1035 info.path = item->key; |
1036 info.fs_err = SVN_NO_ERROR; 1037 info.done = FALSE; 1038 |
|
1107 if (!ub->skip_check) 1108 SVN_ERR(check_unlock(&info.fs_err, info.path, token, ub, root, 1109 iterpool)); | 1039 if (!ub->skip_check) 1040 SVN_ERR(check_unlock(&info.fs_err, info.path, token, ub, root, 1041 iterpool)); |
1042 1043 /* If no error occurred while pre-checking, schedule the index updates for 1044 this path. */ |
|
1110 if (!info.fs_err) | 1045 if (!info.fs_err) |
1111 { 1112 const char *s; | 1046 schedule_index_update(indices_updates, info.path, iterpool); |
1113 | 1047 |
1114 info.components = 1; 1115 info.component = info.path; 1116 while((s = strchr(info.component + 1, '/'))) 1117 { 1118 info.component = s; 1119 ++info.components; 1120 } 1121 1122 if (info.components > max_components) 1123 max_components = info.components; 1124 1125 ++outstanding; 1126 } | |
1127 APR_ARRAY_PUSH(ub->infos, unlock_info_t) = info; 1128 } 1129 1130 rev_0_path = svn_fs_x__path_rev_absolute(ub->fs, 0, pool); 1131 | 1048 APR_ARRAY_PUSH(ub->infos, unlock_info_t) = info; 1049 } 1050 1051 rev_0_path = svn_fs_x__path_rev_absolute(ub->fs, 0, pool); 1052 |
1132 for (i = max_components; i >= 0; --i) | 1053 /* Unlike the lock_body(), we need to delete locks *before* we start to 1054 update indices. */ 1055 1056 for (i = 0; i < ub->infos->nelts; ++i) |
1133 { | 1057 { |
1134 const char *last_path = NULL; 1135 apr_array_header_t *paths; 1136 int j; | 1058 struct unlock_info_t *info = &APR_ARRAY_IDX(ub->infos, i, 1059 struct unlock_info_t); |
1137 1138 svn_pool_clear(iterpool); | 1060 1061 svn_pool_clear(iterpool); |
1139 paths = apr_array_make(pool, 1, sizeof(const char *)); | |
1140 | 1062 |
1141 for (j = 0; j < ub->infos->nelts; ++j) | 1063 if (! info->fs_err) |
1142 { | 1064 { |
1143 unlock_info_t *info = &APR_ARRAY_IDX(ub->infos, j, unlock_info_t); | 1065 SVN_ERR(delete_lock(ub->fs->path, info->path, iterpool)); 1066 info->done = TRUE; 1067 } 1068 } |
1144 | 1069 |
1145 if (!info->fs_err && info->path) 1146 { | 1070 for (hi = apr_hash_first(pool, indices_updates); hi; hi = apr_hash_next(hi)) 1071 { 1072 const char *path = apr_hash_this_key(hi); 1073 apr_array_header_t *children = apr_hash_this_val(hi); |
1147 | 1074 |
1148 if (info->components == i) 1149 { 1150 SVN_ERR(delete_lock(ub->fs->path, info->path, iterpool)); 1151 info->done = TRUE; 1152 } 1153 else if (info->components > i) 1154 { 1155 apr_size_t len = info->component - info->path; 1156 1157 if (last_path 1158 && strcmp(last_path, "/") 1159 && (strncmp(last_path, info->path, len) 1160 || strlen(last_path) != len)) 1161 { 1162 SVN_ERR(delete_from_digest(ub->fs->path, paths, last_path, 1163 rev_0_path, iterpool)); 1164 apr_array_clear(paths); 1165 last_path = NULL; 1166 } 1167 APR_ARRAY_PUSH(paths, const char *) = info->path; 1168 if (!last_path) 1169 { 1170 if (info->component > info->path) 1171 last_path = apr_pstrndup(pool, info->path, len); 1172 else 1173 last_path = "/"; 1174 } 1175 1176 if (info->component > info->path) 1177 { 1178 --info->component; 1179 while(info->component[0] != '/') 1180 --info->component; 1181 } 1182 } 1183 } 1184 1185 if (last_path && j == ub->infos->nelts - 1) 1186 SVN_ERR(delete_from_digest(ub->fs->path, paths, last_path, 1187 rev_0_path, iterpool)); 1188 } | 1075 svn_pool_clear(iterpool); 1076 SVN_ERR(delete_from_digest(ub->fs->path, children, path, rev_0_path, 1077 iterpool)); |
1189 } 1190 | 1078 } 1079 |
1080 svn_pool_destroy(iterpool); |
|
1191 return SVN_NO_ERROR; 1192} 1193 1194/* Unlock the lock described by LOCK->path and LOCK->token in FS. 1195 1196 This assumes that the write lock is held. 1197 */ 1198static svn_error_t * --- 7 unchanged lines hidden (view full) --- 1206 sizeof(svn_sort__item_t)); 1207 item.key = lock->path; 1208 item.klen = strlen(item.key); 1209 item.value = (char*)lock->token; 1210 APR_ARRAY_PUSH(targets, svn_sort__item_t) = item; 1211 1212 ub.fs = fs; 1213 ub.targets = targets; | 1081 return SVN_NO_ERROR; 1082} 1083 1084/* Unlock the lock described by LOCK->path and LOCK->token in FS. 1085 1086 This assumes that the write lock is held. 1087 */ 1088static svn_error_t * --- 7 unchanged lines hidden (view full) --- 1096 sizeof(svn_sort__item_t)); 1097 item.key = lock->path; 1098 item.klen = strlen(item.key); 1099 item.value = (char*)lock->token; 1100 APR_ARRAY_PUSH(targets, svn_sort__item_t) = item; 1101 1102 ub.fs = fs; 1103 ub.targets = targets; |
1104 ub.infos = apr_array_make(scratch_pool, targets->nelts, 1105 sizeof(struct unlock_info_t)); |
|
1214 ub.skip_check = TRUE; 1215 ub.result_pool = scratch_pool; 1216 1217 /* No ub.infos[].fs_err error because skip_check is TRUE. */ 1218 SVN_ERR(unlock_body(&ub, scratch_pool)); 1219 1220 return SVN_NO_ERROR; 1221} --- 45 unchanged lines hidden (view full) --- 1267 } 1268 1269 sorted_targets = svn_sort__hash(canonical_targets, 1270 svn_sort_compare_items_as_paths, 1271 scratch_pool); 1272 1273 lb.fs = fs; 1274 lb.targets = sorted_targets; | 1106 ub.skip_check = TRUE; 1107 ub.result_pool = scratch_pool; 1108 1109 /* No ub.infos[].fs_err error because skip_check is TRUE. */ 1110 SVN_ERR(unlock_body(&ub, scratch_pool)); 1111 1112 return SVN_NO_ERROR; 1113} --- 45 unchanged lines hidden (view full) --- 1159 } 1160 1161 sorted_targets = svn_sort__hash(canonical_targets, 1162 svn_sort_compare_items_as_paths, 1163 scratch_pool); 1164 1165 lb.fs = fs; 1166 lb.targets = sorted_targets; |
1167 lb.infos = apr_array_make(result_pool, sorted_targets->nelts, 1168 sizeof(struct lock_info_t)); |
|
1275 lb.comment = comment; 1276 lb.is_dav_comment = is_dav_comment; 1277 lb.expiration_date = expiration_date; 1278 lb.steal_lock = steal_lock; 1279 lb.result_pool = result_pool; 1280 1281 iterpool = svn_pool_create(scratch_pool); 1282 err = svn_fs_x__with_write_lock(fs, lock_body, &lb, iterpool); --- 78 unchanged lines hidden (view full) --- 1361 } 1362 1363 sorted_targets = svn_sort__hash(canonical_targets, 1364 svn_sort_compare_items_as_paths, 1365 scratch_pool); 1366 1367 ub.fs = fs; 1368 ub.targets = sorted_targets; | 1169 lb.comment = comment; 1170 lb.is_dav_comment = is_dav_comment; 1171 lb.expiration_date = expiration_date; 1172 lb.steal_lock = steal_lock; 1173 lb.result_pool = result_pool; 1174 1175 iterpool = svn_pool_create(scratch_pool); 1176 err = svn_fs_x__with_write_lock(fs, lock_body, &lb, iterpool); --- 78 unchanged lines hidden (view full) --- 1255 } 1256 1257 sorted_targets = svn_sort__hash(canonical_targets, 1258 svn_sort_compare_items_as_paths, 1259 scratch_pool); 1260 1261 ub.fs = fs; 1262 ub.targets = sorted_targets; |
1263 ub.infos = apr_array_make(result_pool, sorted_targets->nelts, 1264 sizeof(struct unlock_info_t)); |
|
1369 ub.skip_check = FALSE; 1370 ub.break_lock = break_lock; 1371 ub.result_pool = result_pool; 1372 1373 iterpool = svn_pool_create(scratch_pool); 1374 err = svn_fs_x__with_write_lock(fs, unlock_body, &ub, iterpool); 1375 for (i = 0; i < ub.infos->nelts; ++i) 1376 { --- 116 unchanged lines hidden --- | 1265 ub.skip_check = FALSE; 1266 ub.break_lock = break_lock; 1267 ub.result_pool = result_pool; 1268 1269 iterpool = svn_pool_create(scratch_pool); 1270 err = svn_fs_x__with_write_lock(fs, unlock_body, &ub, iterpool); 1271 for (i = 0; i < ub.infos->nelts; ++i) 1272 { --- 116 unchanged lines hidden --- |