summaryrefslogtreecommitdiff
path: root/subversion/libsvn_repos/fs-wrap.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_repos/fs-wrap.c')
-rw-r--r--subversion/libsvn_repos/fs-wrap.c369
1 files changed, 296 insertions, 73 deletions
diff --git a/subversion/libsvn_repos/fs-wrap.c b/subversion/libsvn_repos/fs-wrap.c
index 006b286..b46cda6 100644
--- a/subversion/libsvn_repos/fs-wrap.c
+++ b/subversion/libsvn_repos/fs-wrap.c
@@ -36,6 +36,7 @@
#include "repos.h"
#include "svn_private_config.h"
#include "private/svn_repos_private.h"
+#include "private/svn_sorts_private.h"
#include "private/svn_utf_private.h"
#include "private/svn_fspath.h"
@@ -66,13 +67,14 @@ svn_repos_fs_commit_txn(const char **conflict_p,
SVN_ERR(svn_fs_txn_name(&txn_name, txn, pool));
SVN_ERR(svn_repos__hooks_pre_commit(repos, hooks_env, txn_name, pool));
- /* Remove any ephemeral transaction properties. */
+ /* Remove any ephemeral transaction properties. If the commit fails
+ we will attempt to restore the properties but if that fails, or
+ the process is killed, the properties will be lost. */
SVN_ERR(svn_fs_txn_proplist(&props, txn, pool));
iterpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
{
- const void *key;
- apr_hash_this(hi, &key, NULL, NULL);
+ const char *key = apr_hash_this_key(hi);
svn_pool_clear(iterpool);
@@ -87,7 +89,24 @@ svn_repos_fs_commit_txn(const char **conflict_p,
/* Commit. */
err = svn_fs_commit_txn(conflict_p, new_rev, txn, pool);
if (! SVN_IS_VALID_REVNUM(*new_rev))
- return err;
+ {
+ /* The commit failed, try to restore the ephemeral properties. */
+ iterpool = svn_pool_create(pool);
+ for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
+ {
+ const char *key = apr_hash_this_key(hi);
+ svn_string_t *val = apr_hash_this_val(hi);
+
+ svn_pool_clear(iterpool);
+
+ if (strncmp(key, SVN_PROP_TXN_PREFIX,
+ (sizeof(SVN_PROP_TXN_PREFIX) - 1)) == 0)
+ svn_error_clear(svn_fs_change_txn_prop(txn, key, val, iterpool));
+ }
+ svn_pool_destroy(iterpool);
+
+ return err;
+ }
/* Run post-commit hooks. */
if ((err2 = svn_repos__hooks_post_commit(repos, hooks_env,
@@ -148,7 +167,7 @@ svn_repos_fs_begin_txn_for_commit2(svn_fs_txn_t **txn_p,
if (err)
return svn_error_compose_create(err, svn_fs_abort_txn(txn, pool));
- /* We have API promise that *TXN_P is unaffected on faulure. */
+ /* We have API promise that *TXN_P is unaffected on failure. */
*txn_p = txn;
return SVN_NO_ERROR;
}
@@ -488,115 +507,319 @@ svn_repos_fs_revision_proplist(apr_hash_t **table_p,
return SVN_NO_ERROR;
}
+struct lock_many_baton_t {
+ svn_boolean_t need_lock;
+ apr_array_header_t *paths;
+ svn_fs_lock_callback_t lock_callback;
+ void *lock_baton;
+ svn_error_t *cb_err;
+ apr_pool_t *pool;
+};
+
+/* Implements svn_fs_lock_callback_t. Used by svn_repos_fs_lock_many
+ and svn_repos_fs_unlock_many to record the paths for use by post-
+ hooks, forward to the supplied callback and record any callback
+ error. */
+static svn_error_t *
+lock_many_cb(void *lock_baton,
+ const char *path,
+ const svn_lock_t *lock,
+ svn_error_t *fs_err,
+ apr_pool_t *pool)
+{
+ struct lock_many_baton_t *b = lock_baton;
+
+ if (!b->cb_err && b->lock_callback)
+ b->cb_err = b->lock_callback(b->lock_baton, path, lock, fs_err, pool);
+
+ if ((b->need_lock && lock) || (!b->need_lock && !fs_err))
+ APR_ARRAY_PUSH(b->paths, const char *) = apr_pstrdup(b->pool, path);
+
+ return SVN_NO_ERROR;
+}
+
svn_error_t *
-svn_repos_fs_lock(svn_lock_t **lock,
- svn_repos_t *repos,
- const char *path,
- const char *token,
- const char *comment,
- svn_boolean_t is_dav_comment,
- apr_time_t expiration_date,
- svn_revnum_t current_rev,
- svn_boolean_t steal_lock,
- apr_pool_t *pool)
+svn_repos_fs_lock_many(svn_repos_t *repos,
+ apr_hash_t *targets,
+ const char *comment,
+ svn_boolean_t is_dav_comment,
+ apr_time_t expiration_date,
+ svn_boolean_t steal_lock,
+ svn_fs_lock_callback_t lock_callback,
+ void *lock_baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- svn_error_t *err;
+ svn_error_t *err, *cb_err = SVN_NO_ERROR;
svn_fs_access_t *access_ctx = NULL;
const char *username = NULL;
- const char *new_token;
- apr_array_header_t *paths;
apr_hash_t *hooks_env;
+ apr_hash_t *pre_targets = apr_hash_make(scratch_pool);
+ apr_hash_index_t *hi;
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ struct lock_many_baton_t baton;
+
+ if (!apr_hash_count(targets))
+ return SVN_NO_ERROR;
/* Parse the hooks-env file (if any). */
SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path,
- pool, pool));
-
- /* Setup an array of paths in anticipation of the ra layers handling
- multiple locks in one request (1.3 most likely). This is only
- used by svn_repos__hooks_post_lock. */
- paths = apr_array_make(pool, 1, sizeof(const char *));
- APR_ARRAY_PUSH(paths, const char *) = path;
+ scratch_pool, scratch_pool));
SVN_ERR(svn_fs_get_access(&access_ctx, repos->fs));
if (access_ctx)
SVN_ERR(svn_fs_access_get_username(&username, access_ctx));
if (! username)
- return svn_error_createf
+ return svn_error_create
(SVN_ERR_FS_NO_USER, NULL,
- "Cannot lock path '%s', no authenticated username available.", path);
+ "Cannot lock path, no authenticated username available.");
/* Run pre-lock hook. This could throw error, preventing
- svn_fs_lock() from happening. */
- SVN_ERR(svn_repos__hooks_pre_lock(repos, hooks_env, &new_token, path,
- username, comment, steal_lock, pool));
- if (*new_token)
- token = new_token;
-
- /* Lock. */
- SVN_ERR(svn_fs_lock(lock, repos->fs, path, token, comment, is_dav_comment,
- expiration_date, current_rev, steal_lock, pool));
-
- /* Run post-lock hook. */
- if ((err = svn_repos__hooks_post_lock(repos, hooks_env,
- paths, username, pool)))
- return svn_error_create
- (SVN_ERR_REPOS_POST_LOCK_HOOK_FAILED, err,
- "Lock succeeded, but post-lock hook failed");
+ svn_fs_lock2() from happening for that path. */
+ for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi))
+ {
+ const char *new_token;
+ svn_fs_lock_target_t *target;
+ const char *path = apr_hash_this_key(hi);
- return SVN_NO_ERROR;
+ svn_pool_clear(iterpool);
+
+ err = svn_repos__hooks_pre_lock(repos, hooks_env, &new_token, path,
+ username, comment, steal_lock, iterpool);
+ if (err)
+ {
+ if (!cb_err && lock_callback)
+ cb_err = lock_callback(lock_baton, path, NULL, err, iterpool);
+ svn_error_clear(err);
+
+ continue;
+ }
+
+ target = apr_hash_this_val(hi);
+ if (*new_token)
+ svn_fs_lock_target_set_token(target, new_token);
+ svn_hash_sets(pre_targets, path, target);
+ }
+
+ if (!apr_hash_count(pre_targets))
+ return svn_error_trace(cb_err);
+
+ baton.need_lock = TRUE;
+ baton.paths = apr_array_make(scratch_pool, apr_hash_count(pre_targets),
+ sizeof(const char *));
+ baton.lock_callback = lock_callback;
+ baton.lock_baton = lock_baton;
+ baton.cb_err = cb_err;
+ baton.pool = scratch_pool;
+
+ err = svn_fs_lock_many(repos->fs, pre_targets, comment,
+ is_dav_comment, expiration_date, steal_lock,
+ lock_many_cb, &baton, result_pool, iterpool);
+
+ /* If there are locks run the post-lock even if there is an error. */
+ if (baton.paths->nelts)
+ {
+ svn_error_t *perr = svn_repos__hooks_post_lock(repos, hooks_env,
+ baton.paths, username,
+ iterpool);
+ if (perr)
+ {
+ perr = svn_error_create(SVN_ERR_REPOS_POST_LOCK_HOOK_FAILED, perr,
+ _("Locking succeeded, but post-lock hook failed"));
+ err = svn_error_compose_create(err, perr);
+ }
+ }
+
+ svn_pool_destroy(iterpool);
+
+ if (err && cb_err)
+ svn_error_compose(err, cb_err);
+ else if (!err)
+ err = cb_err;
+
+ return svn_error_trace(err);
}
+struct lock_baton_t {
+ const svn_lock_t *lock;
+ svn_error_t *fs_err;
+};
+
+/* Implements svn_fs_lock_callback_t. Used by svn_repos_fs_lock and
+ svn_repos_fs_unlock to record the lock and error from
+ svn_repos_fs_lock_many and svn_repos_fs_unlock_many. */
+static svn_error_t *
+lock_cb(void *lock_baton,
+ const char *path,
+ const svn_lock_t *lock,
+ svn_error_t *fs_err,
+ apr_pool_t *pool)
+{
+ struct lock_baton_t *b = lock_baton;
+
+ b->lock = lock;
+ b->fs_err = svn_error_dup(fs_err);
+
+ return SVN_NO_ERROR;
+}
svn_error_t *
-svn_repos_fs_unlock(svn_repos_t *repos,
- const char *path,
- const char *token,
- svn_boolean_t break_lock,
- apr_pool_t *pool)
+svn_repos_fs_lock(svn_lock_t **lock,
+ svn_repos_t *repos,
+ const char *path,
+ const char *token,
+ const char *comment,
+ svn_boolean_t is_dav_comment,
+ apr_time_t expiration_date,
+ svn_revnum_t current_rev,
+ svn_boolean_t steal_lock,
+ apr_pool_t *pool)
{
+ apr_hash_t *targets = apr_hash_make(pool);
+ svn_fs_lock_target_t *target = svn_fs_lock_target_create(token, current_rev,
+ pool);
svn_error_t *err;
+ struct lock_baton_t baton = {0};
+
+ svn_hash_sets(targets, path, target);
+
+ err = svn_repos_fs_lock_many(repos, targets, comment, is_dav_comment,
+ expiration_date, steal_lock, lock_cb, &baton,
+ pool, pool);
+
+ if (baton.lock)
+ *lock = (svn_lock_t*)baton.lock;
+
+ if (err && baton.fs_err)
+ svn_error_compose(err, baton.fs_err);
+ else if (!err)
+ err = baton.fs_err;
+
+ return svn_error_trace(err);
+}
+
+
+svn_error_t *
+svn_repos_fs_unlock_many(svn_repos_t *repos,
+ apr_hash_t *targets,
+ svn_boolean_t break_lock,
+ svn_fs_lock_callback_t lock_callback,
+ void *lock_baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_error_t *err, *cb_err = SVN_NO_ERROR;
svn_fs_access_t *access_ctx = NULL;
const char *username = NULL;
- apr_array_header_t *paths;
apr_hash_t *hooks_env;
+ apr_hash_t *pre_targets = apr_hash_make(scratch_pool);
+ apr_hash_index_t *hi;
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ struct lock_many_baton_t baton;
+
+ if (!apr_hash_count(targets))
+ return SVN_NO_ERROR;
/* Parse the hooks-env file (if any). */
SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path,
- pool, pool));
-
- /* Setup an array of paths in anticipation of the ra layers handling
- multiple locks in one request (1.3 most likely). This is only
- used by svn_repos__hooks_post_lock. */
- paths = apr_array_make(pool, 1, sizeof(const char *));
- APR_ARRAY_PUSH(paths, const char *) = path;
+ scratch_pool, scratch_pool));
SVN_ERR(svn_fs_get_access(&access_ctx, repos->fs));
if (access_ctx)
SVN_ERR(svn_fs_access_get_username(&username, access_ctx));
if (! break_lock && ! username)
- return svn_error_createf
+ return svn_error_create
(SVN_ERR_FS_NO_USER, NULL,
- _("Cannot unlock path '%s', no authenticated username available"),
- path);
+ _("Cannot unlock, no authenticated username available"));
/* Run pre-unlock hook. This could throw error, preventing
- svn_fs_unlock() from happening. */
- SVN_ERR(svn_repos__hooks_pre_unlock(repos, hooks_env, path, username, token,
- break_lock, pool));
+ svn_fs_unlock_many() from happening for that path. */
+ for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi))
+ {
+ const char *path = apr_hash_this_key(hi);
+ const char *token = apr_hash_this_val(hi);
- /* Unlock. */
- SVN_ERR(svn_fs_unlock(repos->fs, path, token, break_lock, pool));
+ svn_pool_clear(iterpool);
- /* Run post-unlock hook. */
- if ((err = svn_repos__hooks_post_unlock(repos, hooks_env, paths,
- username, pool)))
- return svn_error_create
- (SVN_ERR_REPOS_POST_UNLOCK_HOOK_FAILED, err,
- _("Unlock succeeded, but post-unlock hook failed"));
+ err = svn_repos__hooks_pre_unlock(repos, hooks_env, path, username, token,
+ break_lock, iterpool);
+ if (err)
+ {
+ if (!cb_err && lock_callback)
+ cb_err = lock_callback(lock_baton, path, NULL, err, iterpool);
+ svn_error_clear(err);
- return SVN_NO_ERROR;
+ continue;
+ }
+
+ svn_hash_sets(pre_targets, path, token);
+ }
+
+ if (!apr_hash_count(pre_targets))
+ return svn_error_trace(cb_err);
+
+ baton.need_lock = FALSE;
+ baton.paths = apr_array_make(scratch_pool, apr_hash_count(pre_targets),
+ sizeof(const char *));
+ baton.lock_callback = lock_callback;
+ baton.lock_baton = lock_baton;
+ baton.cb_err = cb_err;
+ baton.pool = scratch_pool;
+
+ err = svn_fs_unlock_many(repos->fs, pre_targets, break_lock,
+ lock_many_cb, &baton, result_pool, iterpool);
+
+ /* If there are 'unlocks' run the post-unlock even if there is an error. */
+ if (baton.paths->nelts)
+ {
+ svn_error_t *perr = svn_repos__hooks_post_unlock(repos, hooks_env,
+ baton.paths,
+ username, iterpool);
+ if (perr)
+ {
+ perr = svn_error_create(SVN_ERR_REPOS_POST_UNLOCK_HOOK_FAILED, perr,
+ _("Unlock succeeded, but post-unlock hook failed"));
+ err = svn_error_compose_create(err, perr);
+ }
+ }
+
+ svn_pool_destroy(iterpool);
+
+ if (err && cb_err)
+ svn_error_compose(err, cb_err);
+ else if (!err)
+ err = cb_err;
+
+ return svn_error_trace(err);
+}
+
+svn_error_t *
+svn_repos_fs_unlock(svn_repos_t *repos,
+ const char *path,
+ const char *token,
+ svn_boolean_t break_lock,
+ apr_pool_t *pool)
+{
+ apr_hash_t *targets = apr_hash_make(pool);
+ svn_error_t *err;
+ struct lock_baton_t baton = {0};
+
+ if (!token)
+ token = "";
+
+ svn_hash_sets(targets, path, token);
+
+ err = svn_repos_fs_unlock_many(repos, targets, break_lock, lock_cb, &baton,
+ pool, pool);
+
+ if (err && baton.fs_err)
+ svn_error_compose(err, baton.fs_err);
+ else if (!err)
+ err = baton.fs_err;
+
+ return svn_error_trace(err);
}
@@ -841,7 +1064,7 @@ svn_repos_fs_get_inherited_props(apr_array_header_t **inherited_props_p,
apr_pstrdup(result_pool, parent_path + 1);
i_props->prop_hash = parent_properties;
/* Build the output array in depth-first order. */
- svn_sort__array_insert(&i_props, inherited_props, 0);
+ svn_sort__array_insert(inherited_props, &i_props, 0);
}
}
}