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 *


Reply via email to