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
};