Author: rhuijben
Date: Sat Feb 14 22:56:38 2015
New Revision: 1659867

URL: http://svn.apache.org/r1659867
Log:
Add a safety net for a few ugly corner case where users would go into error
handling and abort a commit that just succeeded, just because the commit
callback failed.

Daniel Shahaf reproduced this edge case by somehow causing a SIGPIPE in
the commit handler by producing a ^C at the right time. See todays irc
log for details.

ra-svn already had a similar safety net here.

* subversion/libsvn_ra_serf/commit.c
  (close_edit): Make sure we don't try to abort a succeeded txn, by
    setting the right variables.

* subversion/libsvn_repos/commit.c
  (close_edit): Clear some variables to avoid a theoretical case where
    we can abort a succeeded commit.

* subversion/tests/libsvn_ra/ra-test.c
  (includes): Add svn_time.h.
  (commit_callback_with_failure): New function.
  (commit_callback_failure): New function.
  (test_list): Add commit_callback_failure.

Modified:
    subversion/trunk/subversion/libsvn_ra_serf/commit.c
    subversion/trunk/subversion/libsvn_repos/commit.c
    subversion/trunk/subversion/tests/libsvn_ra/ra-test.c

Modified: subversion/trunk/subversion/libsvn_ra_serf/commit.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_serf/commit.c?rev=1659867&r1=1659866&r2=1659867&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_serf/commit.c (original)
+++ subversion/trunk/subversion/libsvn_ra_serf/commit.c Sat Feb 14 22:56:38 2015
@@ -1975,6 +1975,7 @@ close_edit(void *edit_baton,
   const char *merge_target =
     ctx->activity_url ? ctx->activity_url : ctx->txn_url;
   const svn_commit_info_t *commit_info;
+  svn_error_t *err = NULL;
 
   /* MERGE our activity */
   SVN_ERR(svn_ra_serf__run_merge(&commit_info,
@@ -1984,9 +1985,11 @@ close_edit(void *edit_baton,
                                  ctx->keep_locks,
                                  pool, pool));
 
+  ctx->txn_url = NULL; /* If HTTPv2, the txn is now done */
+
   /* Inform the WC that we did a commit.  */
   if (ctx->callback)
-    SVN_ERR(ctx->callback(commit_info, ctx->callback_baton, pool));
+    err = ctx->callback(commit_info, ctx->callback_baton, pool);
 
   /* If we're using activities, DELETE our completed activity.  */
   if (ctx->activity_url)
@@ -2003,12 +2006,16 @@ close_edit(void *edit_baton,
 
       ctx->activity_url = NULL; /* Don't try again in abort_edit() on fail */
 
-      SVN_ERR(svn_ra_serf__context_run_one(handler, pool));
+      SVN_ERR(svn_error_compose_create(
+                  err,
+                  svn_ra_serf__context_run_one(handler, pool)));
 
       if (handler->sline.code != 204)
         return svn_error_trace(svn_ra_serf__unexpected_status(handler));
     }
 
+  SVN_ERR(err);
+
   return SVN_NO_ERROR;
 }
 

Modified: subversion/trunk/subversion/libsvn_repos/commit.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_repos/commit.c?rev=1659867&r1=1659866&r2=1659867&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_repos/commit.c (original)
+++ subversion/trunk/subversion/libsvn_repos/commit.c Sat Feb 14 22:56:38 2015
@@ -780,6 +780,13 @@ close_edit(void *edit_baton,
           post_commit_err = svn_repos__post_commit_error_str(err, pool);
           svn_error_clear(err);
         }
+
+      /* Make sure a future abort doesn't perform
+         any work. This may occur if the commit
+         callback returns an error! */
+
+      eb->txn = NULL;
+      eb->txn_root = NULL;
     }
   else
     {

Modified: subversion/trunk/subversion/tests/libsvn_ra/ra-test.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_ra/ra-test.c?rev=1659867&r1=1659866&r2=1659867&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_ra/ra-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_ra/ra-test.c Sat Feb 14 22:56:38 
2015
@@ -32,6 +32,7 @@
 #include "svn_error.h"
 #include "svn_delta.h"
 #include "svn_ra.h"
+#include "svn_time.h"
 #include "svn_pools.h"
 #include "svn_cmdline.h"
 #include "svn_dirent_uri.h"
@@ -616,6 +617,52 @@ get_dir_test(const svn_test_opts_t *opts
   return SVN_NO_ERROR;
 }
 
+/* Implements svn_commit_callback2_t for commit_callback_failure() */
+static svn_error_t *
+commit_callback_with_failure(const svn_commit_info_t *info,
+                             void *baton,
+                             apr_pool_t *scratch_pool)
+{
+  apr_time_t timetemp;
+
+  SVN_TEST_ASSERT(info != NULL);
+  SVN_TEST_STRING_ASSERT(info->author, "jrandom");
+  SVN_TEST_STRING_ASSERT(info->post_commit_err, NULL);
+
+  SVN_ERR(svn_time_from_cstring(&timetemp, info->date, scratch_pool));
+  SVN_TEST_ASSERT(info->date != 0);
+  SVN_TEST_ASSERT(info->repos_root != NULL);
+  SVN_TEST_ASSERT(info->revision == 1);
+
+  return svn_error_create(SVN_ERR_CANCELLED, NULL, NULL);
+}
+
+static svn_error_t *
+commit_callback_failure(const svn_test_opts_t *opts,
+                        apr_pool_t *pool)
+{
+  svn_ra_session_t *ra_session;
+  const svn_delta_editor_t *editor;
+  void *edit_baton;
+  void *root_baton;
+  SVN_ERR(make_and_open_repos(&ra_session, "commit_cb_failure", opts, pool));
+
+  SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
+                                    apr_hash_make(pool), 
commit_callback_with_failure,
+                                    NULL, NULL, FALSE, pool));
+
+  SVN_ERR(editor->open_root(edit_baton, 1, pool, &root_baton));
+  SVN_ERR(editor->change_dir_prop(root_baton, "A",
+                                  svn_string_create("B", pool), pool));
+  SVN_ERR(editor->close_directory(root_baton, pool));
+  SVN_TEST_ASSERT_ERROR(editor->close_edit(edit_baton, pool),
+                        SVN_ERR_CANCELLED);
+
+  /* This is what users should do if close_edit fails... Except that in this 
case
+     the commit actually succeeded*/
+  SVN_ERR(editor->abort_edit(edit_baton, pool));
+  return SVN_NO_ERROR;
+}
 
 
 /* The test table.  */
@@ -635,6 +682,8 @@ static struct svn_test_descriptor_t test
                        "lock multiple paths"),
     SVN_TEST_OPTS_PASS(get_dir_test,
                        "test ra_get_dir2"),
+    SVN_TEST_OPTS_PASS(commit_callback_failure,
+                       "commit callback failure"),
     SVN_TEST_NULL
   };
 


Reply via email to