aboutsummaryrefslogtreecommitdiff
path: root/subversion/libsvn_fs_x/lock.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_fs_x/lock.c')
-rw-r--r--subversion/libsvn_fs_x/lock.c434
1 files changed, 165 insertions, 269 deletions
diff --git a/subversion/libsvn_fs_x/lock.c b/subversion/libsvn_fs_x/lock.c
index 6819f630ef1a..d1db492c0f37 100644
--- a/subversion/libsvn_fs_x/lock.c
+++ b/subversion/libsvn_fs_x/lock.c
@@ -113,7 +113,8 @@ hash_fetch(apr_hash_t *hash,
/* SVN_ERR_FS_CORRUPT: the lockfile for PATH in FS is corrupt. */
static svn_error_t *
-err_corrupt_lockfile(const char *fs_path, const char *path)
+err_corrupt_lockfile(const char *fs_path,
+ const char *path)
{
return
svn_error_createf(
@@ -234,7 +235,7 @@ write_digest_file(apr_hash_t *children,
if ((err = svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR,
scratch_pool)))
{
- svn_error_clear(svn_stream_close(stream));
+ err = svn_error_compose_create(err, svn_stream_close(stream));
return svn_error_createf(err->apr_err,
err,
_("Cannot write lock/entries hashfile '%s'"),
@@ -243,7 +244,7 @@ write_digest_file(apr_hash_t *children,
}
SVN_ERR(svn_stream_close(stream));
- SVN_ERR(svn_io_file_rename(tmp_path, digest_path, scratch_pool));
+ SVN_ERR(svn_io_file_rename2(tmp_path, digest_path, FALSE, scratch_pool));
SVN_ERR(svn_io_copy_perms(perms_reference, digest_path, scratch_pool));
return SVN_NO_ERROR;
}
@@ -286,7 +287,7 @@ read_digest_file(apr_hash_t **children_p,
hash = apr_hash_make(pool);
if ((err = svn_hash_read2(hash, stream, SVN_HASH_TERMINATOR, pool)))
{
- svn_error_clear(svn_stream_close(stream));
+ err = svn_error_compose_create(err, svn_stream_close(stream));
return svn_error_createf(err->apr_err,
err,
_("Can't parse lock/entries hashfile '%s'"),
@@ -470,6 +471,12 @@ unlock_single(svn_fs_t *fs,
svn_lock_t *lock,
apr_pool_t *pool);
+/* Check if LOCK has been already expired. */
+static svn_boolean_t lock_expired(const svn_lock_t *lock)
+{
+ return lock->expiration_date && (apr_time_now() > lock->expiration_date);
+}
+
/* Set *LOCK_P to the lock for PATH in FS. HAVE_WRITE_LOCK should be
TRUE if the caller (or one of its callers) has taken out the
repository-wide write lock, FALSE otherwise. If MUST_EXIST is
@@ -499,7 +506,7 @@ get_lock(svn_lock_t **lock_p,
return must_exist ? SVN_FS__ERR_NO_SUCH_LOCK(fs, path) : SVN_NO_ERROR;
/* Don't return an expired lock. */
- if (lock->expiration_date && (apr_time_now() > lock->expiration_date))
+ if (lock_expired(lock))
{
/* Only remove the lock if we have the write lock.
Read operations shouldn't change the filesystem. */
@@ -546,68 +553,17 @@ get_lock_helper(svn_fs_t *fs,
}
-/* Baton for locks_walker(). */
-typedef struct walk_locks_baton_t
-{
- svn_fs_get_locks_callback_t get_locks_func;
- void *get_locks_baton;
- svn_fs_t *fs;
-} walk_locks_baton_t;
-
-/* Implements walk_digests_callback_t. */
-static svn_error_t *
-locks_walker(void *baton,
- const char *fs_path,
- const char *digest_path,
- svn_lock_t *lock,
- svn_boolean_t have_write_lock,
- apr_pool_t *pool)
-{
- walk_locks_baton_t *wlb = baton;
-
- if (lock)
- {
- /* Don't report an expired lock. */
- if (lock->expiration_date == 0
- || (apr_time_now() <= lock->expiration_date))
- {
- if (wlb->get_locks_func)
- SVN_ERR(wlb->get_locks_func(wlb->get_locks_baton, lock, pool));
- }
- else
- {
- /* Only remove the lock if we have the write lock.
- Read operations shouldn't change the filesystem. */
- if (have_write_lock)
- SVN_ERR(unlock_single(wlb->fs, lock, pool));
- }
- }
-
- return SVN_NO_ERROR;
-}
-
-/* Callback type for walk_digest_files().
- *
- * LOCK come from a read_digest_file(digest_path) call.
- */
-typedef svn_error_t *(*walk_digests_callback_t)(void *baton,
- const char *fs_path,
- const char *digest_path,
- svn_lock_t *lock,
- svn_boolean_t have_write_lock,
- apr_pool_t *pool);
-
-/* A function that calls WALK_DIGESTS_FUNC/WALK_DIGESTS_BATON for
- all lock digest files in and under PATH in FS.
+/* A function that calls GET_LOCKS_FUNC/GET_LOCKS_BATON for
+ all locks in and under PATH in FS.
HAVE_WRITE_LOCK should be true if the caller (directly or indirectly)
has the FS write lock. */
static svn_error_t *
-walk_digest_files(const char *fs_path,
- const char *digest_path,
- walk_digests_callback_t walk_digests_func,
- void *walk_digests_baton,
- svn_boolean_t have_write_lock,
- apr_pool_t *pool)
+walk_locks(svn_fs_t *fs,
+ const char *digest_path,
+ svn_fs_get_locks_callback_t get_locks_func,
+ void *get_locks_baton,
+ svn_boolean_t have_write_lock,
+ apr_pool_t *pool)
{
apr_hash_index_t *hi;
apr_hash_t *children;
@@ -615,10 +571,19 @@ walk_digest_files(const char *fs_path,
svn_lock_t *lock;
/* First, send up any locks in the current digest file. */
- SVN_ERR(read_digest_file(&children, &lock, fs_path, digest_path, pool));
+ SVN_ERR(read_digest_file(&children, &lock, fs->path, digest_path, pool));
- SVN_ERR(walk_digests_func(walk_digests_baton, fs_path, digest_path, lock,
- have_write_lock, pool));
+ if (lock && lock_expired(lock))
+ {
+ /* Only remove the lock if we have the write lock.
+ Read operations shouldn't change the filesystem. */
+ if (have_write_lock)
+ SVN_ERR(unlock_single(fs, lock, pool));
+ }
+ else if (lock)
+ {
+ SVN_ERR(get_locks_func(get_locks_baton, lock, pool));
+ }
/* Now, report all the child entries (if any; bail otherwise). */
if (! apr_hash_count(children))
@@ -630,39 +595,25 @@ walk_digest_files(const char *fs_path,
svn_pool_clear(subpool);
SVN_ERR(read_digest_file
- (NULL, &lock, fs_path,
- digest_path_from_digest(fs_path, digest, subpool), subpool));
+ (NULL, &lock, fs->path,
+ digest_path_from_digest(fs->path, digest, subpool), subpool));
- SVN_ERR(walk_digests_func(walk_digests_baton, fs_path, digest_path, lock,
- have_write_lock, subpool));
+ if (lock && lock_expired(lock))
+ {
+ /* Only remove the lock if we have the write lock.
+ Read operations shouldn't change the filesystem. */
+ if (have_write_lock)
+ SVN_ERR(unlock_single(fs, lock, pool));
+ }
+ else if (lock)
+ {
+ SVN_ERR(get_locks_func(get_locks_baton, lock, pool));
+ }
}
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
-/* A function that calls GET_LOCKS_FUNC/GET_LOCKS_BATON for
- all locks in and under PATH in FS.
- HAVE_WRITE_LOCK should be true if the caller (directly or indirectly)
- has the FS write lock. */
-static svn_error_t *
-walk_locks(svn_fs_t *fs,
- const char *digest_path,
- svn_fs_get_locks_callback_t get_locks_func,
- void *get_locks_baton,
- svn_boolean_t have_write_lock,
- apr_pool_t *pool)
-{
- walk_locks_baton_t wlb;
-
- wlb.get_locks_func = get_locks_func;
- wlb.get_locks_baton = get_locks_baton;
- wlb.fs = fs;
- SVN_ERR(walk_digest_files(fs->path, digest_path, locks_walker, &wlb,
- have_write_lock, pool));
- return SVN_NO_ERROR;
-}
-
-
/* Utility function: verify that a lock can be used. Interesting
errors returned from this function:
@@ -737,6 +688,35 @@ svn_fs_x__allow_locked_operation(const char *path,
return SVN_NO_ERROR;
}
+/* Helper function called from the lock and unlock code.
+ UPDATES is a map from "const char *" parent paths to "apr_array_header_t *"
+ arrays of child paths. For all of the parent paths of PATH this function
+ adds PATH to the corresponding array of child paths. */
+static void
+schedule_index_update(apr_hash_t *updates,
+ const char *path,
+ apr_pool_t *scratch_pool)
+{
+ apr_pool_t *hashpool = apr_hash_pool_get(updates);
+ const char *parent_path = path;
+
+ while (! svn_fspath__is_root(parent_path, strlen(parent_path)))
+ {
+ apr_array_header_t *children;
+
+ parent_path = svn_fspath__dirname(parent_path, scratch_pool);
+ children = svn_hash_gets(updates, parent_path);
+
+ if (! children)
+ {
+ children = apr_array_make(hashpool, 8, sizeof(const char *));
+ svn_hash_sets(updates, apr_pstrdup(hashpool, parent_path), children);
+ }
+
+ APR_ARRAY_PUSH(children, const char *) = path;
+ }
+}
+
/* The effective arguments for lock_body() below. */
typedef struct lock_baton_t {
svn_fs_t *fs;
@@ -859,7 +839,6 @@ check_lock(svn_error_t **fs_err,
typedef struct lock_info_t {
const char *path;
- const char *component;
svn_lock_t *lock;
svn_error_t *fs_err;
} lock_info_t;
@@ -876,20 +855,20 @@ typedef struct lock_info_t {
type, and assumes that the write lock is held.
*/
static svn_error_t *
-lock_body(void *baton, apr_pool_t *pool)
+lock_body(void *baton,
+ apr_pool_t *pool)
{
lock_baton_t *lb = baton;
svn_fs_root_t *root;
svn_revnum_t youngest;
const char *rev_0_path;
- int i, outstanding = 0;
+ int i;
+ apr_hash_t *index_updates = apr_hash_make(pool);
+ apr_hash_index_t *hi;
apr_pool_t *iterpool = svn_pool_create(pool);
- lb->infos = apr_array_make(lb->result_pool, lb->targets->nelts,
- sizeof(lock_info_t));
-
/* Until we implement directory locks someday, we only allow locks
- on files or non-existent paths. */
+ on files. */
/* Use fs->vtable->foo instead of svn_fs_foo to avoid circular
library dependencies, which are not portable. */
SVN_ERR(lb->fs->vtable->youngest_rev(&youngest, lb->fs, pool));
@@ -899,35 +878,28 @@ lock_body(void *baton, apr_pool_t *pool)
{
const svn_sort__item_t *item = &APR_ARRAY_IDX(lb->targets, i,
svn_sort__item_t);
- const svn_fs_lock_target_t *target = item->value;
lock_info_t info;
svn_pool_clear(iterpool);
info.path = item->key;
- SVN_ERR(check_lock(&info.fs_err, info.path, target, lb, root,
- youngest, iterpool));
info.lock = NULL;
- info.component = NULL;
- APR_ARRAY_PUSH(lb->infos, lock_info_t) = info;
+ info.fs_err = SVN_NO_ERROR;
+
+ SVN_ERR(check_lock(&info.fs_err, info.path, item->value, lb, root,
+ youngest, iterpool));
+
+ /* If no error occurred while pre-checking, schedule the index updates for
+ this path. */
if (!info.fs_err)
- ++outstanding;
+ schedule_index_update(index_updates, info.path, iterpool);
+
+ APR_ARRAY_PUSH(lb->infos, lock_info_t) = info;
}
rev_0_path = svn_fs_x__path_rev_absolute(lb->fs, 0, pool);
- /* Given the paths:
-
- /foo/bar/f
- /foo/bar/g
- /zig/x
-
- we loop through repeatedly. The first pass sees '/' on all paths
- and writes the '/' index. The second pass sees '/foo' twice and
- writes that index followed by '/zig' and that index. The third
- pass sees '/foo/bar' twice and writes that index, and then writes
- the lock for '/zig/x'. The fourth pass writes the locks for
- '/foo/bar/f' and '/foo/bar/g'.
+ /* We apply the scheduled index updates before writing the actual locks.
Writing indices before locks is correct: if interrupted it leaves
indices without locks rather than locks without indices. An
@@ -936,91 +908,50 @@ lock_body(void *baton, apr_pool_t *pool)
index is inconsistent, svn_fs_x__allow_locked_operation will
show locked on the file but unlocked on the parent. */
+ for (hi = apr_hash_first(pool, index_updates); hi; hi = apr_hash_next(hi))
+ {
+ const char *path = apr_hash_this_key(hi);
+ apr_array_header_t *children = apr_hash_this_val(hi);
- while (outstanding)
+ svn_pool_clear(iterpool);
+ SVN_ERR(add_to_digest(lb->fs->path, children, path, rev_0_path,
+ iterpool));
+ }
+
+ for (i = 0; i < lb->infos->nelts; ++i)
{
- const char *last_path = NULL;
- apr_array_header_t *paths;
+ struct lock_info_t *info = &APR_ARRAY_IDX(lb->infos, i,
+ struct lock_info_t);
+ svn_sort__item_t *item = &APR_ARRAY_IDX(lb->targets, i, svn_sort__item_t);
+ svn_fs_lock_target_t *target = item->value;
svn_pool_clear(iterpool);
- paths = apr_array_make(iterpool, 1, sizeof(const char *));
- for (i = 0; i < lb->infos->nelts; ++i)
+ if (! info->fs_err)
{
- lock_info_t *info = &APR_ARRAY_IDX(lb->infos, i, lock_info_t);
- const svn_sort__item_t *item = &APR_ARRAY_IDX(lb->targets, i,
- svn_sort__item_t);
- const svn_fs_lock_target_t *target = item->value;
-
- if (!info->fs_err && !info->lock)
- {
- if (!info->component)
- {
- info->component = info->path;
- APR_ARRAY_PUSH(paths, const char *) = info->path;
- last_path = "/";
- }
- else
- {
- info->component = strchr(info->component + 1, '/');
- if (!info->component)
- {
- /* The component is a path to lock, this cannot
- match a previous path that need to be indexed. */
- if (paths->nelts)
- {
- SVN_ERR(add_to_digest(lb->fs->path, paths, last_path,
- rev_0_path, iterpool));
- apr_array_clear(paths);
- last_path = NULL;
- }
-
- info->lock = svn_lock_create(lb->result_pool);
- if (target->token)
- info->lock->token = target->token;
- else
- SVN_ERR(svn_fs_x__generate_lock_token(
- &(info->lock->token), lb->fs,
- lb->result_pool));
- info->lock->path = info->path;
- info->lock->owner = lb->fs->access_ctx->username;
- info->lock->comment = lb->comment;
- info->lock->is_dav_comment = lb->is_dav_comment;
- info->lock->creation_date = apr_time_now();
- info->lock->expiration_date = lb->expiration_date;
-
- info->fs_err = set_lock(lb->fs->path, info->lock,
- rev_0_path, iterpool);
- --outstanding;
- }
- else
- {
- /* The component is a path to an index. */
- apr_size_t len = info->component - info->path;
-
- if (last_path
- && (strncmp(last_path, info->path, len)
- || strlen(last_path) != len))
- {
- /* No match to the previous paths to index. */
- SVN_ERR(add_to_digest(lb->fs->path, paths, last_path,
- rev_0_path, iterpool));
- apr_array_clear(paths);
- last_path = NULL;
- }
- APR_ARRAY_PUSH(paths, const char *) = info->path;
- if (!last_path)
- last_path = apr_pstrndup(iterpool, info->path, len);
- }
- }
- }
-
- if (last_path && i == lb->infos->nelts - 1)
- SVN_ERR(add_to_digest(lb->fs->path, paths, last_path,
- rev_0_path, iterpool));
+ info->lock = svn_lock_create(lb->result_pool);
+ if (target->token)
+ info->lock->token = apr_pstrdup(lb->result_pool, target->token);
+ else
+ SVN_ERR(svn_fs_x__generate_lock_token(&(info->lock->token), lb->fs,
+ lb->result_pool));
+
+ /* The INFO->PATH is already allocated in LB->RESULT_POOL as a result
+ of svn_fspath__canonicalize() (see svn_fs_x__lock()). */
+ info->lock->path = info->path;
+ info->lock->owner = apr_pstrdup(lb->result_pool,
+ lb->fs->access_ctx->username);
+ info->lock->comment = apr_pstrdup(lb->result_pool, lb->comment);
+ info->lock->is_dav_comment = lb->is_dav_comment;
+ info->lock->creation_date = apr_time_now();
+ info->lock->expiration_date = lb->expiration_date;
+
+ info->fs_err = set_lock(lb->fs->path, info->lock, rev_0_path,
+ iterpool);
}
}
+ svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
@@ -1061,10 +992,8 @@ check_unlock(svn_error_t **fs_err,
typedef struct unlock_info_t {
const char *path;
- const char *component;
svn_error_t *fs_err;
svn_boolean_t done;
- int components;
} unlock_info_t;
/* The body of svn_fs_x__unlock(), which see.
@@ -1079,18 +1008,18 @@ typedef struct unlock_info_t {
type, and assumes that the write lock is held.
*/
static svn_error_t *
-unlock_body(void *baton, apr_pool_t *pool)
+unlock_body(void *baton,
+ apr_pool_t *pool)
{
unlock_baton_t *ub = baton;
svn_fs_root_t *root;
svn_revnum_t youngest;
const char *rev_0_path;
- int i, max_components = 0, outstanding = 0;
+ int i;
+ apr_hash_t *indices_updates = apr_hash_make(pool);
+ apr_hash_index_t *hi;
apr_pool_t *iterpool = svn_pool_create(pool);
- ub->infos = apr_array_make(ub->result_pool, ub->targets->nelts,
- sizeof( unlock_info_t));
-
SVN_ERR(ub->fs->vtable->youngest_rev(&youngest, ub->fs, pool));
SVN_ERR(ub->fs->vtable->revision_root(&root, ub->fs, youngest, pool));
@@ -1099,95 +1028,56 @@ unlock_body(void *baton, apr_pool_t *pool)
const svn_sort__item_t *item = &APR_ARRAY_IDX(ub->targets, i,
svn_sort__item_t);
const char *token = item->value;
- unlock_info_t info = { 0 };
+ unlock_info_t info;
svn_pool_clear(iterpool);
info.path = item->key;
+ info.fs_err = SVN_NO_ERROR;
+ info.done = FALSE;
+
if (!ub->skip_check)
SVN_ERR(check_unlock(&info.fs_err, info.path, token, ub, root,
iterpool));
- if (!info.fs_err)
- {
- const char *s;
- info.components = 1;
- info.component = info.path;
- while((s = strchr(info.component + 1, '/')))
- {
- info.component = s;
- ++info.components;
- }
-
- if (info.components > max_components)
- max_components = info.components;
+ /* If no error occurred while pre-checking, schedule the index updates for
+ this path. */
+ if (!info.fs_err)
+ schedule_index_update(indices_updates, info.path, iterpool);
- ++outstanding;
- }
APR_ARRAY_PUSH(ub->infos, unlock_info_t) = info;
}
rev_0_path = svn_fs_x__path_rev_absolute(ub->fs, 0, pool);
- for (i = max_components; i >= 0; --i)
+ /* Unlike the lock_body(), we need to delete locks *before* we start to
+ update indices. */
+
+ for (i = 0; i < ub->infos->nelts; ++i)
{
- const char *last_path = NULL;
- apr_array_header_t *paths;
- int j;
+ struct unlock_info_t *info = &APR_ARRAY_IDX(ub->infos, i,
+ struct unlock_info_t);
svn_pool_clear(iterpool);
- paths = apr_array_make(pool, 1, sizeof(const char *));
- for (j = 0; j < ub->infos->nelts; ++j)
+ if (! info->fs_err)
{
- unlock_info_t *info = &APR_ARRAY_IDX(ub->infos, j, unlock_info_t);
-
- if (!info->fs_err && info->path)
- {
-
- if (info->components == i)
- {
- SVN_ERR(delete_lock(ub->fs->path, info->path, iterpool));
- info->done = TRUE;
- }
- else if (info->components > i)
- {
- apr_size_t len = info->component - info->path;
-
- if (last_path
- && strcmp(last_path, "/")
- && (strncmp(last_path, info->path, len)
- || strlen(last_path) != len))
- {
- SVN_ERR(delete_from_digest(ub->fs->path, paths, last_path,
- rev_0_path, iterpool));
- apr_array_clear(paths);
- last_path = NULL;
- }
- APR_ARRAY_PUSH(paths, const char *) = info->path;
- if (!last_path)
- {
- if (info->component > info->path)
- last_path = apr_pstrndup(pool, info->path, len);
- else
- last_path = "/";
- }
-
- if (info->component > info->path)
- {
- --info->component;
- while(info->component[0] != '/')
- --info->component;
- }
- }
- }
-
- if (last_path && j == ub->infos->nelts - 1)
- SVN_ERR(delete_from_digest(ub->fs->path, paths, last_path,
- rev_0_path, iterpool));
+ SVN_ERR(delete_lock(ub->fs->path, info->path, iterpool));
+ info->done = TRUE;
}
}
+ for (hi = apr_hash_first(pool, indices_updates); hi; hi = apr_hash_next(hi))
+ {
+ const char *path = apr_hash_this_key(hi);
+ apr_array_header_t *children = apr_hash_this_val(hi);
+
+ svn_pool_clear(iterpool);
+ SVN_ERR(delete_from_digest(ub->fs->path, children, path, rev_0_path,
+ iterpool));
+ }
+
+ svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
@@ -1211,6 +1101,8 @@ unlock_single(svn_fs_t *fs,
ub.fs = fs;
ub.targets = targets;
+ ub.infos = apr_array_make(scratch_pool, targets->nelts,
+ sizeof(struct unlock_info_t));
ub.skip_check = TRUE;
ub.result_pool = scratch_pool;
@@ -1272,6 +1164,8 @@ svn_fs_x__lock(svn_fs_t *fs,
lb.fs = fs;
lb.targets = sorted_targets;
+ lb.infos = apr_array_make(result_pool, sorted_targets->nelts,
+ sizeof(struct lock_info_t));
lb.comment = comment;
lb.is_dav_comment = is_dav_comment;
lb.expiration_date = expiration_date;
@@ -1366,6 +1260,8 @@ svn_fs_x__unlock(svn_fs_t *fs,
ub.fs = fs;
ub.targets = sorted_targets;
+ ub.infos = apr_array_make(result_pool, sorted_targets->nelts,
+ sizeof(struct unlock_info_t));
ub.skip_check = FALSE;
ub.break_lock = break_lock;
ub.result_pool = result_pool;