Author: philip
Date: Tue Feb 26 20:03:36 2013
New Revision: 1450382
URL: http://svn.apache.org/r1450382
Log:
Ensure both move source and destination are locked when resolving
conflicts.
* subversion/include/private/svn_wc_private.h
* subversion/libsvn_wc/lock.c
(svn_wc__acquire_write_lock_for_resolve): New.
* subversion/libsvn_client/resolved.c
(svn_client_resolve): Use new lock acquire function.
* subversion/libsvn_wc/wc-queries.sql
(STMT_SELECT_MOVED_OUTSIDE): Select moved_to.
* subversion/libsvn_wc/wc_db.h
(svn_wc__required_lock_for_resolve): New.
* subversion/libsvn_wc/wc_db_update_move.c
(update_moved_away_conflict_victim): Verify that lock is held on
destination.
(svn_wc__db_update_moved_away_conflict_victim): Verify that lock is held
on source.
(required_lock_for_conflict, required_lock_for_resolve,
svn_wc__required_lock_for_resolve): New.
* subversion/tests/libsvn_wc/utils.c
(sbox_wc_resolve): Acquire and release locks.
Modified:
subversion/trunk/subversion/include/private/svn_wc_private.h
subversion/trunk/subversion/libsvn_client/resolved.c
subversion/trunk/subversion/libsvn_wc/lock.c
subversion/trunk/subversion/libsvn_wc/wc-queries.sql
subversion/trunk/subversion/libsvn_wc/wc_db.h
subversion/trunk/subversion/libsvn_wc/wc_db_update_move.c
subversion/trunk/subversion/tests/libsvn_wc/utils.c
Modified: subversion/trunk/subversion/include/private/svn_wc_private.h
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/include/private/svn_wc_private.h?rev=1450382&r1=1450381&r2=1450382&view=diff
==============================================================================
--- subversion/trunk/subversion/include/private/svn_wc_private.h (original)
+++ subversion/trunk/subversion/include/private/svn_wc_private.h Tue Feb 26
20:03:36 2013
@@ -1845,6 +1845,17 @@ svn_wc__complete_directory_add(svn_wc_co
svn_revnum_t copyfrom_rev,
apr_pool_t *scratch_pool);
+
+/* Acquire a write lock on LOCAL_ABSPATH or an ancestor that covers
+ all possible paths affected by resolving the conflicts in the tree
+ LOCAL_ABSPATH. Set *LOCK_ROOT_ABSPATH to the path of the lock
+ obtained. */
+svn_error_t *
+svn_wc__acquire_write_lock_for_resolve(const char **lock_root_abspath,
+ svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
#ifdef __cplusplus
}
#endif /* __cplusplus */
Modified: subversion/trunk/subversion/libsvn_client/resolved.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/resolved.c?rev=1450382&r1=1450381&r2=1450382&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/resolved.c (original)
+++ subversion/trunk/subversion/libsvn_client/resolved.c Tue Feb 26 20:03:36
2013
@@ -48,6 +48,8 @@ svn_client_resolve(const char *path,
apr_pool_t *pool)
{
const char *local_abspath;
+ svn_error_t *err;
+ const char *lock_abspath;
if (svn_path_is_url(path))
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
@@ -55,19 +57,25 @@ svn_client_resolve(const char *path,
SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
- SVN_WC__CALL_WITH_WRITE_LOCK(
- svn_wc__resolve_conflicts(ctx->wc_ctx, local_abspath,
- depth,
- TRUE /* resolve_text */,
- "" /* resolve_prop (ALL props) */,
- TRUE /* resolve_tree */,
- conflict_choice,
- ctx->conflict_func2,
- ctx->conflict_baton2,
- ctx->cancel_func, ctx->cancel_baton,
- ctx->notify_func2, ctx->notify_baton2,
- pool),
- ctx->wc_ctx, local_abspath, TRUE, pool);
+ /* Similar to SVN_WC__CALL_WITH_WRITE_LOCK but using a custom
+ locking function. */
- return SVN_NO_ERROR;
+ SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, ctx->wc_ctx,
+ local_abspath, pool, pool));
+ err = svn_wc__resolve_conflicts(ctx->wc_ctx, local_abspath,
+ depth,
+ TRUE /* resolve_text */,
+ "" /* resolve_prop (ALL props) */,
+ TRUE /* resolve_tree */,
+ conflict_choice,
+ ctx->conflict_func2,
+ ctx->conflict_baton2,
+ ctx->cancel_func, ctx->cancel_baton,
+ ctx->notify_func2, ctx->notify_baton2,
+ pool);
+
+ err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
+ lock_abspath,
+ pool));
+ return svn_error_trace(err);
}
Modified: subversion/trunk/subversion/libsvn_wc/lock.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/lock.c?rev=1450382&r1=1450381&r2=1450382&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/lock.c (original)
+++ subversion/trunk/subversion/libsvn_wc/lock.c Tue Feb 26 20:03:36 2013
@@ -1604,4 +1604,53 @@ svn_wc__call_with_write_lock(svn_wc__wit
}
+svn_error_t *
+svn_wc__acquire_write_lock_for_resolve(const char **lock_root_abspath,
+ svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_boolean_t locked = FALSE;
+ const char *obtained_abspath;
+ const char *requested_abspath = local_abspath;
+ while (!locked)
+ {
+ const char *required_abspath;
+ const char *child;
+
+ SVN_ERR(svn_wc__acquire_write_lock(&obtained_abspath, wc_ctx,
+ requested_abspath, FALSE,
+ scratch_pool, scratch_pool));
+ locked = TRUE;
+
+ SVN_ERR(svn_wc__required_lock_for_resolve(&required_abspath,
+ wc_ctx->db, local_abspath,
+ scratch_pool, scratch_pool));
+
+ /* It's possible for the required lock path to be an ancestor
+ of, a descendent of, or equal to, the obtained lock path. If
+ it's an ancestor we have to try again, otherwise the obtained
+ lock will do. */
+ child = svn_dirent_skip_ancestor(required_abspath, obtained_abspath);
+ if (child && child[0])
+ {
+ SVN_ERR(svn_wc__release_write_lock(wc_ctx, obtained_abspath,
+ scratch_pool));
+ locked = FALSE;
+ requested_abspath = required_abspath;
+ }
+ else
+ {
+ /* required should be a descendent of, or equal to, obtained */
+ SVN_ERR_ASSERT(!strcmp(required_abspath, obtained_abspath)
+ || svn_dirent_skip_ancestor(obtained_abspath,
+ required_abspath));
+ }
+ }
+
+ *lock_root_abspath = apr_pstrdup(result_pool, obtained_abspath);
+
+ return SVN_NO_ERROR;
+}
Modified: subversion/trunk/subversion/libsvn_wc/wc-queries.sql
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc-queries.sql?rev=1450382&r1=1450381&r2=1450382&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc-queries.sql (original)
+++ subversion/trunk/subversion/libsvn_wc/wc-queries.sql Tue Feb 26 20:03:36
2013
@@ -1521,7 +1521,7 @@ WHERE wc_id = ?1
AND moved_to IS NOT NULL
-- STMT_SELECT_MOVED_OUTSIDE
-SELECT local_relpath FROM nodes
+SELECT local_relpath, moved_to FROM nodes
WHERE wc_id = ?1
AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
AND op_depth >= ?3
Modified: subversion/trunk/subversion/libsvn_wc/wc_db.h
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db.h?rev=1450382&r1=1450381&r2=1450382&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc_db.h (original)
+++ subversion/trunk/subversion/libsvn_wc/wc_db.h Tue Feb 26 20:03:36 2013
@@ -3366,6 +3366,16 @@ svn_wc__db_resolve_break_moved_away_chil
svn_wc_notify_func2_t notify_func,
void *notify_baton,
apr_pool_t *scratch_pool);
+
+/* Set *REQUIRED_ABSPATH to the path that should be locked to ensure
+ * that the lock covers all paths affected by resolving the conflicts
+ * in the tree LOCAL_ABSPATH. */
+svn_error_t *
+svn_wc__required_lock_for_resolve(const char **required_abspath,
+ svn_wc__db_t *db,
+ const char *local_abspath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
/* @} */
Modified: subversion/trunk/subversion/libsvn_wc/wc_db_update_move.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db_update_move.c?rev=1450382&r1=1450381&r2=1450382&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc_db_update_move.c (original)
+++ subversion/trunk/subversion/libsvn_wc/wc_db_update_move.c Tue Feb 26
20:03:36 2013
@@ -1797,6 +1797,7 @@ update_moved_away_conflict_victim(svn_sk
svn_boolean_t have_row;
const char *dummy1, *dummy2, *dummy3;
int src_op_depth;
+ const char *move_root_dst_abspath;
/* ### assumes wc write lock already held */
@@ -1813,6 +1814,12 @@ update_moved_away_conflict_victim(svn_sk
svn_dirent_join(wcroot->abspath, victim_relpath,
scratch_pool),
scratch_pool));
+
+ move_root_dst_abspath
+ = svn_dirent_join(wcroot->abspath, tc_editor_baton->move_root_dst_relpath,
+ scratch_pool);
+ SVN_ERR(svn_wc__write_check(db, move_root_dst_abspath, scratch_pool));
+
tc_editor_baton->operation = operation;
tc_editor_baton->old_version= old_version;
tc_editor_baton->new_version= new_version;
@@ -1893,6 +1900,8 @@ svn_wc__db_update_moved_away_conflict_vi
db, victim_abspath,
scratch_pool, scratch_pool));
+ SVN_ERR(svn_wc__write_check(db, move_src_op_root_abspath, scratch_pool));
+
SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
db, victim_abspath,
scratch_pool, scratch_pool));
@@ -2499,3 +2508,71 @@ svn_wc__db_resolve_break_moved_away_chil
scratch_pool));
return SVN_NO_ERROR;
}
+
+static svn_error_t *
+required_lock_for_resolve(const char **required_relpath,
+ svn_wc__db_wcroot_t *wcroot,
+ const char *local_relpath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_sqlite__stmt_t *stmt;
+ svn_boolean_t have_row;
+
+ *required_relpath = local_relpath;
+
+ /* This simply looks for all moves out of the LOCAL_RELPATH tree. We
+ could attempt to limit it to only those moves that are going to
+ be resolved but that would require second guessing the resolver.
+ This simple algorithm is sufficient although it may give a
+ strictly larger/deeper lock than necessary. */
+ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+ STMT_SELECT_MOVED_OUTSIDE));
+ SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0));
+ SVN_ERR(svn_sqlite__step(&have_row, stmt));
+
+ while (have_row)
+ {
+ const char *move_dst_relpath = svn_sqlite__column_text(stmt, 1,
+ NULL);
+
+ *required_relpath
+ = svn_relpath_get_longest_ancestor(*required_relpath,
+ move_dst_relpath,
+ scratch_pool);
+
+ SVN_ERR(svn_sqlite__step(&have_row, stmt));
+ }
+ SVN_ERR(svn_sqlite__reset(stmt));
+
+ *required_relpath = apr_pstrdup(result_pool, *required_relpath);
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__required_lock_for_resolve(const char **required_abspath,
+ svn_wc__db_t *db,
+ const char *local_abspath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_wc__db_wcroot_t *wcroot;
+ const char *local_relpath;
+ const char *required_relpath;
+
+ SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
+ db, local_abspath,
+ scratch_pool, scratch_pool));
+ VERIFY_USABLE_WCROOT(wcroot);
+
+ SVN_WC__DB_WITH_TXN(
+ required_lock_for_resolve(&required_relpath, wcroot, local_relpath,
+ scratch_pool, scratch_pool),
+ wcroot);
+
+ *required_abspath = svn_dirent_join(wcroot->abspath, required_relpath,
+ result_pool);
+
+ return SVN_NO_ERROR;
+}
Modified: subversion/trunk/subversion/tests/libsvn_wc/utils.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_wc/utils.c?rev=1450382&r1=1450381&r2=1450382&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_wc/utils.c (original)
+++ subversion/trunk/subversion/tests/libsvn_wc/utils.c Tue Feb 26 20:03:36 2013
@@ -373,17 +373,27 @@ svn_error_t *
sbox_wc_resolve(svn_test__sandbox_t *b, const char *path, svn_depth_t depth,
svn_wc_conflict_choice_t conflict_choice)
{
- SVN_ERR(svn_wc__resolve_conflicts(b->wc_ctx, sbox_wc_path(b, path),
- depth,
- TRUE /* resolve_text */,
- "" /* resolve_prop (ALL props) */,
- TRUE /* resolve_tree */,
- conflict_choice,
- NULL, NULL, /* conflict func */
- NULL, NULL, /* cancellation */
- NULL, NULL, /* notification */
- b->pool));
- return SVN_NO_ERROR;
+ const char *lock_abspath;
+ svn_error_t *err;
+
+ SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, b->wc_ctx,
+ sbox_wc_path(b, path),
+ b->pool, b->pool));
+ err = svn_wc__resolve_conflicts(b->wc_ctx, sbox_wc_path(b, path),
+ depth,
+ TRUE /* resolve_text */,
+ "" /* resolve_prop (ALL props) */,
+ TRUE /* resolve_tree */,
+ conflict_choice,
+ NULL, NULL, /* conflict func */
+ NULL, NULL, /* cancellation */
+ NULL, NULL, /* notification */
+ b->pool);
+
+ err = svn_error_compose_create(err, svn_wc__release_write_lock(b->wc_ctx,
+ lock_abspath,
+ b->pool));
+ return err;
}
svn_error_t *