You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ph...@apache.org on 2014/02/25 21:18:18 UTC
svn commit: r1571812 [1/2] - in
/subversion/branches/fsfs-lock-many/subversion: include/ libsvn_fs/
libsvn_fs_base/ libsvn_fs_fs/ libsvn_repos/ tests/libsvn_fs/ tests/libsvn_ra/
Author: philip
Date: Tue Feb 25 20:18:18 2014
New Revision: 1571812
URL: http://svn.apache.org/r1571812
Log:
On fsfs-lock-many branch: implement the multiple path functions
svn_fs_lock2 and svn_fs_unlock2 for FSFS along with the the
corresponding repos wrappers. All the locking now goes via the
multiple path functions but only passing a single path at a time.
Add some low-level regression tests to exercise the code with
multiple paths. This also fixes issue 4472, digest/lock written
in wrong order.
Notes:
- BDB just loops calling the single path function at a low-level.
- FSX is currently non-working but will eventually be a duplicate
of FSFS.
* subversion/include/svn_fs.h
(svn_fs_lock_target_t,
svn_fs_lock_result_t,
svn_fs_lock2,
svn_fs_unlock2): New.
* subversion/include/svn_repos.h
(svn_repos_fs_lock2,
svn_repos_fs_unlock2): New.
* subversion/libsvn_fs/fs-loader.c
(svn_fs_lock2): New.
(svn_fs_lock): Call svn_fs_lock2.
(svn_fs_unlock2): New.
(svn_fs_unlock): Call svn_fs_unlock2.
* subversion/libsvn_fs/fs-loader.h
(lock, unlock): Change parameters.
* subversion/libsvn_fs_fs/lock.h
(svn_fs_fs__lock,
svn_fs_fs__unlock): Change parameters.
* subversion/libsvn_fs_fs/lock.c
(set_lock,
delete_lock): Just act on one file.
(add_to_digest,
delete_from_digest): New.
(struct lock_baton): Change members.
(check_lock,
struct lock_info_t): New.
(lock_body): Handle multiple paths.
(struct unlock_baton): Change members.
(check_unlock): New.
(unlock_body): Handle multiple paths.
(unlock_single): New.
(svn_fs_fs__lock,
svn_fs_fs__unlock): Change parameters.
* subversion/libsvn_fs_base/lock.c
(svn_fs_base__lock,
svn_fs_base__unlock): Change parameters.
* subversion/libsvn_fs_base/lock.h
(svn_fs_base__lock,
svn_fs_base__unlock): Change parameters.
* subversion/libsvn_repos/fs-wrap.c
(svn_repos_fs_lock2): New.
(svn_repos_fs_lock): Call svn_repos_fs_lock2.
(svn_repos_fs_unlock2): New.
(svn_repos_fs_unlock): Call svn_repos_fs_unlock2.
* subversion/libsvn_repos/repos.c
(SVN_REPOS__HOOK_POST_LOCK): Tweak comments in prototype file.
(SVN_REPOS__HOOK_POST_UNLOCK): Tweak comments in prototype file.
* subversion/tests/libsvn_fs/locks-test.c
(lock_multiple_paths): New test.
(test_funcs): Add new test.
* subversion/tests/libsvn_ra/ra-test.c
(make_and_open_local_repos): Create auth baton.
(commit_tree): New helper.
(struct lock_baton_t): New.
(lock_cb): New helper.
(lock_test): New test.
(test_funcs): Add new test.
Modified:
subversion/branches/fsfs-lock-many/subversion/include/svn_fs.h
subversion/branches/fsfs-lock-many/subversion/include/svn_repos.h
subversion/branches/fsfs-lock-many/subversion/libsvn_fs/fs-loader.c
subversion/branches/fsfs-lock-many/subversion/libsvn_fs/fs-loader.h
subversion/branches/fsfs-lock-many/subversion/libsvn_fs_base/lock.c
subversion/branches/fsfs-lock-many/subversion/libsvn_fs_base/lock.h
subversion/branches/fsfs-lock-many/subversion/libsvn_fs_fs/lock.c
subversion/branches/fsfs-lock-many/subversion/libsvn_fs_fs/lock.h
subversion/branches/fsfs-lock-many/subversion/libsvn_repos/fs-wrap.c
subversion/branches/fsfs-lock-many/subversion/libsvn_repos/repos.c
subversion/branches/fsfs-lock-many/subversion/tests/libsvn_fs/locks-test.c
subversion/branches/fsfs-lock-many/subversion/tests/libsvn_ra/ra-test.c
Modified: subversion/branches/fsfs-lock-many/subversion/include/svn_fs.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-lock-many/subversion/include/svn_fs.h?rev=1571812&r1=1571811&r2=1571812&view=diff
==============================================================================
--- subversion/branches/fsfs-lock-many/subversion/include/svn_fs.h (original)
+++ subversion/branches/fsfs-lock-many/subversion/include/svn_fs.h Tue Feb 25 20:18:18 2014
@@ -2451,11 +2451,30 @@ svn_fs_set_uuid(svn_fs_t *fs,
* expiration error (depending on the API).
*/
+/* The @a targets hash passed to svn_fs_lock2() has <tt>const char
+ *</tt> keys and <tt>svn_fs_lock_target_t *</tt> values. */
+typedef struct svn_fs_lock_target_t
+{
+ const char *token;
+ svn_revnum_t current_rev;
+
+} svn_fs_lock_target_t;
+
+/* The @a results hash returned by svn_fs_lock2() and svn_fs_unlock2()
+ has <tt>const char *</tt> keys and <tt>svn_fs_lock_result_t *</tt>
+ values. */
+typedef struct svn_fs_lock_result_t
+{
+ svn_lock_t *lock;
+ svn_error_t *err;
+
+} svn_fs_lock_result_t;
-/** Lock @a path in @a fs, and set @a *lock to a lock
- * representing the new lock, allocated in @a pool.
+/** Lock the paths in @a targets in @a fs, and set @a *results to the
+ * locks or errors representing each new lock, allocated in @a
+ * result_pool.
*
- * @warning You may prefer to use svn_repos_fs_lock() instead,
+ * @warning You may prefer to use svn_repos_fs_lock2() instead,
* which see.
*
* @a fs must have a username associated with it (see
@@ -2469,28 +2488,56 @@ svn_fs_set_uuid(svn_fs_t *fs,
* generic DAV client; only mod_dav_svn's autoversioning feature needs
* to use it. If in doubt, pass 0.
*
- * If path is already locked, then return #SVN_ERR_FS_PATH_ALREADY_LOCKED,
+ * The paths to be locked are passed as the <tt>const char *<tt> keys
+ * of the @a targets hash. The hash values are
+ * <tt>svn_fs_lock_target_t *</tt> and provide the token and
+ * current_rev for each path. The token is a lock token such as can
+ * be generated using svn_fs_generate_lock_token() (indicating that
+ * the caller wants to dictate the lock token used), or it is @c NULL
+ * (indicating that the caller wishes to have a new token generated by
+ * this function). If the token is not @c NULL, and represents an
+ * existing lock, then the path must match the path associated with
+ * that existing lock. If current_rev is a valid revnum, then do an
+ * out-of-dateness check. If the revnum is less than the
+ * last-changed-revision of the path (or if the path doesn't exist in
+ * HEAD), return * #SVN_ERR_FS_OUT_OF_DATE.
+ *
+ * If a path is already locked, then return #SVN_ERR_FS_PATH_ALREADY_LOCKED,
* unless @a steal_lock is TRUE, in which case "steal" the existing
* lock, even if the FS access-context's username does not match the
- * current lock's owner: delete the existing lock on @a path, and
+ * current lock's owner: delete the existing lock on the path, and
* create a new one.
*
- * @a token is a lock token such as can be generated using
- * svn_fs_generate_lock_token() (indicating that the caller wants to
- * dictate the lock token used), or it is @c NULL (indicating that the
- * caller wishes to have a new token generated by this function). If
- * @a token is not @c NULL, and represents an existing lock, then @a
- * path must match the path associated with that existing lock.
- *
* If @a expiration_date is zero, then create a non-expiring lock.
* Else, the lock will expire at @a expiration_date.
*
- * If @a current_rev is a valid revnum, then do an out-of-dateness
- * check. If the revnum is less than the last-changed-revision of @a
- * path (or if @a path doesn't exist in HEAD), return
- * #SVN_ERR_FS_OUT_OF_DATE.
+ * The results are returned in @a *results hash where the keys are
+ * <tt>const char *</tt> paths and the values are
+ * <tt>svn_fs_lock_result_t *</tt>. The error associated with each
+ * path is returned as #svn_fs_lock_result_t->err. The caller must
+ * ensure that all such errors are handled to avoid leaks. The lock
+ * associated with each path is returned as #svn_fs_lock_result_t->lock,
+ * this will be @c NULL if no lock was created.
*
* @note At this time, only files can be locked.
+ *
+ * @since New in 1.9.
+ */
+svn_error_t *
+svn_fs_lock2(apr_hash_t **results,
+ svn_fs_t *fs,
+ apr_hash_t *targets,
+ const char *comment,
+ svn_boolean_t is_dav_comment,
+ apr_time_t expiration_date,
+ svn_boolean_t steal_lock,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/* Similar to svn_fs_lock2() but locks only a single @a path and
+ * returns the lock in @a *lock, allocated in @a pool, or an error.
+ *
+ * @since New in 1.2.
*/
svn_error_t *
svn_fs_lock(svn_lock_t **lock,
@@ -2517,20 +2564,53 @@ svn_fs_generate_lock_token(const char **
apr_pool_t *pool);
-/** Remove the lock on @a path represented by @a token in @a fs.
+/** Remove the locks on the paths in @a targets in @a fs, and return
+ * the results in @a *results allocated in @a result_pool.
*
- * If @a token doesn't point to a lock, return #SVN_ERR_FS_BAD_LOCK_TOKEN.
- * If @a token points to an expired lock, return #SVN_ERR_FS_LOCK_EXPIRED.
- * If @a fs has no username associated with it, return #SVN_ERR_FS_NO_USER
- * unless @a break_lock is specified.
+ * The paths to be unlocked are passed as <tt>const char *</tt> keys
+ * of the @a targets hash with <tt>svn_fs_lock_target_t *</tt> values.
+ * #svn_fs_lock_target_t->token provides the token to be unlocked for
+ * each path. If the the token doesn't point to a lock, return
+ * #SVN_ERR_FS_BAD_LOCK_TOKEN. If the token points to an expired
+ * lock, return #SVN_ERR_FS_LOCK_EXPIRED. If @a fs has no username
+ * associated with it, return #SVN_ERR_FS_NO_USER unless @a break_lock
+ * is specified.
*
- * If @a token points to a lock, but the username of @a fs's access
+ * If the token points to a lock, but the username of @a fs's access
* context doesn't match the lock's owner, return
* #SVN_ERR_FS_LOCK_OWNER_MISMATCH. If @a break_lock is TRUE, however, don't
* return error; allow the lock to be "broken" in any case. In the latter
- * case, @a token shall be @c NULL.
+ * case, the token shall be @c NULL.
+ *
+ * The results are returned in @a *results hash where the keys are
+ * <tt>const char *</tt> paths and the values are
+ * <tt>svn_fs_lock_result_t *</tt>. The error associated with each
+ * path is returned as #svn_fs_lock_result_t->err. The caller must
+ * ensure that all such errors are handled to avoid leaks.
+ *
+ * @note #svn_fs_lock_target_t is used to allow @c NULL tokens to be
+ * passed (it is not possible to pass @c NULL as a hash value
+ * directly), #svn_fs_lock_target_t->current_rev is ignored.
+ *
+ * @note #svn_err_fs_lock_result_t is used to allow @c SVN_NO_ERROR to
+ * be returned (it is not possible to return @c SVN_NO_ERROR as a hash
+ * value directly), #svn_err_fs_lock_result_t->lock is always NULL.
*
* Use @a pool for temporary allocations.
+ *
+ * @since New in 1.9.
+ */
+svn_error_t *
+svn_fs_unlock2(apr_hash_t **results,
+ svn_fs_t *fs,
+ apr_hash_t *targets,
+ svn_boolean_t break_lock,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/** Similar to svn_fs_unlock2() but only unlocks a single path.
+ *
+ * @since New in 1.2.
*/
svn_error_t *
svn_fs_unlock(svn_fs_t *fs,
Modified: subversion/branches/fsfs-lock-many/subversion/include/svn_repos.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-lock-many/subversion/include/svn_repos.h?rev=1571812&r1=1571811&r2=1571812&view=diff
==============================================================================
--- subversion/branches/fsfs-lock-many/subversion/include/svn_repos.h (original)
+++ subversion/branches/fsfs-lock-many/subversion/include/svn_repos.h Tue Feb 25 20:18:18 2014
@@ -2177,20 +2177,40 @@ svn_repos_fs_begin_txn_for_update(svn_fs
* @{
*/
-/** Like svn_fs_lock(), but invoke the @a repos's pre- and
+/** Like svn_fs_lock2(), but invoke the @a repos's pre- and
* post-lock hooks before and after the locking action. Use @a pool
* for any necessary allocations.
*
- * If the pre-lock hook or svn_fs_lock() fails, throw the original
- * error to caller. If an error occurs when running the post-lock
- * hook, return the original error wrapped with
+ * The pre-lock is run for every path in @a targets. Those entries in
+ * @a targets for which the pre-lock is successful are passed to
+ * svn_fs_lock2 and the post-lock is run for those that are
+ * successfully locked.
+ *
+ * @a results contains the result of running the pre-lock and
+ * svn_fs_lock2 if the pre-lock was successful. If an error occurs
+ * when running the post-lock hook the error is returned wrapped with
* SVN_ERR_REPOS_POST_LOCK_HOOK_FAILED. If the caller sees this
- * error, it knows that the lock succeeded anyway.
+ * error, it knows that the some locks succeeded. In all cases the
+ * caller must handle all errors in @a results to avoid leaks.
*
* The pre-lock hook may cause a different token to be used for the
* lock, instead of @a token; see the pre-lock-hook documentation for
* more.
*
+ * @since New in 1.9.
+ */
+svn_error_t *
+svn_repos_fs_lock2(apr_hash_t **results,
+ 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,
+ apr_pool_t *scratch_pool);
+
+/** Similar to svn_repos_fs_lock2() but locks only a single path.
+ *
* @since New in 1.2.
*/
svn_error_t *
@@ -2219,6 +2239,13 @@ svn_repos_fs_lock(svn_lock_t **lock,
* @since New in 1.2.
*/
svn_error_t *
+svn_repos_fs_unlock2(apr_hash_t **results,
+ svn_repos_t *repos,
+ apr_hash_t *targets,
+ svn_boolean_t break_lock,
+ apr_pool_t *pool);
+
+svn_error_t *
svn_repos_fs_unlock(svn_repos_t *repos,
const char *path,
const char *token,
Modified: subversion/branches/fsfs-lock-many/subversion/libsvn_fs/fs-loader.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-lock-many/subversion/libsvn_fs/fs-loader.c?rev=1571812&r1=1571811&r2=1571812&view=diff
==============================================================================
--- subversion/branches/fsfs-lock-many/subversion/libsvn_fs/fs-loader.c (original)
+++ subversion/branches/fsfs-lock-many/subversion/libsvn_fs/fs-loader.c Tue Feb 25 20:18:18 2014
@@ -1561,54 +1561,115 @@ svn_fs_set_uuid(svn_fs_t *fs, const char
}
svn_error_t *
-svn_fs_lock(svn_lock_t **lock, svn_fs_t *fs, 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_fs_lock2(apr_hash_t **results,
+ svn_fs_t *fs,
+ apr_hash_t *targets,
+ const char *comment,
+ svn_boolean_t is_dav_comment,
+ apr_time_t expiration_date,
+ svn_boolean_t steal_lock,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
+ apr_hash_index_t *hi;
+ apr_hash_t *ok_targets = apr_hash_make(scratch_pool), *ok_results;
+ svn_error_t *err;
+
+ *results = apr_hash_make(result_pool);
+
/* Enforce that the comment be xml-escapable. */
if (comment)
- {
- if (! svn_xml_is_xml_safe(comment, strlen(comment)))
- return svn_error_create
- (SVN_ERR_XML_UNESCAPABLE_DATA, NULL,
- _("Lock comment contains illegal characters"));
- }
+ if (! svn_xml_is_xml_safe(comment, strlen(comment)))
+ return svn_error_create(SVN_ERR_XML_UNESCAPABLE_DATA, NULL,
+ _("Lock comment contains illegal characters"));
+
+ if (expiration_date < 0)
+ return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
+ _("Negative expiration date passed to svn_fs_lock"));
/* Enforce that the token be an XML-safe URI. */
- if (token)
+ for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi))
{
- const char *c;
+ const svn_fs_lock_target_t *target = svn__apr_hash_index_val(hi);
+
+ err = SVN_NO_ERROR;
+ if (target->token)
+ {
+ const char *c;
+
+
+ if (strncmp(target->token, "opaquelocktoken:", 16))
+ err = svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
+ _("Lock token URI '%s' has bad scheme; "
+ "expected '%s'"),
+ target->token, "opaquelocktoken");
+
+ if (!err)
+ for (c = target->token; *c && !err; c++)
+ if (! svn_ctype_isascii(*c) || svn_ctype_iscntrl(*c))
+ err = svn_error_createf(
+ SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
+ _("Lock token '%s' is not ASCII or is a "
+ "control character at byte %u"),
+ target->token,
+ (unsigned)(c - target->token));
+
+ /* strlen(token) == c - token. */
+ if (!err && !svn_xml_is_xml_safe(target->token, c - target->token))
+ err = svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
+ _("Lock token URI '%s' is not XML-safe"),
+ target->token);
+ }
- if (strncmp(token, "opaquelocktoken:", 16))
- return svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
- _("Lock token URI '%s' has bad scheme; "
- "expected '%s'"),
- token, "opaquelocktoken");
-
- for (c = token; *c; c++)
- if (! svn_ctype_isascii(*c) || svn_ctype_iscntrl(*c))
- return svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
- _("Lock token '%s' is not ASCII or is a "
- "control character at byte %u"),
- token, (unsigned)(c - token));
-
- /* strlen(token) == c - token. */
- if (! svn_xml_is_xml_safe(token, c - token))
- return svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
- _("Lock token URI '%s' is not XML-safe"),
- token);
+ if (err)
+ {
+ svn_fs_lock_result_t *result
+ = apr_palloc(result_pool, sizeof(svn_fs_lock_result_t));
+
+ result->err = err;
+ result->lock = NULL;
+ svn_hash_sets(*results, svn__apr_hash_index_key(hi), result);
+ }
+ else
+ svn_hash_sets(ok_targets, svn__apr_hash_index_key(hi), target);
}
- if (expiration_date < 0)
- return svn_error_create
- (SVN_ERR_INCORRECT_PARAMS, NULL,
- _("Negative expiration date passed to svn_fs_lock"));
-
- return svn_error_trace(fs->vtable->lock(lock, fs, path, token, comment,
- is_dav_comment, expiration_date,
- current_rev, steal_lock, pool));
+ err = fs->vtable->lock(&ok_results, fs, ok_targets, comment,
+ is_dav_comment, expiration_date,
+ steal_lock, result_pool, scratch_pool);
+
+ for (hi = apr_hash_first(scratch_pool, ok_results);
+ hi; hi = apr_hash_next(hi))
+ svn_hash_sets(*results, svn__apr_hash_index_key(hi),
+ svn__apr_hash_index_val(hi));
+
+ return svn_error_trace(err);
+}
+
+svn_error_t *
+svn_fs_lock(svn_lock_t **lock, svn_fs_t *fs, 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), *results;
+ svn_fs_lock_target_t target;
+ svn_fs_lock_result_t *result;
+
+ target.token = token;
+ target.current_rev = current_rev;
+ svn_hash_sets(targets, path, &target);
+
+ SVN_ERR(svn_fs_lock2(&results, fs, targets, comment, is_dav_comment,
+ expiration_date, steal_lock, pool, pool));
+
+ SVN_ERR_ASSERT(apr_hash_count(results));
+ result = svn__apr_hash_index_val(apr_hash_first(pool, results));
+ if (result->lock)
+ *lock = result->lock;
+
+ return result->err;
}
svn_error_t *
@@ -1618,11 +1679,34 @@ svn_fs_generate_lock_token(const char **
}
svn_error_t *
+svn_fs_unlock2(apr_hash_t **results,
+ svn_fs_t *fs,
+ apr_hash_t *targets,
+ svn_boolean_t break_lock,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ return svn_error_trace(fs->vtable->unlock(results, fs, targets, break_lock,
+ result_pool, scratch_pool));
+}
+
+svn_error_t *
svn_fs_unlock(svn_fs_t *fs, const char *path, const char *token,
svn_boolean_t break_lock, apr_pool_t *pool)
{
- return svn_error_trace(fs->vtable->unlock(fs, path, token, break_lock,
- pool));
+ apr_hash_t *targets = apr_hash_make(pool), *results;
+ svn_fs_lock_result_t *result;
+
+ if (!token)
+ token = "";
+ svn_hash_sets(targets, path, token);
+
+ SVN_ERR(svn_fs_unlock2(&results, fs, targets, break_lock, pool, pool));
+
+ SVN_ERR_ASSERT(apr_hash_count(results));
+ result = svn__apr_hash_index_val(apr_hash_first(pool, results));
+
+ return result->err;
}
svn_error_t *
Modified: subversion/branches/fsfs-lock-many/subversion/libsvn_fs/fs-loader.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-lock-many/subversion/libsvn_fs/fs-loader.h?rev=1571812&r1=1571811&r2=1571812&view=diff
==============================================================================
--- subversion/branches/fsfs-lock-many/subversion/libsvn_fs/fs-loader.h (original)
+++ subversion/branches/fsfs-lock-many/subversion/libsvn_fs/fs-loader.h Tue Feb 25 20:18:18 2014
@@ -217,16 +217,18 @@ typedef struct fs_vtable_t
svn_error_t *(*list_transactions)(apr_array_header_t **names_p,
svn_fs_t *fs, apr_pool_t *pool);
svn_error_t *(*deltify)(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool);
- svn_error_t *(*lock)(svn_lock_t **lock, svn_fs_t *fs,
- const char *path, const char *token,
+ svn_error_t *(*lock)(apr_hash_t **results,
+ svn_fs_t *fs,
+ apr_hash_t *targets,
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_time_t expiration_date, svn_boolean_t steal_lock,
+ apr_pool_t *result_pool, apr_pool_t *scratch_pool);
svn_error_t *(*generate_lock_token)(const char **token, svn_fs_t *fs,
apr_pool_t *pool);
- svn_error_t *(*unlock)(svn_fs_t *fs, const char *path, const char *token,
- svn_boolean_t break_lock, apr_pool_t *pool);
+ svn_error_t *(*unlock)(apr_hash_t ** results,
+ svn_fs_t *fs, apr_hash_t *targets,
+ svn_boolean_t break_lock,
+ apr_pool_t *result_pool, apr_pool_t *scratch_pool);
svn_error_t *(*get_lock)(svn_lock_t **lock, svn_fs_t *fs,
const char *path, apr_pool_t *pool);
svn_error_t *(*get_locks)(svn_fs_t *fs, const char *path, svn_depth_t depth,
Modified: subversion/branches/fsfs-lock-many/subversion/libsvn_fs_base/lock.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-lock-many/subversion/libsvn_fs_base/lock.c?rev=1571812&r1=1571811&r2=1571812&view=diff
==============================================================================
--- subversion/branches/fsfs-lock-many/subversion/libsvn_fs_base/lock.c (original)
+++ subversion/branches/fsfs-lock-many/subversion/libsvn_fs_base/lock.c Tue Feb 25 20:18:18 2014
@@ -91,6 +91,8 @@ txn_body_lock(void *baton, trail_t *trai
svn_lock_t *existing_lock;
svn_lock_t *lock;
+ *args->lock_p = NULL;
+
SVN_ERR(svn_fs_base__get_path_kind(&kind, args->path, trail, trail->pool));
/* Until we implement directory locks someday, we only allow locks
@@ -215,31 +217,47 @@ txn_body_lock(void *baton, trail_t *trai
svn_error_t *
-svn_fs_base__lock(svn_lock_t **lock,
+svn_fs_base__lock(apr_hash_t **results,
svn_fs_t *fs,
- const char *path,
- const char *token,
+ apr_hash_t *targets,
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_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- struct lock_args args;
+ apr_hash_index_t *hi;
+
+ *results = apr_hash_make(result_pool);
SVN_ERR(svn_fs__check_fs(fs, TRUE));
- args.lock_p = lock;
- args.path = svn_fs__canonicalize_abspath(path, pool);
- args.token = token;
- args.comment = comment;
- args.is_dav_comment = is_dav_comment;
- args.steal_lock = steal_lock;
- args.expiration_date = expiration_date;
- args.current_rev = current_rev;
+ for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi))
+ {
+ struct lock_args args;
+ const char *path = svn__apr_hash_index_key(hi);
+ const svn_fs_lock_target_t *target = svn__apr_hash_index_val(hi);
+ svn_lock_t *lock;
+ svn_fs_lock_result_t *result = apr_palloc(result_pool,
+ sizeof(svn_fs_lock_result_t));
+
+ args.lock_p = &lock;
+ args.path = svn_fs__canonicalize_abspath(path, result_pool);
+ args.token = target->token;
+ args.comment = comment;
+ args.is_dav_comment = is_dav_comment;
+ args.steal_lock = steal_lock;
+ args.expiration_date = expiration_date;
+ args.current_rev = target->current_rev;
+
+ result->err = svn_fs_base__retry_txn(fs, txn_body_lock, &args, FALSE,
+ scratch_pool);
+ result->lock = lock;
+ svn_hash_sets(*results, args.path, result);
+ }
- return svn_fs_base__retry_txn(fs, txn_body_lock, &args, FALSE, pool);
+ return SVN_NO_ERROR;
}
@@ -307,20 +325,37 @@ txn_body_unlock(void *baton, trail_t *tr
svn_error_t *
-svn_fs_base__unlock(svn_fs_t *fs,
- const char *path,
- const char *token,
+svn_fs_base__unlock(apr_hash_t **results,
+ svn_fs_t *fs,
+ apr_hash_t *targets,
svn_boolean_t break_lock,
- apr_pool_t *pool)
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- struct unlock_args args;
+ apr_hash_index_t *hi;
+
+ *results = apr_hash_make(result_pool);
SVN_ERR(svn_fs__check_fs(fs, TRUE));
- args.path = svn_fs__canonicalize_abspath(path, pool);
- args.token = token;
- args.break_lock = break_lock;
- return svn_fs_base__retry_txn(fs, txn_body_unlock, &args, TRUE, pool);
+ for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi))
+ {
+ struct unlock_args args;
+ const char *path = svn__apr_hash_index_key(hi);
+ const char *token = svn__apr_hash_index_val(hi);
+ svn_fs_lock_result_t *result = apr_palloc(result_pool,
+ sizeof(svn_fs_lock_result_t));
+
+ args.path = svn_fs__canonicalize_abspath(path, result_pool);
+ args.token = token;
+ args.break_lock = break_lock;
+ result->err = svn_fs_base__retry_txn(fs, txn_body_unlock, &args, TRUE,
+ scratch_pool);
+ result->lock = NULL;
+ svn_hash_sets(*results, args.path, result);
+ }
+
+ return SVN_NO_ERROR;
}
Modified: subversion/branches/fsfs-lock-many/subversion/libsvn_fs_base/lock.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-lock-many/subversion/libsvn_fs_base/lock.h?rev=1571812&r1=1571811&r2=1571812&view=diff
==============================================================================
--- subversion/branches/fsfs-lock-many/subversion/libsvn_fs_base/lock.h (original)
+++ subversion/branches/fsfs-lock-many/subversion/libsvn_fs_base/lock.h Tue Feb 25 20:18:18 2014
@@ -35,26 +35,26 @@ extern "C" {
/* These functions implement part of the FS loader library's fs
vtables. See the public svn_fs.h for docstrings.*/
-svn_error_t *svn_fs_base__lock(svn_lock_t **lock,
+svn_error_t *svn_fs_base__lock(apr_hash_t **results,
svn_fs_t *fs,
- const char *path,
- const char *token,
+ apr_hash_t *targets,
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_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
svn_error_t *svn_fs_base__generate_lock_token(const char **token,
svn_fs_t *fs,
apr_pool_t *pool);
-svn_error_t *svn_fs_base__unlock(svn_fs_t *fs,
- const char *path,
- const char *token,
+svn_error_t *svn_fs_base__unlock(apr_hash_t **results,
+ svn_fs_t *fs,
+ apr_hash_t *targets,
svn_boolean_t break_lock,
- apr_pool_t *pool);
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
svn_error_t *svn_fs_base__get_lock(svn_lock_t **lock,
svn_fs_t *fs,
Modified: subversion/branches/fsfs-lock-many/subversion/libsvn_fs_fs/lock.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-lock-many/subversion/libsvn_fs_fs/lock.c?rev=1571812&r1=1571811&r2=1571812&view=diff
==============================================================================
--- subversion/branches/fsfs-lock-many/subversion/libsvn_fs_fs/lock.c (original)
+++ subversion/branches/fsfs-lock-many/subversion/libsvn_fs_fs/lock.c Tue Feb 25 20:18:18 2014
@@ -42,6 +42,7 @@
#include "private/svn_fs_util.h"
#include "private/svn_fspath.h"
+#include "private/svn_sorts_private.h"
/* Names of hash keys used to store a lock for writing to disk. */
#define PATH_KEY "path"
@@ -339,8 +340,6 @@ read_digest_file(apr_hash_t **children_p
/* Write LOCK in FS to the actual OS filesystem.
Use PERMS_REFERENCE for the permissions of any digest files.
-
- Note: this takes an FS_PATH because it's called from the hotcopy logic.
*/
static svn_error_t *
set_lock(const char *fs_path,
@@ -348,129 +347,110 @@ set_lock(const char *fs_path,
const char *perms_reference,
apr_pool_t *pool)
{
- svn_stringbuf_t *this_path = svn_stringbuf_create(lock->path, pool);
- const char *lock_digest_path = NULL;
- apr_pool_t *subpool;
+ const char *digest_path;
+ apr_hash_t *children;
- SVN_ERR_ASSERT(lock);
+ SVN_ERR(digest_path_from_path(&digest_path, fs_path, lock->path, pool));
- /* Iterate in reverse, creating the lock for LOCK->path, and then
- just adding entries for its parent, until we reach a parent
- that's already listed in *its* parent. */
- subpool = svn_pool_create(pool);
- while (1729)
- {
- const char *digest_path, *digest_file;
- apr_hash_t *this_children;
- svn_lock_t *this_lock;
+ /* We could get away without reading the file as children should
+ always come back empty. */
+ SVN_ERR(read_digest_file(&children, NULL, fs_path, digest_path, pool));
- svn_pool_clear(subpool);
+ SVN_ERR(write_digest_file(children, lock, fs_path, digest_path,
+ perms_reference, pool));
- /* Calculate the DIGEST_PATH for the currently FS path, and then
- get its DIGEST_FILE basename. */
- SVN_ERR(digest_path_from_path(&digest_path, fs_path, this_path->data,
- subpool));
- digest_file = svn_dirent_basename(digest_path, subpool);
+ return SVN_NO_ERROR;
+}
- SVN_ERR(read_digest_file(&this_children, &this_lock, fs_path,
- digest_path, subpool));
+static svn_error_t *
+delete_lock(const char *fs_path,
+ const char *path,
+ apr_pool_t *pool)
+{
+ const char *digest_path;
- /* We're either writing a new lock (first time through only) or
- a new entry (every time but the first). */
- if (lock)
- {
- this_lock = lock;
- lock = NULL;
- lock_digest_path = apr_pstrdup(pool, digest_file);
- }
- else
- {
- /* If we already have an entry for this path, we're done. */
- if (svn_hash_gets(this_children, lock_digest_path))
- break;
- svn_hash_sets(this_children, lock_digest_path, (void *)1);
- }
- SVN_ERR(write_digest_file(this_children, this_lock, fs_path,
- digest_path, perms_reference, subpool));
+ SVN_ERR(digest_path_from_path(&digest_path, fs_path, path, pool));
- /* Prep for next iteration, or bail if we're done. */
- if (svn_fspath__is_root(this_path->data, this_path->len))
- break;
- svn_stringbuf_set(this_path,
- svn_fspath__dirname(this_path->data, subpool));
- }
+ SVN_ERR(svn_io_remove_file2(digest_path, TRUE, pool));
- svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
-/* Delete LOCK from FS in the actual OS filesystem. */
static svn_error_t *
-delete_lock(svn_fs_t *fs,
- svn_lock_t *lock,
- apr_pool_t *pool)
+add_to_digest(const char *fs_path,
+ apr_array_header_t *paths,
+ const char *index_path,
+ const char *perms_reference,
+ apr_pool_t *pool)
{
- svn_stringbuf_t *this_path = svn_stringbuf_create(lock->path, pool);
- const char *child_to_kill = NULL;
- apr_pool_t *subpool;
+ const char *index_digest_path;
+ apr_hash_t *children;
+ svn_lock_t *lock;
+ int i, original_count;
- SVN_ERR_ASSERT(lock);
+ SVN_ERR(digest_path_from_path(&index_digest_path, fs_path, index_path, pool));
- /* Iterate in reverse, deleting the lock for LOCK->path, and then
- deleting its entry as it appears in each of its parents. */
- subpool = svn_pool_create(pool);
- while (1729)
+ SVN_ERR(read_digest_file(&children, &lock, fs_path, index_digest_path, pool));
+
+ original_count = apr_hash_count(children);
+
+ for (i = 0; i < paths->nelts; ++i)
{
+ const char *path = APR_ARRAY_IDX(paths, i, const char *);
const char *digest_path, *digest_file;
- apr_hash_t *this_children;
- svn_lock_t *this_lock;
- svn_pool_clear(subpool);
+ SVN_ERR(digest_path_from_path(&digest_path, fs_path, path, pool));
+ digest_file = svn_dirent_basename(digest_path, NULL);
+ svn_hash_sets(children, digest_file, (void *)1);
+ }
- /* Calculate the DIGEST_PATH for the currently FS path, and then
- get its DIGEST_FILE basename. */
- SVN_ERR(digest_path_from_path(&digest_path, fs->path, this_path->data,
- subpool));
- digest_file = svn_dirent_basename(digest_path, subpool);
+ if (apr_hash_count(children) != original_count)
+ SVN_ERR(write_digest_file(children, lock, fs_path, index_digest_path,
+ perms_reference, pool));
- SVN_ERR(read_digest_file(&this_children, &this_lock, fs->path,
- digest_path, subpool));
+ return SVN_NO_ERROR;
+}
- /* Delete the lock (first time through only). */
- if (lock)
- {
- this_lock = NULL;
- lock = NULL;
- child_to_kill = apr_pstrdup(pool, digest_file);
- }
+static svn_error_t *
+delete_from_digest(const char *fs_path,
+ apr_array_header_t *paths,
+ const char *index_path,
+ const char *perms_reference,
+ apr_pool_t *pool)
+{
+ const char *index_digest_path;
+ apr_hash_t *children;
+ svn_lock_t *lock;
+ int i;
- if (child_to_kill)
- svn_hash_sets(this_children, child_to_kill, NULL);
+ SVN_ERR(digest_path_from_path(&index_digest_path, fs_path, index_path, pool));
- if (! (this_lock || apr_hash_count(this_children) != 0))
- {
- /* Special case: no goodz, no file. And remember to nix
- the entry for it in its parent. */
- SVN_ERR(svn_io_remove_file2(digest_path, FALSE, subpool));
- }
- else
- {
- const char *rev_0_path = svn_fs_fs__path_rev_absolute(fs, 0, pool);
- SVN_ERR(write_digest_file(this_children, this_lock, fs->path,
- digest_path, rev_0_path, subpool));
- }
+ SVN_ERR(read_digest_file(&children, &lock, fs_path, index_digest_path, pool));
- /* Prep for next iteration, or bail if we're done. */
- if (svn_fspath__is_root(this_path->data, this_path->len))
- break;
- svn_stringbuf_set(this_path,
- svn_fspath__dirname(this_path->data, subpool));
+ for (i = 0; i < paths->nelts; ++i)
+ {
+ const char *path = APR_ARRAY_IDX(paths, i, const char *);
+ const char *digest_path, *digest_file;
+
+ SVN_ERR(digest_path_from_path(&digest_path, fs_path, path, pool));
+ digest_file = svn_dirent_basename(digest_path, NULL);
+ svn_hash_sets(children, digest_file, NULL);
}
- svn_pool_destroy(subpool);
+ if (apr_hash_count(children) || lock)
+ SVN_ERR(write_digest_file(children, lock, fs_path, index_digest_path,
+ perms_reference, pool));
+ else
+ SVN_ERR(svn_io_remove_file2(index_digest_path, TRUE, pool));
+
return SVN_NO_ERROR;
}
+static svn_error_t *
+unlock_single(svn_fs_t *fs,
+ svn_lock_t *lock,
+ apr_pool_t *pool);
+
/* 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
@@ -505,7 +485,7 @@ get_lock(svn_lock_t **lock_p,
/* Only remove the lock if we have the write lock.
Read operations shouldn't change the filesystem. */
if (have_write_lock)
- SVN_ERR(delete_lock(fs, lock, pool));
+ SVN_ERR(unlock_single(fs, lock, pool));
return SVN_FS__ERR_LOCK_EXPIRED(fs, lock->token);
}
@@ -580,7 +560,7 @@ locks_walker(void *baton,
/* Only remove the lock if we have the write lock.
Read operations shouldn't change the filesystem. */
if (have_write_lock)
- SVN_ERR(delete_lock(wlb->fs, lock, pool));
+ SVN_ERR(unlock_single(wlb->fs, lock, pool));
}
}
@@ -737,68 +717,59 @@ svn_fs_fs__allow_locked_operation(const
/* Baton used for lock_body below. */
struct lock_baton {
- svn_lock_t **lock_p;
svn_fs_t *fs;
- const char *path;
- const char *token;
+ apr_array_header_t *targets;
+ apr_array_header_t *infos;
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_pool_t *result_pool;
};
-
-/* This implements the svn_fs_fs__with_write_lock() 'body' callback
- type, and assumes that the write lock is held.
- BATON is a 'struct lock_baton *'. */
static svn_error_t *
-lock_body(void *baton, apr_pool_t *pool)
+check_lock(svn_error_t **fs_err,
+ const char *path,
+ const svn_fs_lock_target_t *target,
+ struct lock_baton *lb,
+ svn_fs_root_t *root,
+ apr_pool_t *pool)
{
- struct lock_baton *lb = baton;
svn_node_kind_t kind;
svn_lock_t *existing_lock;
- svn_lock_t *lock;
- svn_fs_root_t *root;
- svn_revnum_t youngest;
- const char *rev_0_path;
- /* Until we implement directory locks someday, we only allow locks
- on files or non-existent paths. */
- /* 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));
- SVN_ERR(lb->fs->vtable->revision_root(&root, lb->fs, youngest, pool));
- SVN_ERR(svn_fs_fs__check_path(&kind, root, lb->path, pool));
+ *fs_err = SVN_NO_ERROR;
+
+ SVN_ERR(svn_fs_fs__check_path(&kind, root, path, pool));
if (kind == svn_node_dir)
- return SVN_FS__ERR_NOT_FILE(lb->fs, lb->path);
+ {
+ *fs_err = SVN_FS__ERR_NOT_FILE(lb->fs, path);
+ return SVN_NO_ERROR;
+ }
/* While our locking implementation easily supports the locking of
nonexistent paths, we deliberately choose not to allow such madness. */
if (kind == svn_node_none)
{
- if (SVN_IS_VALID_REVNUM(lb->current_rev))
- return svn_error_createf(
+ if (SVN_IS_VALID_REVNUM(target->current_rev))
+ *fs_err = svn_error_createf(
SVN_ERR_FS_OUT_OF_DATE, NULL,
_("Path '%s' doesn't exist in HEAD revision"),
- lb->path);
+ path);
else
- return svn_error_createf(
+ *fs_err = svn_error_createf(
SVN_ERR_FS_NOT_FOUND, NULL,
_("Path '%s' doesn't exist in HEAD revision"),
- lb->path);
- }
+ path);
- /* We need to have a username attached to the fs. */
- if (!lb->fs->access_ctx || !lb->fs->access_ctx->username)
- return SVN_FS__ERR_NO_USER(lb->fs);
+ return SVN_NO_ERROR;
+ }
/* Is the caller attempting to lock an out-of-date working file? */
- if (SVN_IS_VALID_REVNUM(lb->current_rev))
+ if (SVN_IS_VALID_REVNUM(target->current_rev))
{
svn_revnum_t created_rev;
- SVN_ERR(svn_fs_fs__node_created_rev(&created_rev, root, lb->path,
+ SVN_ERR(svn_fs_fs__node_created_rev(&created_rev, root, path,
pool));
/* SVN_INVALID_REVNUM means the path doesn't exist. So
@@ -806,14 +777,22 @@ lock_body(void *baton, apr_pool_t *pool)
working copy, but somebody else has deleted the thing
from HEAD. That counts as being 'out of date'. */
if (! SVN_IS_VALID_REVNUM(created_rev))
- return svn_error_createf
- (SVN_ERR_FS_OUT_OF_DATE, NULL,
- _("Path '%s' doesn't exist in HEAD revision"), lb->path);
-
- if (lb->current_rev < created_rev)
- return svn_error_createf
- (SVN_ERR_FS_OUT_OF_DATE, NULL,
- _("Lock failed: newer version of '%s' exists"), lb->path);
+ {
+ *fs_err = svn_error_createf
+ (SVN_ERR_FS_OUT_OF_DATE, NULL,
+ _("Path '%s' doesn't exist in HEAD revision"), path);
+
+ return SVN_NO_ERROR;
+ }
+
+ if (target->current_rev < created_rev)
+ {
+ *fs_err = svn_error_createf
+ (SVN_ERR_FS_OUT_OF_DATE, NULL,
+ _("Lock failed: newer version of '%s' exists"), path);
+
+ return SVN_NO_ERROR;
+ }
}
/* If the caller provided a TOKEN, we *really* need to see
@@ -832,117 +811,428 @@ lock_body(void *baton, apr_pool_t *pool)
acceptable to ignore; it means that the path is now free and
clear for locking, because the fsfs funcs just cleared out both
of the tables for us. */
- SVN_ERR(get_lock_helper(lb->fs, &existing_lock, lb->path, TRUE, pool));
+ SVN_ERR(get_lock_helper(lb->fs, &existing_lock, path, TRUE, pool));
if (existing_lock)
{
if (! lb->steal_lock)
{
/* Sorry, the path is already locked. */
- return SVN_FS__ERR_PATH_ALREADY_LOCKED(lb->fs, existing_lock);
- }
- else
- {
- /* STEAL_LOCK was passed, so fs_username is "stealing" the
- lock from lock->owner. Destroy the existing lock. */
- SVN_ERR(delete_lock(lb->fs, existing_lock, pool));
+ *fs_err = SVN_FS__ERR_PATH_ALREADY_LOCKED(lb->fs, existing_lock);
+ return SVN_NO_ERROR;
}
}
- /* Create our new lock, and add it to the tables.
- Ensure that the lock is created in the correct pool. */
- lock = svn_lock_create(lb->pool);
- if (lb->token)
- lock->token = apr_pstrdup(lb->pool, lb->token);
- else
- SVN_ERR(svn_fs_fs__generate_lock_token(&(lock->token), lb->fs,
- lb->pool));
- lock->path = apr_pstrdup(lb->pool, lb->path);
- lock->owner = apr_pstrdup(lb->pool, lb->fs->access_ctx->username);
- lock->comment = apr_pstrdup(lb->pool, lb->comment);
- lock->is_dav_comment = lb->is_dav_comment;
- lock->creation_date = apr_time_now();
- lock->expiration_date = lb->expiration_date;
+ return SVN_NO_ERROR;
+}
+
+struct lock_info_t {
+ const char *path;
+ const char *component;
+ svn_lock_t *lock;
+ svn_error_t *fs_err;
+};
+
+/* This implements the svn_fs_fs__with_write_lock() 'body' callback
+ type, and assumes that the write lock is held.
+ BATON is a 'struct lock_baton *'. */
+static svn_error_t *
+lock_body(void *baton, apr_pool_t *pool)
+{
+ struct lock_baton *lb = baton;
+ svn_fs_root_t *root;
+ svn_revnum_t youngest;
+ const char *rev_0_path;
+ int i, outstanding = 0;
+ apr_pool_t *iterpool = svn_pool_create(pool);
+
+ lb->infos = apr_array_make(lb->result_pool, lb->targets->nelts,
+ sizeof(struct lock_info_t));
+
+ /* Until we implement directory locks someday, we only allow locks
+ on files or non-existent paths. */
+ /* 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));
+ SVN_ERR(lb->fs->vtable->revision_root(&root, lb->fs, youngest, pool));
+
+ for (i = 0; i < lb->targets->nelts; ++i)
+ {
+ 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;
+ struct lock_info_t info;
+
+ svn_pool_clear(iterpool);
+
+ info.path = item->key;
+ SVN_ERR(check_lock(&info.fs_err, info.path, target, lb, root, iterpool));
+ info.lock = NULL;
+ info.component = NULL;
+ APR_ARRAY_PUSH(lb->infos, struct lock_info_t) = info;
+ if (!info.fs_err)
+ ++outstanding;
+ }
rev_0_path = svn_fs_fs__path_rev_absolute(lb->fs, 0, pool);
- SVN_ERR(set_lock(lb->fs->path, lock, rev_0_path, pool));
- *lb->lock_p = lock;
+ /* 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'.
+
+ Writing indices before locks is correct: if interrupted it leaves
+ indices without locks rather than locks without indices. An
+ index without a lock is consistent in that it always shows up as
+ unlocked in svn_fs_fs__allow_locked_operation. A lock without an
+ index is inconsistent, svn_fs_fs__allow_locked_operation will
+ show locked on the file but unlocked on the parent. */
+
+
+ while (outstanding)
+ {
+ const char *last_path = NULL;
+ apr_array_header_t *paths;
+
+ svn_pool_clear(iterpool);
+ paths = apr_array_make(iterpool, 1, sizeof(const char *));
+
+ for (i = 0; i < lb->infos->nelts; ++i)
+ {
+ struct lock_info_t *info = &APR_ARRAY_IDX(lb->infos, i,
+ struct 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_fs__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));
+ }
+ }
+
return SVN_NO_ERROR;
}
-/* Baton used for unlock_body below. */
struct unlock_baton {
svn_fs_t *fs;
- const char *path;
- const char *token;
+ apr_array_header_t *targets;
+ apr_array_header_t *infos;
+ svn_boolean_t skip_check;
svn_boolean_t break_lock;
+ apr_pool_t *result_pool;
+};
+
+static svn_error_t *
+check_unlock(svn_error_t **fs_err,
+ const char *path,
+ const char *token,
+ struct unlock_baton *ub,
+ svn_fs_root_t *root,
+ apr_pool_t *pool)
+{
+ svn_lock_t *lock;
+
+ *fs_err = get_lock(&lock, ub->fs, path, TRUE, TRUE, pool);
+ if (!*fs_err && !ub->break_lock)
+ {
+ if (strcmp(token, lock->token) != 0)
+ *fs_err = SVN_FS__ERR_NO_SUCH_LOCK(ub->fs, path);
+ else if (strcmp(ub->fs->access_ctx->username, lock->owner) != 0)
+ *fs_err = SVN_FS__ERR_LOCK_OWNER_MISMATCH(ub->fs,
+ ub->fs->access_ctx->username,
+ lock->owner);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+struct unlock_info_t {
+ const char *path;
+ const char *component;
+ svn_error_t *fs_err;
+ int components;
};
-/* This implements the svn_fs_fs__with_write_lock() 'body' callback
- type, and assumes that the write lock is held.
- BATON is a 'struct unlock_baton *'. */
static svn_error_t *
unlock_body(void *baton, apr_pool_t *pool)
{
struct unlock_baton *ub = baton;
- svn_lock_t *lock;
+ svn_fs_root_t *root;
+ svn_revnum_t youngest;
+ const char *rev_0_path;
+ int i, max_components = 0, outstanding = 0;
+ apr_pool_t *iterpool = svn_pool_create(pool);
+
+ ub->infos = apr_array_make(ub->result_pool, ub->targets->nelts,
+ sizeof(struct 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));
+
+ for (i = 0; i < ub->targets->nelts; ++i)
+ {
+ const svn_sort__item_t *item = &APR_ARRAY_IDX(ub->targets, i,
+ svn_sort__item_t);
+ const char *token = item->value;
+ struct unlock_info_t info;
+
+ svn_pool_clear(iterpool);
+
+ info.path = item->key;
+ 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;
+
+ ++outstanding;
+ }
+ APR_ARRAY_PUSH(ub->infos, struct unlock_info_t) = info;
+ }
- /* This could return SVN_ERR_FS_BAD_LOCK_TOKEN or SVN_ERR_FS_LOCK_EXPIRED. */
- SVN_ERR(get_lock(&lock, ub->fs, ub->path, TRUE, TRUE, pool));
+ rev_0_path = svn_fs_fs__path_rev_absolute(ub->fs, 0, pool);
- /* Unless breaking the lock, we do some checks. */
- if (! ub->break_lock)
+ for (i = max_components; i >= 0; --i)
{
- /* Sanity check: the incoming token should match lock->token. */
- if (strcmp(ub->token, lock->token) != 0)
- return SVN_FS__ERR_NO_SUCH_LOCK(ub->fs, lock->path);
+ const char *last_path = NULL;
+ apr_array_header_t *paths;
+ int j;
- /* There better be a username attached to the fs. */
- if (! (ub->fs->access_ctx && ub->fs->access_ctx->username))
- return SVN_FS__ERR_NO_USER(ub->fs);
+ svn_pool_clear(iterpool);
+ paths = apr_array_make(pool, 1, sizeof(const char *));
- /* And that username better be the same as the lock's owner. */
- if (strcmp(ub->fs->access_ctx->username, lock->owner) != 0)
- return SVN_FS__ERR_LOCK_OWNER_MISMATCH(
- ub->fs, ub->fs->access_ctx->username, lock->owner);
+ for (j = 0; j < ub->infos->nelts; ++j)
+ {
+ struct unlock_info_t *info = &APR_ARRAY_IDX(ub->infos, j,
+ struct unlock_info_t);
+
+ if (!info->fs_err && info->path)
+ {
+
+ if (info->components == i)
+ {
+ SVN_ERR(delete_lock(ub->fs->path, info->path, iterpool));
+ }
+ else if (info->components > i)
+ {
+ apr_size_t len = info->component - info->path;
+
+ if (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));
+ }
}
- /* Remove lock and lock token files. */
- return delete_lock(ub->fs, lock, pool);
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+unlock_single(svn_fs_t *fs,
+ svn_lock_t *lock,
+ apr_pool_t *pool)
+{
+ struct unlock_baton ub;
+ svn_sort__item_t item;
+ apr_array_header_t *targets = apr_array_make(pool, 1,
+ sizeof(svn_sort__item_t));
+ item.key = lock->path;
+ item.klen = strlen(item.key);
+ item.value = (char*)lock->token;
+ APR_ARRAY_PUSH(targets, svn_sort__item_t) = item;
+
+ ub.fs = fs;
+ ub.targets = targets;
+ ub.skip_check = TRUE;
+ ub.result_pool = pool;
+
+ SVN_ERR(unlock_body(&ub, pool));
+
+ return SVN_NO_ERROR;
}
/*** Public API implementations ***/
svn_error_t *
-svn_fs_fs__lock(svn_lock_t **lock_p,
+svn_fs_fs__lock(apr_hash_t **results,
svn_fs_t *fs,
- const char *path,
- const char *token,
+ apr_hash_t *targets,
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_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
struct lock_baton lb;
+ apr_array_header_t *sorted_targets;
+ apr_hash_t *cannonical_targets = apr_hash_make(scratch_pool);
+ apr_hash_index_t *hi;
+ svn_error_t *err;
+ int i;
+
+ *results = apr_hash_make(result_pool);
SVN_ERR(svn_fs__check_fs(fs, TRUE));
- path = svn_fs__canonicalize_abspath(path, pool);
- lb.lock_p = lock_p;
+ /* We need to have a username attached to the fs. */
+ if (!fs->access_ctx || !fs->access_ctx->username)
+ return SVN_FS__ERR_NO_USER(fs);
+
+ /* The FS locking API allows both cannonical and non-cannonical
+ paths which means that the same cannonical path could be
+ represented more than once in the TARGETS hash. We just keep
+ one, choosing one with a token if possible. */
+ for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi))
+ {
+ const char *path = svn__apr_hash_index_key(hi);
+ const svn_fs_lock_target_t *target = svn__apr_hash_index_val(hi);
+ const svn_fs_lock_target_t *other;
+
+ path = svn_fspath__canonicalize(path, result_pool);
+ other = svn_hash_gets(cannonical_targets, path);
+
+ if (!other || (!other->token && target->token))
+ svn_hash_sets(cannonical_targets, path, target);
+ }
+
+ sorted_targets = svn_sort__hash(cannonical_targets,
+ svn_sort_compare_items_as_paths,
+ scratch_pool);
+
lb.fs = fs;
- lb.path = path;
- lb.token = token;
+ lb.targets = sorted_targets;
lb.comment = comment;
lb.is_dav_comment = is_dav_comment;
lb.expiration_date = expiration_date;
- lb.current_rev = current_rev;
lb.steal_lock = steal_lock;
- lb.pool = pool;
+ lb.result_pool = result_pool;
+
+ err = svn_fs_fs__with_write_lock(fs, lock_body, &lb, scratch_pool);
+ for (i = 0; i < lb.infos->nelts; ++i)
+ {
+ struct lock_info_t *info = &APR_ARRAY_IDX(lb.infos, i,
+ struct lock_info_t);
+ svn_fs_lock_result_t *result
+ = apr_palloc(result_pool, sizeof(svn_fs_lock_result_t));
+
+ result->lock = info->lock;
+ result->err = info->fs_err;
+
+ svn_hash_sets(*results, info->path, result);
+ }
- return svn_fs_fs__with_write_lock(fs, lock_body, &lb, pool);
+ return err;
}
@@ -962,25 +1252,67 @@ svn_fs_fs__generate_lock_token(const cha
return SVN_NO_ERROR;
}
-
svn_error_t *
-svn_fs_fs__unlock(svn_fs_t *fs,
- const char *path,
- const char *token,
+svn_fs_fs__unlock(apr_hash_t **results,
+ svn_fs_t *fs,
+ apr_hash_t *targets,
svn_boolean_t break_lock,
- apr_pool_t *pool)
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
struct unlock_baton ub;
+ apr_array_header_t *sorted_targets;
+ apr_hash_t *cannonical_targets = apr_hash_make(scratch_pool);
+ apr_hash_index_t *hi;
+ svn_error_t *err;
+ int i;
+
+ *results = apr_hash_make(result_pool);
SVN_ERR(svn_fs__check_fs(fs, TRUE));
- path = svn_fs__canonicalize_abspath(path, pool);
+
+ /* We need to have a username attached to the fs. */
+ if (!fs->access_ctx || !fs->access_ctx->username)
+ return SVN_FS__ERR_NO_USER(fs);
+
+ for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi))
+ {
+ const char *path = svn__apr_hash_index_key(hi);
+ const char *token = svn__apr_hash_index_val(hi);
+ const char *other;
+
+ path = svn_fspath__canonicalize(path, result_pool);
+ other = svn_hash_gets(cannonical_targets, path);
+
+ if (!other)
+ svn_hash_sets(cannonical_targets, path, token);
+ }
+
+ sorted_targets = svn_sort__hash(cannonical_targets,
+ svn_sort_compare_items_as_paths,
+ scratch_pool);
ub.fs = fs;
- ub.path = path;
- ub.token = token;
+ ub.targets = sorted_targets;
+ ub.skip_check = FALSE;
ub.break_lock = break_lock;
+ ub.result_pool = result_pool;
+
+ err = svn_fs_fs__with_write_lock(fs, unlock_body, &ub, scratch_pool);
+ for (i = 0; i < ub.infos->nelts; ++i)
+ {
+ struct unlock_info_t *info = &APR_ARRAY_IDX(ub.infos, i,
+ struct unlock_info_t);
+ svn_fs_lock_result_t *result
+ = apr_palloc(result_pool, sizeof(svn_fs_lock_result_t));
+
+ result->lock = NULL;
+ result->err = info->fs_err;
+
+ svn_hash_sets(*results, info->path, result);
+ }
- return svn_fs_fs__with_write_lock(fs, unlock_body, &ub, pool);
+ return err;
}
Modified: subversion/branches/fsfs-lock-many/subversion/libsvn_fs_fs/lock.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-lock-many/subversion/libsvn_fs_fs/lock.h?rev=1571812&r1=1571811&r2=1571812&view=diff
==============================================================================
--- subversion/branches/fsfs-lock-many/subversion/libsvn_fs_fs/lock.h (original)
+++ subversion/branches/fsfs-lock-many/subversion/libsvn_fs_fs/lock.h Tue Feb 25 20:18:18 2014
@@ -32,26 +32,26 @@ extern "C" {
/* These functions implement some of the calls in the FS loader
library's fs vtables. */
-svn_error_t *svn_fs_fs__lock(svn_lock_t **lock,
+svn_error_t *svn_fs_fs__lock(apr_hash_t **results,
svn_fs_t *fs,
- const char *path,
- const char *token,
+ apr_hash_t *targets,
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_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
svn_error_t *svn_fs_fs__generate_lock_token(const char **token,
svn_fs_t *fs,
apr_pool_t *pool);
-svn_error_t *svn_fs_fs__unlock(svn_fs_t *fs,
- const char *path,
- const char *token,
+svn_error_t *svn_fs_fs__unlock(apr_hash_t **results,
+ svn_fs_t *fs,
+ apr_hash_t *targets,
svn_boolean_t break_lock,
- apr_pool_t *pool);
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
svn_error_t *svn_fs_fs__get_lock(svn_lock_t **lock,
svn_fs_t *fs,
Modified: subversion/branches/fsfs-lock-many/subversion/libsvn_repos/fs-wrap.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-lock-many/subversion/libsvn_repos/fs-wrap.c?rev=1571812&r1=1571811&r2=1571812&view=diff
==============================================================================
--- subversion/branches/fsfs-lock-many/subversion/libsvn_repos/fs-wrap.c (original)
+++ subversion/branches/fsfs-lock-many/subversion/libsvn_repos/fs-wrap.c Tue Feb 25 20:18:18 2014
@@ -500,114 +500,254 @@ svn_repos_fs_revision_proplist(apr_hash_
}
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_lock2(apr_hash_t **results,
+ 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,
+ apr_pool_t *pool)
{
svn_error_t *err;
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(pool);
+ apr_hash_index_t *hi;
+ apr_array_header_t *paths;
+ apr_hash_t *pre_results;
+
+ *results = apr_hash_make(pool);
/* 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;
-
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(pool, targets); hi; hi = apr_hash_next(hi))
+ {
+ const char *new_token;
+ svn_fs_lock_target_t *target;
+ const char *path = svn__apr_hash_index_key(hi);
+
+ err = svn_repos__hooks_pre_lock(repos, hooks_env, &new_token, path,
+ username, comment, steal_lock, pool);
+ if (err)
+ {
+ svn_fs_lock_result_t *result
+ = apr_palloc(pool, sizeof(svn_fs_lock_result_t));
+ result->lock = NULL;
+ result->err = err;
+ svn_hash_sets(*results, path, result);
+ continue;
+ }
- return SVN_NO_ERROR;
+ target = svn__apr_hash_index_val(hi);
+ if (*new_token)
+ target->token = new_token;
+ svn_hash_sets(pre_targets, path, target);
+ }
+
+ err = svn_fs_lock2(&pre_results, repos->fs, pre_targets, comment,
+ is_dav_comment, expiration_date, steal_lock,
+ pool, pool);
+
+ /* Combine results so the caller can handle all the errors. */
+ for (hi = apr_hash_first(pool, pre_results); hi; hi = apr_hash_next(hi))
+ svn_hash_sets(*results, svn__apr_hash_index_key(hi),
+ svn__apr_hash_index_val(hi));
+
+ /* If there are locks and an error should we return or run the post-lock? */
+ if (err)
+ return svn_error_trace(err);
+
+ /* Extract paths that were successfully locked for the post-lock. */
+ paths = apr_array_make(pool, apr_hash_count(pre_results),
+ sizeof(const char *));
+ for (hi = apr_hash_first(pool, pre_results); hi; hi = apr_hash_next(hi))
+ {
+ const char *path = svn__apr_hash_index_key(hi);
+ svn_fs_lock_result_t *result = svn__apr_hash_index_val(hi);
+
+ if (result->lock)
+ APR_ARRAY_PUSH(paths, const char *) = path;
+ }
+
+ err = svn_repos__hooks_post_lock(repos, hooks_env, paths, username, pool);
+ if (err)
+ err = svn_error_create(SVN_ERR_REPOS_POST_LOCK_HOOK_FAILED, err,
+ "Locking succeeded, but post-lock hook failed");
+
+ return err;
+}
+
+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)
+{
+ apr_hash_t *targets = apr_hash_make(pool), *results;
+ svn_fs_lock_target_t target;
+ svn_error_t *err;
+
+ target.token = token;
+ target.current_rev = current_rev;
+ svn_hash_sets(targets, path, &target);
+
+ err = svn_repos_fs_lock2(&results, repos, targets, comment, is_dav_comment,
+ expiration_date, steal_lock,
+ pool);
+
+ if (apr_hash_count(results))
+ {
+ const svn_fs_lock_result_t *result
+ = svn__apr_hash_index_val(apr_hash_first(pool, results));
+
+ if (result->lock)
+ *lock = result->lock;
+
+ if (err && result->err)
+ svn_error_compose(err, result->err);
+ else if (!err)
+ err = result->err;
+ }
+
+ return 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)
+svn_repos_fs_unlock2(apr_hash_t **results,
+ svn_repos_t *repos,
+ apr_hash_t *targets,
+ svn_boolean_t break_lock,
+ apr_pool_t *pool)
{
svn_error_t *err;
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(pool);
+ apr_hash_index_t *hi;
+ apr_array_header_t *paths;
+ apr_hash_t *pre_results;
+
+ * results = apr_hash_make(pool);
/* 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;
-
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_unlock2() from happening for that path. */
+ for (hi = apr_hash_first(pool, targets); hi; hi = apr_hash_next(hi))
+ {
+ const char *path = svn__apr_hash_index_key(hi);
+ const char *token = svn__apr_hash_index_val(hi);
- /* Unlock. */
- SVN_ERR(svn_fs_unlock(repos->fs, path, token, break_lock, pool));
+ err = svn_repos__hooks_pre_unlock(repos, hooks_env, path, username, token,
+ break_lock, pool);
+ if (err)
+ {
+ svn_fs_lock_result_t *result
+ = apr_palloc(pool, sizeof(svn_fs_lock_result_t));
+ result->lock = NULL;
+ result->err = err;
+ svn_hash_sets(*results, path, result);
+ continue;
+ }
+
+ svn_hash_sets(pre_targets, path, token);
+ }
+
+ err = svn_fs_unlock2(&pre_results, repos->fs, pre_targets, break_lock,
+ pool, pool);
+
+ /* Combine results for all paths. */
+ for (hi = apr_hash_first(pool, pre_results); hi; hi = apr_hash_next(hi))
+ svn_hash_sets(*results, svn__apr_hash_index_key(hi),
+ svn__apr_hash_index_val(hi));
+
+ if (err)
+ return svn_error_trace(err);
+
+ /* Extract paths that were successfully unlocked for the post-unlock. */
+ paths = apr_array_make(pool, apr_hash_count(pre_results),
+ sizeof(const char *));
+ for (hi = apr_hash_first(pool, pre_results); hi; hi = apr_hash_next(hi))
+ {
+ const char *path = svn__apr_hash_index_key(hi);
+ svn_fs_lock_result_t *result = svn__apr_hash_index_val(hi);
+
+ if (result->lock)
+ APR_ARRAY_PUSH(paths, const char *) = path;
+ }
+
- /* 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_error_create(SVN_ERR_REPOS_POST_UNLOCK_HOOK_FAILED, err,
+ _("Unlock succeeded, but post-unlock hook failed"));
- return SVN_NO_ERROR;
+ return 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), *results;
+ svn_error_t *err;
+
+ if (!token)
+ token = "";
+
+ svn_hash_sets(targets, path, token);
+
+ err = svn_repos_fs_unlock2(&results, repos, targets, break_lock, pool);
+
+ if (apr_hash_count(results))
+ {
+ const svn_fs_lock_result_t *result
+ = svn__apr_hash_index_val(apr_hash_first(pool, results));
+
+ if (err && result->err)
+ svn_error_compose(err, result->err);
+ else if (!err)
+ err = result->err;
+ }
+
+ return err;
}
Modified: subversion/branches/fsfs-lock-many/subversion/libsvn_repos/repos.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-lock-many/subversion/libsvn_repos/repos.c?rev=1571812&r1=1571811&r2=1571812&view=diff
==============================================================================
--- subversion/branches/fsfs-lock-many/subversion/libsvn_repos/repos.c (original)
+++ subversion/branches/fsfs-lock-many/subversion/libsvn_repos/repos.c Tue Feb 25 20:18:18 2014
@@ -807,18 +807,16 @@ PREWRITTEN_HOOKS_TEXT
"# [1] REPOS-PATH (the path to this repository)" NL
"# [2] USER (the user who created the lock)" NL
"#" NL
-"# The paths that were just locked are passed to the hook via STDIN (as" NL
-"# of Subversion 1.2, only one path is passed per invocation, but the" NL
-"# plan is to pass all locked paths at once, so the hook program" NL
-"# should be written accordingly)." NL
+"# The paths that were just locked are passed to the hook via STDIN." NL
"#" NL
"# The default working directory for the invocation is undefined, so" NL
"# the program should set one explicitly if it cares." NL
"#" NL
-"# Because the lock has already been created and cannot be undone," NL
+"# Because the locks have already been created and cannot be undone," NL
"# the exit code of the hook program is ignored. The hook program" NL
-"# can use the 'svnlook' utility to help it examine the" NL
-"# newly-created lock." NL
+"# can use the 'svnlook' utility to examine the paths in the repository" NL
+"# but since the hook is invoked asyncronously the newly-created locks" NL
+"# may no longer be present." NL
"#" NL
"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL
"# invoke other programs to do the real work, though it may do the" NL
@@ -870,10 +868,7 @@ PREWRITTEN_HOOKS_TEXT
"# [1] REPOS-PATH (the path to this repository)" NL
"# [2] USER (the user who destroyed the lock)" NL
"#" NL
-"# The paths that were just unlocked are passed to the hook via STDIN" NL
-"# (as of Subversion 1.2, only one path is passed per invocation, but" NL
-"# the plan is to pass all unlocked paths at once, so the hook program" NL
-"# should be written accordingly)." NL
+"# The paths that were just unlocked are passed to the hook via STDIN." NL
"#" NL
"# The default working directory for the invocation is undefined, so" NL
"# the program should set one explicitly if it cares." NL
Modified: subversion/branches/fsfs-lock-many/subversion/tests/libsvn_fs/locks-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-lock-many/subversion/tests/libsvn_fs/locks-test.c?rev=1571812&r1=1571811&r2=1571812&view=diff
==============================================================================
--- subversion/branches/fsfs-lock-many/subversion/tests/libsvn_fs/locks-test.c (original)
+++ subversion/branches/fsfs-lock-many/subversion/tests/libsvn_fs/locks-test.c Tue Feb 25 20:18:18 2014
@@ -28,6 +28,7 @@
#include "svn_error.h"
#include "svn_fs.h"
+#include "svn_hash.h"
#include "../svn_test_fs.h"
@@ -787,6 +788,94 @@ lock_out_of_date(const svn_test_opts_t *
}
+static svn_error_t *
+lock_multiple_paths(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_fs_t *fs;
+ svn_fs_txn_t *txn;
+ svn_fs_root_t *root, *txn_root;
+ const char *conflict;
+ svn_revnum_t newrev;
+ svn_fs_access_t *access;
+ svn_fs_lock_target_t target;
+ apr_hash_t *paths, *results;
+ svn_fs_lock_result_t *result;
+ svn_lock_t *lock;
+
+ SVN_ERR(create_greek_fs(&fs, &newrev, "test-lock-multiple-paths",
+ opts, pool));
+ SVN_ERR(svn_fs_create_access(&access, "bubba", pool));
+ SVN_ERR(svn_fs_set_access(fs, access));
+ SVN_ERR(svn_fs_revision_root(&root, fs, newrev, pool));
+ SVN_ERR(svn_fs_begin_txn2(&txn, fs, newrev, SVN_FS_TXN_CHECK_LOCKS, pool));
+ SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
+ SVN_ERR(svn_fs_make_dir(txn_root, "/A/BB", pool));
+ SVN_ERR(svn_fs_make_dir(txn_root, "/A/BBB", pool));
+ SVN_ERR(svn_fs_copy(root, "/A/mu", txn_root, "/A/BB/mu", pool));
+ SVN_ERR(svn_fs_copy(root, "/A/mu", txn_root, "/A/BBB/mu", pool));
+ SVN_ERR(svn_fs_commit_txn(&conflict, &newrev, txn, pool));
+
+ paths = apr_hash_make(pool);
+ target.token = NULL;
+ target.current_rev = newrev;
+ svn_hash_sets(paths, "/A/B/E/alpha", &target);
+ svn_hash_sets(paths, "/A/B/E/beta", &target);
+ svn_hash_sets(paths, "/A/B/E/zulu", &target);
+ svn_hash_sets(paths, "/A/BB/mu", &target);
+ svn_hash_sets(paths, "/A/BBB/mu", &target);
+ svn_hash_sets(paths, "/A/D/G/pi", &target);
+ svn_hash_sets(paths, "/A/D/G/rho", &target);
+ svn_hash_sets(paths, "/A/mu", &target);
+ svn_hash_sets(paths, "/X/zulu", &target);
+
+#define EXPECT_LOCK(path) \
+ result = svn_hash_gets(results, (path)); \
+ SVN_TEST_ASSERT(result && result->lock && !result->err); \
+ SVN_ERR(svn_fs_get_lock(&lock, fs, (path), pool)); \
+ SVN_TEST_ASSERT(lock)
+
+#define EXPECT_ERROR(path) \
+ result = svn_hash_gets(results, (path)); \
+ SVN_TEST_ASSERT(result && !result->lock && result->err); \
+ svn_error_clear(result->err); \
+ SVN_ERR(svn_fs_get_lock(&lock, fs, (path), pool)); \
+ SVN_TEST_ASSERT(!lock)
+
+ SVN_ERR(svn_fs_lock2(&results, fs, paths, "comment", 0, 0, 0, pool, pool));
+
+ EXPECT_LOCK("/A/B/E/alpha");
+ EXPECT_LOCK("/A/B/E/beta");
+ EXPECT_ERROR("/A/B/E/zulu");
+ EXPECT_LOCK("/A/BB/mu");
+ EXPECT_LOCK("/A/BBB/mu");
+ EXPECT_LOCK("/A/D/G/pi");
+ EXPECT_LOCK("/A/D/G/rho");
+ EXPECT_LOCK("/A/mu");
+ EXPECT_ERROR("/X/zulu");
+
+#define EXPECT_NO_ERROR(path) \
+ result = svn_hash_gets(results, (path)); \
+ SVN_TEST_ASSERT(result && !result->err)
+
+ SVN_ERR(svn_fs_unlock2(&results, fs, paths, TRUE, pool, pool));
+
+ EXPECT_NO_ERROR("/A/B/E/alpha");
+ EXPECT_NO_ERROR("/A/B/E/beta");
+ EXPECT_ERROR("/A/B/E/zulu");
+ EXPECT_NO_ERROR("/A/BB/mu");
+ EXPECT_NO_ERROR("/A/BBB/mu");
+ EXPECT_NO_ERROR("/A/D/G/pi");
+ EXPECT_NO_ERROR("/A/D/G/rho");
+ EXPECT_NO_ERROR("/A/mu");
+ EXPECT_ERROR("/X/zulu");
+
+ return SVN_NO_ERROR;
+
+#undef EXPECT_LOCK
+#undef EXPECT_ERROR
+#undef EXPECT_NO_ERROR
+}
/* ------------------------------------------------------------------------ */
@@ -819,5 +908,7 @@ struct svn_test_descriptor_t test_funcs[
"breaking, stealing, refreshing a lock"),
SVN_TEST_OPTS_PASS(lock_out_of_date,
"check out-of-dateness before locking"),
+ SVN_TEST_OPTS_PASS(lock_multiple_paths,
+ "lock multiple paths"),
SVN_TEST_NULL
};