Author: rhuijben
Date: Fri Mar  6 18:56:22 2015
New Revision: 1664698

URL: http://svn.apache.org/r1664698
Log:
Make the commit editor for all ra layers behave in the same way on
encountering base revisions that are higher than the revision that the
commit is against (read: > HEAD). If callers don't have a real BASE
revision they should provide SVN_INVALID_REVNUM or fetch HEAD, instead
of just passing something above head.

* subversion/libsvn_repos/commit.c
  (dir_baton,
   file_baton): Add boolean to note that we already did a write check.
  (check_out_of_date): New function.
  (add_file_or_directory): Remember that we did a write check.
  (delete_entry): Perform base_revision check if needed.
  (apply_textdelta): Only check authz if we didn't just do that.
  (open_file): Use standard out of date check.
  (change_file_prop): Only check authz if we didn't just do that.
  (change_dir_prop): Only check authz if we didn't just do that.
    Improve out of date check.

* subversion/mod_dav_svn/repos.c
  (remove_resource): Verify that base revision makes sense.

* subversion/tests/libsvn_ra/ra-test.c
  (delete_revision_above_youngest): New test.
  (test_funcs): Add delete_revision_above_youngest.

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

Modified: subversion/trunk/subversion/libsvn_repos/commit.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_repos/commit.c?rev=1664698&r1=1664697&r2=1664698&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_repos/commit.c (original)
+++ subversion/trunk/subversion/libsvn_repos/commit.c Fri Mar  6 18:56:22 2015
@@ -124,6 +124,7 @@ struct dir_baton
   svn_revnum_t base_rev;        /* the revision I'm based on  */
   svn_boolean_t was_copied; /* was this directory added with history? */
   apr_pool_t *pool; /* my personal pool, in which I am allocated. */
+  svn_boolean_t checked_write; /* TRUE after successfull write check */
 };
 
 
@@ -131,6 +132,7 @@ struct file_baton
 {
   struct edit_baton *edit_baton;
   const char *path; /* the -absolute- path to this file in the fs */
+  svn_boolean_t checked_write; /* TRUE after successfull write check */
 };
 
 
@@ -171,6 +173,30 @@ out_of_date(const char *path, svn_node_k
                            path);
 }
 
