Deleted Added
full compact
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 ---