+/* Perform an out of date check for base_rev against created rev,
+   and a sanity check of base_rev. */
+static svn_error_t *
+check_out_of_date(struct edit_baton *eb,
+                  const char *path,
+                  svn_node_kind_t kind,
+                  svn_revnum_t base_rev,
+                  svn_revnum_t created_rev)
+{
+  if (base_rev < created_rev)
+    {
+      return out_of_date(path, kind);
+    }
+  else if (base_rev > created_rev)
+    {
+      if (base_rev > svn_fs_txn_base_revision(eb->txn))
+        return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, 
+                                 _("No such revision %ld"),
+                                 base_rev);
+    }
+
+  return SVN_NO_ERROR;
+}
+
 
 static svn_error_t *
 invoke_commit_cb(svn_commit_callback2_t commit_cb,
@@ -364,14 +390,18 @@ add_file_or_directory(const char *path,
   /* Build a new child baton. */
   if (is_dir)
     {
-      *return_baton = make_dir_baton(eb, pb, full_path, was_copied,
-                                     SVN_INVALID_REVNUM, pool);
+      struct dir_baton *new_db = make_dir_baton(eb, pb, full_path, was_copied,
+                                                SVN_INVALID_REVNUM, pool);
+
+      new_db->checked_write = TRUE; /* Just created */
+      *return_baton = new_db;
     }
   else
     {
       struct file_baton *new_fb = apr_pcalloc(pool, sizeof(*new_fb));
       new_fb->edit_baton = eb;
       new_fb->path = full_path;
+      new_fb->checked_write = TRUE; /* Just created */
       *return_baton = new_fb;
     }
 
@@ -448,7 +478,6 @@ delete_entry(const char *path,
   struct dir_baton *parent = parent_baton;
   struct edit_baton *eb = parent->edit_baton;
   svn_node_kind_t kind;
-  svn_revnum_t cr_rev;
   svn_repos_authz_access_t required = svn_authz_write;
   const char *full_path;
 
@@ -473,14 +502,18 @@ delete_entry(const char *path,
 
   /* Now, make sure we're deleting the node we *think* we're
      deleting, else return an out-of-dateness error. */
-  SVN_ERR(svn_fs_node_created_rev(&cr_rev, eb->txn_root, full_path, pool));
-  if (SVN_IS_VALID_REVNUM(revision) && (revision < cr_rev))
-    return svn_error_trace(out_of_date(full_path, kind));
+  if (SVN_IS_VALID_REVNUM(revision))
+    {
+      svn_revnum_t cr_rev;
+
+      SVN_ERR(svn_fs_node_created_rev(&cr_rev, eb->txn_root, full_path, pool));
+      SVN_ERR(check_out_of_date(eb, full_path, kind, revision, cr_rev));
+    }
 
   /* This routine is a mindless wrapper.  We call svn_fs_delete()
      because that will delete files and recursively delete
      directories.  */
-  return svn_fs_delete(eb->txn_root, full_path, pool);
+  return svn_error_trace(svn_fs_delete(eb->txn_root, full_path, pool));
 }
 
 
@@ -535,18 +568,23 @@ apply_textdelta(void *file_baton,
                 void **handler_baton)
 {
   struct file_baton *fb = file_baton;
+  struct edit_baton *eb = fb->edit_baton;
 
-  /* Check for write authorization. */
-  SVN_ERR(check_authz(fb->edit_baton, fb->path,
-                      fb->edit_baton->txn_root,
-                      svn_authz_write, pool));
+  if (!fb->checked_write)
+    {
+      /* Check for write authorization. */
+      SVN_ERR(check_authz(eb, fb->path, eb->txn_root,
+                          svn_authz_write, pool));
+      fb->checked_write = TRUE;
+    }
 
-  return svn_fs_apply_textdelta(handler, handler_baton,
-                                fb->edit_baton->txn_root,
-                                fb->path,
-                                base_checksum,
-                                NULL,
-                                pool);
+  return svn_error_trace(
+          svn_fs_apply_textdelta(handler, handler_baton,
+                                 eb->txn_root,
+                                 fb->path,
+                                 base_checksum,
+                                 NULL,
+                                 pool));
 }
 
 
@@ -590,8 +628,9 @@ open_file(const char *path,
 
   /* If the node our caller has is an older revision number than the
      one in our transaction, return an out-of-dateness error. */
-  if (SVN_IS_VALID_REVNUM(base_revision) && (base_revision < cr_rev))
-    return svn_error_trace(out_of_date(full_path, svn_node_file));
+  if (SVN_IS_VALID_REVNUM(base_revision))
+    SVN_ERR(check_out_of_date(eb, full_path, svn_node_file,
+                              base_revision, cr_rev));
 
   /* Build a new file baton */
   new_fb = apr_pcalloc(pool, sizeof(*new_fb));
@@ -616,9 +655,13 @@ change_file_prop(void *file_baton,
   struct file_baton *fb = file_baton;
   struct edit_baton *eb = fb->edit_baton;
 
-  /* Check for write authorization. */
-  SVN_ERR(check_authz(eb, fb->path, eb->txn_root,
-                      svn_authz_write, pool));
+  if (!fb->checked_write)
+    {
+      /* Check for write authorization. */
+      SVN_ERR(check_authz(eb, fb->path, eb->txn_root,
+                          svn_authz_write, pool));
+      fb->checked_write = TRUE;
+    }
 
   return svn_repos_fs_change_node_prop(eb->txn_root, fb->path,
                                        name, value, pool);
@@ -663,19 +706,24 @@ change_dir_prop(void *dir_baton,
   struct edit_baton *eb = db->edit_baton;
 
   /* Check for write authorization. */
-  SVN_ERR(check_authz(eb, db->path, eb->txn_root,
-                      svn_authz_write, pool));
-
-  if (SVN_IS_VALID_REVNUM(db->base_rev))
+  if (!db->checked_write)
     {
-      /* Subversion rule:  propchanges can only happen on a directory
-         which is up-to-date. */
-      svn_revnum_t created_rev;
-      SVN_ERR(svn_fs_node_created_rev(&created_rev,
-                                      eb->txn_root, db->path, pool));
+      SVN_ERR(check_authz(eb, db->path, eb->txn_root,
+                          svn_authz_write, pool));
+
+      if (SVN_IS_VALID_REVNUM(db->base_rev))
+        {
+          /* Subversion rule:  propchanges can only happen on a directory
+             which is up-to-date. */
+          svn_revnum_t created_rev;
+          SVN_ERR(svn_fs_node_created_rev(&created_rev,
+                                          eb->txn_root, db->path, pool));
+
+          SVN_ERR(check_out_of_date(eb, db->path, svn_node_dir,
+                                    db->base_rev, created_rev));
+        }
 
-      if (db->base_rev < created_rev)
-        return svn_error_trace(out_of_date(db->path, svn_node_dir));
+      db->checked_write = TRUE; /* Skip on further prop changes */
     }
 
   return svn_repos_fs_change_node_prop(eb->txn_root, db->path,

Modified: subversion/trunk/subversion/mod_dav_svn/repos.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/mod_dav_svn/repos.c?rev=1664698&r1=1664697&r2=1664698&view=diff
==============================================================================
--- subversion/trunk/subversion/mod_dav_svn/repos.c (original)
+++ subversion/trunk/subversion/mod_dav_svn/repos.c Fri Mar  6 18:56:22 2015
@@ -4115,6 +4115,22 @@ remove_resource(dav_resource *resource,
                                       "Can't DELETE out-of-date resource",
                                       resource->pool);
         }
+      else if (resource->info->version_name > created_rev)
+        {
+          svn_revnum_t txn_base_rev;
+
+          txn_base_rev = svn_fs_txn_base_revision(resource->info->root.txn);
+          if (resource->info->version_name > txn_base_rev)
+            {
+              serr = svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
+                                       "No such revision %ld",
+                                       resource->info->version_name);
+
+              return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
+                                          "Unknown base revision",
+                                          resource->pool);
+            }
+        }
     }
 
   /* Before attempting the filesystem delete, we need to push any

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=1664698&r1=1664697&r2=1664698&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_ra/ra-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_ra/ra-test.c Fri Mar  6 18:56:22 
2015
@@ -708,6 +708,55 @@ base_revision_above_youngest(const svn_t
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+delete_revision_above_youngest(const svn_test_opts_t *opts,
+                               apr_pool_t *pool)
+{
+  svn_ra_session_t *ra_session;
+  const svn_delta_editor_t *editor;
+  svn_node_kind_t kind;
+  svn_error_t *err;
+  void *edit_baton;
+
+  SVN_ERR(make_and_open_repos(&ra_session, "delete_revision_above_youngest",
+                              opts, pool));
+
+  SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
+                                    apr_hash_make(pool), NULL,
+                                    NULL, NULL, FALSE, pool));
+
+  {
+    void *root_baton;
+    void *dir_baton;
+
+    SVN_ERR(editor->open_root(edit_baton, 0, pool, &root_baton));
+    SVN_ERR(editor->add_directory("A", root_baton, NULL, SVN_INVALID_REVNUM,
+                                  pool, &dir_baton));
+    SVN_ERR(editor->close_directory(dir_baton, pool));
+    SVN_ERR(editor->close_edit(edit_baton, pool));
+  }
+
+  SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
+                                    apr_hash_make(pool), NULL,
+                                    NULL, NULL, FALSE, pool));
+
+  {
+    void *root_baton;
+    SVN_ERR(editor->open_root(edit_baton, 1, pool, &root_baton));
+
+    /* Now we supply r2, while HEAD is r1 */
+    err = editor->delete_entry("A", 2, root_baton, pool);
+
+    if (!err)
+      err = editor->close_edit(edit_baton, pool);
+
+    SVN_TEST_ASSERT_ERROR(err,
+                          SVN_ERR_FS_NO_SUCH_REVISION);
+
+    SVN_ERR(editor->abort_edit(edit_baton, pool));
+  }
+  return SVN_NO_ERROR;
+}
 
 
 /* The test table.  */
@@ -731,6 +780,8 @@ static struct svn_test_descriptor_t test
                        "commit callback failure"),
     SVN_TEST_OPTS_PASS(base_revision_above_youngest,
                        "base revision newer than youngest"),
+    SVN_TEST_OPTS_PASS(delete_revision_above_youngest,
+                       "delete revision newer than youngest"),
     SVN_TEST_NULL
   };
 


Reply via email to