Return-Path: X-Original-To: apmail-subversion-commits-archive@minotaur.apache.org Delivered-To: apmail-subversion-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 275F6954F for ; Thu, 26 Apr 2012 20:38:01 +0000 (UTC) Received: (qmail 84356 invoked by uid 500); 26 Apr 2012 20:38:01 -0000 Delivered-To: apmail-subversion-commits-archive@subversion.apache.org Received: (qmail 84331 invoked by uid 500); 26 Apr 2012 20:38:01 -0000 Mailing-List: contact commits-help@subversion.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@subversion.apache.org Delivered-To: mailing list commits@subversion.apache.org Received: (qmail 84324 invoked by uid 99); 26 Apr 2012 20:38:01 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 26 Apr 2012 20:38:00 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 26 Apr 2012 20:37:47 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id C8E8623889E7; Thu, 26 Apr 2012 20:37:24 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1331076 [2/8] - in /subversion/branches/ev2-export: ./ build/generator/ build/generator/templates/ notes/ notes/merge-tracking/ subversion/bindings/javahl/src/org/apache/subversion/javahl/ subversion/include/ subversion/include/private/ su... Date: Thu, 26 Apr 2012 20:37:20 -0000 To: commits@subversion.apache.org From: hwright@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20120426203724.C8E8623889E7@eris.apache.org> Modified: subversion/branches/ev2-export/subversion/libsvn_delta/compat.c URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_delta/compat.c?rev=1331076&r1=1331075&r2=1331076&view=diff ============================================================================== --- subversion/branches/ev2-export/subversion/libsvn_delta/compat.c (original) +++ subversion/branches/ev2-export/subversion/libsvn_delta/compat.c Thu Apr 26 20:37:17 2012 @@ -159,15 +159,12 @@ struct ev2_file_baton enum action_code_t { - ACTION_MOVE, ACTION_MKDIR, ACTION_COPY, ACTION_PROPSET, ACTION_PUT, - ACTION_ADD, ACTION_DELETE, - ACTION_ADD_ABSENT, - ACTION_UNLOCK + ACTION_ADD_ABSENT }; struct path_action @@ -198,8 +195,17 @@ struct change_node svn_kind_t kind; /* the NEW kind of this node */ - /* The revision we're trying to change. Replace it, modify it, etc. */ - svn_revnum_t base_revision; + /* We need two revisions: one to specify the revision we are altering, + and a second to specify the revision to delete/replace. These are + mutually exclusive, but they need to be separate to ensure we don't + confuse the operation on this node. For example, we may delete a + node and replace it we use DELETING for REPLACES_REV, and ignore + the value placed into CHANGING when properties were set/changed + on the new node. Or we simply change a node (setting CHANGING), + and DELETING remains SVN_INVALID_REVNUM, indicating we are not + attempting to replace a node. */ + svn_revnum_t changing; + svn_revnum_t deleting; apr_hash_t *props; /* new/final set of props to apply */ @@ -210,6 +216,9 @@ struct change_node RESTRUCTURE must be RESTRUCTURE_ADD. */ const char *copyfrom_path; svn_revnum_t copyfrom_rev; + + /* Record whether an incoming propchange unlocked this node. */ + svn_boolean_t unlock; }; @@ -271,7 +280,8 @@ locate_change(struct ev2_edit_baton *eb, /* Return an empty change. Callers will tweak as needed. */ change = apr_pcalloc(eb->edit_pool, sizeof(*change)); - change->base_revision = SVN_INVALID_REVNUM; + change->changing = SVN_INVALID_REVNUM; + change->deleting = SVN_INVALID_REVNUM; apr_hash_set(eb->changes, relpath, APR_HASH_KEY_STRING, change); @@ -293,9 +303,10 @@ apply_propedit(struct ev2_edit_baton *eb SVN_ERR_ASSERT(change->kind == svn_kind_unknown || change->kind == kind); change->kind = kind; - SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(change->base_revision) - || change->base_revision == base_revision); - change->base_revision = base_revision; + /* We're now changing the node. Record the revision. */ + SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(change->changing) + || change->changing == base_revision); + change->changing = base_revision; if (change->props == NULL) { @@ -377,7 +388,6 @@ process_actions(struct ev2_edit_baton *e { apr_hash_t *props = NULL; svn_boolean_t need_add = FALSE; - svn_boolean_t need_delete = FALSE; svn_boolean_t need_copy = FALSE; const char *copyfrom_path; svn_revnum_t copyfrom_rev; @@ -388,7 +398,6 @@ process_actions(struct ev2_edit_baton *e svn_revnum_t props_base_revision = SVN_INVALID_REVNUM; svn_revnum_t text_base_revision = SVN_INVALID_REVNUM; svn_kind_t kind = svn_kind_unknown; - int i; if (*path == '/') { @@ -396,72 +405,68 @@ process_actions(struct ev2_edit_baton *e *eb->found_abs_paths = TRUE; } - /* Go through all of our actions, populating various datastructures - * dependent on them. */ - for (i = 0; i < actions->nelts; i++) + SVN_ERR_ASSERT(change != NULL); + if (change != NULL) { - const struct path_action *action = APR_ARRAY_IDX(actions, i, - struct path_action *); + if (change->unlock) + SVN_ERR(eb->do_unlock(eb->unlock_baton, path, scratch_pool)); + + if (change->action == RESTRUCTURE_DELETE) + { + /* If the action was left as RESTRUCTURE_DELETE, then a + replacement is not occurring. Just do the delete and bail. */ + SVN_ERR(svn_editor_delete(eb->editor, path, change->deleting)); + + /* No further work possible on this node. */ + return SVN_NO_ERROR; + } + if (change->action == RESTRUCTURE_ADD_ABSENT) + { + SVN_ERR(svn_editor_add_absent(eb->editor, path, change->kind, + change->deleting)); + + /* No further work possible on this node. */ + return SVN_NO_ERROR; + } - switch (action->action) + if (change->action == RESTRUCTURE_ADD) { - case ACTION_DELETE: + kind = change->kind; + + /* An add might be a replace. Grab the revnum we're replacing. */ + delete_revnum = change->deleting; + + if (kind == svn_kind_dir) { - delete_revnum = *((svn_revnum_t *) action->args); - need_delete = TRUE; - break; + children = get_children(eb, path, scratch_pool); } - - case ACTION_ADD: + else { - kind = *((svn_kind_t *) action->args); - need_add = TRUE; - - if (kind == svn_kind_dir) + /* If this file is copied here, then we don't need CONTENTS. + Otherwise, the contents comes from apply_txdelta() and has + been saved at CONTENTS_ABSPATH. If apply_txdelta() was not + called, then we're adding an empty file. */ + if (change->copyfrom_path == NULL + && change->contents_abspath == NULL) { - children = get_children(eb, path, scratch_pool); - } - else - { - /* The default is an empty file. */ contents = svn_stream_empty(scratch_pool); checksum = svn_checksum_empty_checksum(svn_checksum_sha1, scratch_pool); } - break; } - case ACTION_COPY: + if (change->copyfrom_path != NULL) { - struct copy_args *c_args = action->args; - - copyfrom_path = c_args->copyfrom_path; - copyfrom_rev = c_args->copyfrom_rev; need_copy = TRUE; - break; + copyfrom_path = change->copyfrom_path; + copyfrom_rev = change->copyfrom_rev; } - - case ACTION_ADD_ABSENT: - { - kind = *((svn_kind_t *) action->args); - SVN_ERR(svn_editor_add_absent(eb->editor, path, kind, - SVN_INVALID_REVNUM)); - break; - } - - case ACTION_UNLOCK: + else { - SVN_ERR(eb->do_unlock(eb->unlock_baton, path, scratch_pool)); - break; + need_add = TRUE; } - - default: - SVN_ERR_MALFUNCTION(); } - } - if (change != NULL) - { if (change->contents_abspath != NULL) { /* We can only set text on files. */ @@ -474,7 +479,7 @@ process_actions(struct ev2_edit_baton *e SVN_ERR(svn_stream_open_readonly(&contents, change->contents_abspath, scratch_pool, scratch_pool)); - text_base_revision = change->base_revision; + text_base_revision = change->changing; } if (change->props != NULL) @@ -482,20 +487,13 @@ process_actions(struct ev2_edit_baton *e /* ### validate we aren't overwriting KIND? */ kind = change->kind; props = change->props; - props_base_revision = change->base_revision; + props_base_revision = change->changing; } } /* We've now got a wholistic view of what has happened to this node, * so we can call our own editor APIs on it. */ - if (need_delete && !need_add && !need_copy) - { - /* If we're only doing a delete, do it here. */ - SVN_ERR(svn_editor_delete(eb->editor, path, delete_revnum)); - return SVN_NO_ERROR; - } - if (need_add) { if (props == NULL) @@ -512,6 +510,7 @@ process_actions(struct ev2_edit_baton *e props, delete_revnum)); } + /* No further work possible on this node. */ return SVN_NO_ERROR; } @@ -519,8 +518,14 @@ process_actions(struct ev2_edit_baton *e { SVN_ERR(svn_editor_copy(eb->editor, copyfrom_path, copyfrom_rev, path, delete_revnum)); + /* Fall through to possibly make changes post-copy. */ } +#if 0 + /* There *should* be work for this node. But it seems that isn't true + in some cases. Future investigation... */ + SVN_ERR_ASSERT(need_copy || props || contents); +#endif if (props || contents) { /* We fetched and modified the props or content in some way. Apply 'em @@ -639,30 +644,21 @@ ev2_delete_entry(const char *path, apr_pool_t *scratch_pool) { struct ev2_dir_baton *pb = parent_baton; - svn_revnum_t *revnum = apr_palloc(pb->eb->edit_pool, sizeof(*revnum)); + svn_revnum_t base_revision; const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool); struct change_node *change = locate_change(pb->eb, relpath); if (SVN_IS_VALID_REVNUM(revision)) - *revnum = revision; + base_revision = revision; else - *revnum = pb->base_revision; + base_revision = pb->base_revision; - SVN_ERR(add_action(pb->eb, relpath, ACTION_DELETE, revnum)); - - /* ### note: cannot switch to CHANGES just yet. the action loop needs - ### to see a delete action, and set NEED_DELETE. that is used for - ### the file properties. once fileprops are converted, then we - ### can fully switch over. */ - - /* ### assert that RESTRUCTURE is NONE? */ + SVN_ERR_ASSERT(change->action == RESTRUCTURE_NONE); change->action = RESTRUCTURE_DELETE; -#if 0 - SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(change->base_revision) - || change->base_revision == revision); - change->base_revision = revision; -#endif + SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(change->deleting) + || change->deleting == base_revision); + change->deleting = base_revision; return SVN_NO_ERROR; } @@ -684,6 +680,7 @@ ev2_add_directory(const char *path, /* ### assert that RESTRUCTURE is NONE or DELETE? */ change->action = RESTRUCTURE_ADD; + change->kind = svn_kind_dir; cb->eb = pb->eb; cb->path = apr_pstrdup(result_pool, relpath); @@ -692,12 +689,6 @@ ev2_add_directory(const char *path, if (!copyfrom_path) { - /* A simple add. */ - svn_kind_t *kind = apr_palloc(pb->eb->edit_pool, sizeof(*kind)); - - *kind = svn_kind_dir; - SVN_ERR(add_action(pb->eb, relpath, ACTION_ADD, kind)); - if (pb->copyfrom_relpath) { const char *name = svn_relpath_basename(relpath, scratch_pool); @@ -715,10 +706,6 @@ ev2_add_directory(const char *path, pb->eb->edit_pool); change->copyfrom_rev = copyfrom_revision; - args->copyfrom_path = change->copyfrom_path; - args->copyfrom_rev = change->copyfrom_rev; - SVN_ERR(add_action(pb->eb, relpath, ACTION_COPY, args)); - cb->copyfrom_relpath = change->copyfrom_path; cb->copyfrom_rev = change->copyfrom_rev; } @@ -784,11 +771,12 @@ ev2_absent_directory(const char *path, apr_pool_t *scratch_pool) { struct ev2_dir_baton *pb = parent_baton; - svn_kind_t *kind = apr_palloc(pb->eb->edit_pool, sizeof(*kind)); const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool); + struct change_node *change = locate_change(pb->eb, relpath); - *kind = svn_kind_dir; - SVN_ERR(add_action(pb->eb, relpath, ACTION_ADD_ABSENT, kind)); + /* ### assert that RESTRUCTURE is NONE or DELETE? */ + change->action = RESTRUCTURE_ADD_ABSENT; + change->kind = svn_kind_dir; return SVN_NO_ERROR; } @@ -810,6 +798,7 @@ ev2_add_file(const char *path, /* ### assert that RESTRUCTURE is NONE or DELETE? */ change->action = RESTRUCTURE_ADD; + change->kind = svn_kind_file; fb->eb = pb->eb; fb->path = apr_pstrdup(result_pool, relpath); @@ -818,14 +807,8 @@ ev2_add_file(const char *path, if (!copyfrom_path) { - /* A simple add. */ - svn_kind_t *kind = apr_palloc(pb->eb->edit_pool, sizeof(*kind)); - /* Don't bother fetching the base, as in an add we don't have a base. */ fb->delta_base = NULL; - - *kind = svn_kind_file; - SVN_ERR(add_action(pb->eb, relpath, ACTION_ADD, kind)); } else { @@ -841,10 +824,6 @@ ev2_add_file(const char *path, change->copyfrom_path, change->copyfrom_rev, result_pool, scratch_pool)); - - args->copyfrom_path = change->copyfrom_path; - args->copyfrom_rev = change->copyfrom_rev; - SVN_ERR(add_action(pb->eb, relpath, ACTION_COPY, args)); } return SVN_NO_ERROR; @@ -936,9 +915,9 @@ ev2_apply_textdelta(void *file_baton, change = locate_change(fb->eb, fb->path); SVN_ERR_ASSERT(change->contents_abspath == NULL); - SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(change->base_revision) - || change->base_revision == fb->base_revision); - change->base_revision = fb->base_revision; + SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(change->changing) + || change->changing == fb->base_revision); + change->changing = fb->base_revision; if (! fb->delta_base) hb->source = svn_stream_empty(handler_pool); @@ -975,7 +954,13 @@ ev2_change_file_prop(void *file_baton, { /* We special case the lock token propery deletion, which is the server's way of telling the client to unlock the path. */ - SVN_ERR(add_action(fb->eb, fb->path, ACTION_UNLOCK, NULL)); + + /* ### this duplicates much of apply_propedit(). fix in future. */ + const char *relpath = map_to_repos_relpath(fb->eb, fb->path, + scratch_pool); + struct change_node *change = locate_change(fb->eb, relpath); + + change->unlock = TRUE; } SVN_ERR(apply_propedit(fb->eb, fb->path, svn_kind_file, fb->base_revision, @@ -998,11 +983,12 @@ ev2_absent_file(const char *path, apr_pool_t *scratch_pool) { struct ev2_dir_baton *pb = parent_baton; - svn_kind_t *kind = apr_palloc(pb->eb->edit_pool, sizeof(*kind)); const char *relpath = map_to_repos_relpath(pb->eb, path, scratch_pool); + struct change_node *change = locate_change(pb->eb, relpath); - *kind = svn_kind_file; - SVN_ERR(add_action(pb->eb, relpath, ACTION_ADD_ABSENT, kind)); + /* ### assert that RESTRUCTURE is NONE or DELETE? */ + change->action = RESTRUCTURE_ADD_ABSENT; + change->kind = svn_kind_file; return SVN_NO_ERROR; } Modified: subversion/branches/ev2-export/subversion/libsvn_delta/editor.c URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_delta/editor.c?rev=1331076&r1=1331075&r2=1331076&view=diff ============================================================================== --- subversion/branches/ev2-export/subversion/libsvn_delta/editor.c (original) +++ subversion/branches/ev2-export/subversion/libsvn_delta/editor.c Thu Apr 26 20:37:17 2012 @@ -221,6 +221,13 @@ svn_editor_create(svn_editor_t **editor, } +void * +svn_editor_get_baton(const svn_editor_t *editor) +{ + return editor->baton; +} + + svn_error_t * svn_editor_setcb_add_directory(svn_editor_t *editor, svn_editor_cb_add_directory_t callback, Modified: subversion/branches/ev2-export/subversion/libsvn_fs/editor.c URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_fs/editor.c?rev=1331076&r1=1331075&r2=1331076&view=diff ============================================================================== --- subversion/branches/ev2-export/subversion/libsvn_fs/editor.c (original) +++ subversion/branches/ev2-export/subversion/libsvn_fs/editor.c Thu Apr 26 20:37:17 2012 @@ -29,43 +29,376 @@ #include "svn_editor.h" #include "svn_fs.h" +#include "svn_private_config.h" + #include "fs-loader.h" struct edit_baton { + /* The transaction associated with this editor. */ svn_fs_txn_t *txn; - svn_boolean_t no_autocommit; + + /* Has this editor been completed? */ + svn_boolean_t completed; + + /* We sometimes need the cancellation beyond what svn_editor_t provides */ + svn_cancel_func_t cancel_func; + void *cancel_baton; + + /* The pool that the txn lives within. When we create a ROOT, it will + be allocated within a subpool of this. The root will be closed in + complete/abort and that subpool will be destroyed. + + This pool SHOULD NOT be used for any allocations. */ + apr_pool_t *txn_pool; + + /* This is the root from the txn. Use get_root() to fetch/create this + member as appropriate. */ + svn_fs_root_t *root; }; +#define FSPATH(relpath, pool) apr_pstrcat(pool, "/", relpath, NULL) +#define UNUSED(x) ((void)(x)) + + +static svn_error_t * +get_root(svn_fs_root_t **root, + struct edit_baton *eb) +{ + if (eb->root == NULL) + SVN_ERR(svn_fs_txn_root(&eb->root, eb->txn, eb->txn_pool)); + *root = eb->root; + return SVN_NO_ERROR; +} + + +/* Apply each property in PROPS to the node at FSPATH in ROOT. */ +static svn_error_t * +add_new_props(svn_fs_root_t *root, + const char *fspath, + apr_hash_t *props, + apr_pool_t *scratch_pool) +{ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_hash_index_t *hi; + + /* ### it would be nice to have svn_fs_set_node_props(). but since we + ### don't... add each property to the node. this is a new node, so + ### we don't need to worry about deleting props. just adding. */ + + for (hi = apr_hash_first(scratch_pool, props); hi; + hi = apr_hash_next(hi)) + { + const char *name = svn__apr_hash_index_key(hi); + const svn_string_t *value = svn__apr_hash_index_val(hi); + + svn_pool_clear(iterpool); + + SVN_ERR(svn_fs_change_node_prop(root, fspath, name, value, iterpool)); + } + + svn_pool_destroy(iterpool); + return SVN_NO_ERROR; +} + + +/* This implements svn_editor_cb_add_directory_t */ +static svn_error_t * +add_directory_cb(void *baton, + const char *relpath, + const apr_array_header_t *children, + apr_hash_t *props, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool) +{ + struct edit_baton *eb = baton; + const char *fspath = FSPATH(relpath, scratch_pool); + svn_fs_root_t *root; + + /* Note: we ignore CHILDREN. We have no "incomplete" state to worry about, + so we don't need to be aware of what children will be created. */ + + SVN_ERR(get_root(&root, eb)); + + /* ### validate REPLACES_REV */ + + SVN_ERR(svn_fs_make_dir(root, fspath, scratch_pool)); + SVN_ERR(add_new_props(root, fspath, props, scratch_pool)); + + return SVN_NO_ERROR; +} + + +/* This implements svn_editor_cb_add_file_t */ +static svn_error_t * +add_file_cb(void *baton, + const char *relpath, + const svn_checksum_t *checksum, + svn_stream_t *contents, + apr_hash_t *props, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool) +{ + struct edit_baton *eb = baton; + const char *fspath = FSPATH(relpath, scratch_pool); + svn_fs_root_t *root; + svn_stream_t *fs_contents; + + SVN_ERR(get_root(&root, eb)); + + /* ### do something with CHECKSUM */ + /* ### validate REPLACES_REV */ + + SVN_ERR(svn_fs_make_file(root, fspath, scratch_pool)); + + /* ### We probably don't have an MD5 checksum, so no digest is available + ### for svn_fs_apply_text() to validate. It would be nice to have an + ### FS API that takes our CONTENTS/CHECKSUM pair (and PROPS!). */ + SVN_ERR(svn_fs_apply_text(&fs_contents, root, fspath, + NULL /* result_checksum */, + scratch_pool)); + SVN_ERR(svn_stream_copy3(contents, fs_contents, + eb->cancel_func, eb->cancel_baton, + scratch_pool)); + + SVN_ERR(add_new_props(root, fspath, props, scratch_pool)); + + return SVN_NO_ERROR; +} + + +/* This implements svn_editor_cb_add_symlink_t */ +static svn_error_t * +add_symlink_cb(void *baton, + const char *relpath, + const char *target, + apr_hash_t *props, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool) +{ + struct edit_baton *eb = baton; + const char *fspath = FSPATH(relpath, scratch_pool); + svn_fs_root_t *root; + svn_stream_t *fs_contents; + + SVN_ERR(get_root(&root, eb)); + + /* ### validate REPLACES_REV */ + + /* ### we probably need to construct a file with specific contents + ### (until the FS grows some symlink APIs) */ +#if 0 + SVN_ERR(svn_fs_make_file(root, fspath, scratch_pool)); + SVN_ERR(svn_fs_apply_text(&fs_contents, root, fspath, + NULL /* result_checksum */, + scratch_pool)); + /* ### SVN_ERR(svn_stream_printf(fs_contents, ..., scratch_pool)); */ + apr_hash_set(props, SVN_PROP_SPECIAL, APR_HASH_KEY_STRING, + SVN_PROP_SPECIAL_VALUE); + + SVN_ERR(add_new_props(root, fspath, props, scratch_pool)); +#endif + UNUSED(fspath); UNUSED(fs_contents); + + SVN__NOT_IMPLEMENTED(); +} + + +/* This implements svn_editor_cb_add_absent_t */ +static svn_error_t * +add_absent_cb(void *baton, + const char *relpath, + svn_kind_t kind, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool) +{ + /* This is a programming error. Code should not attempt to create these + kinds of nodes within the FS. */ + return svn_error_create( + SVN_ERR_UNSUPPORTED_FEATURE, NULL, + N_("The filesystem does not support 'absent' nodes")); +} + + +/* This implements svn_editor_cb_alter_directory_t */ +static svn_error_t * +alter_directory_cb(void *baton, + const char *relpath, + svn_revnum_t revision, + apr_hash_t *props, + apr_pool_t *scratch_pool) +{ + struct edit_baton *eb = baton; + + UNUSED(eb); SVN__NOT_IMPLEMENTED(); +} + + +/* This implements svn_editor_cb_alter_file_t */ +static svn_error_t * +alter_file_cb(void *baton, + const char *relpath, + svn_revnum_t revision, + apr_hash_t *props, + const svn_checksum_t *checksum, + svn_stream_t *contents, + apr_pool_t *scratch_pool) +{ + struct edit_baton *eb = baton; + + UNUSED(eb); SVN__NOT_IMPLEMENTED(); +} + + +/* This implements svn_editor_cb_alter_symlink_t */ +static svn_error_t * +alter_symlink_cb(void *baton, + const char *relpath, + svn_revnum_t revision, + apr_hash_t *props, + const char *target, + apr_pool_t *scratch_pool) +{ + struct edit_baton *eb = baton; + + UNUSED(eb); SVN__NOT_IMPLEMENTED(); +} + + +/* This implements svn_editor_cb_delete_t */ +static svn_error_t * +delete_cb(void *baton, + const char *relpath, + svn_revnum_t revision, + apr_pool_t *scratch_pool) +{ + struct edit_baton *eb = baton; + + UNUSED(eb); SVN__NOT_IMPLEMENTED(); +} + + +/* This implements svn_editor_cb_copy_t */ +static svn_error_t * +copy_cb(void *baton, + const char *src_relpath, + svn_revnum_t src_revision, + const char *dst_relpath, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool) +{ + struct edit_baton *eb = baton; + + UNUSED(eb); SVN__NOT_IMPLEMENTED(); +} + + +/* This implements svn_editor_cb_move_t */ +static svn_error_t * +move_cb(void *baton, + const char *src_relpath, + svn_revnum_t src_revision, + const char *dst_relpath, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool) +{ + struct edit_baton *eb = baton; + + UNUSED(eb); SVN__NOT_IMPLEMENTED(); +} + + +/* This implements svn_editor_cb_rotate_t */ +static svn_error_t * +rotate_cb(void *baton, + const apr_array_header_t *relpaths, + const apr_array_header_t *revisions, + apr_pool_t *scratch_pool) +{ + struct edit_baton *eb = baton; + + UNUSED(eb); SVN__NOT_IMPLEMENTED(); +} + + +/* This implements svn_editor_cb_complete_t */ +static svn_error_t * +complete_cb(void *baton, + apr_pool_t *scratch_pool) +{ + struct edit_baton *eb = baton; + + /* Watch out for a following call to svn_fs_editor_commit(). Note that + we are likely here because svn_fs_editor_commit() was called, and it + invoked svn_editor_complete(). */ + eb->completed = TRUE; + + if (eb->root != NULL) + { + svn_fs_close_root(eb->root); + eb->root = NULL; + } + + return SVN_NO_ERROR; +} + + +/* This implements svn_editor_cb_abort_t */ +static svn_error_t * +abort_cb(void *baton, + apr_pool_t *scratch_pool) +{ + struct edit_baton *eb = baton; + svn_error_t *err; + + /* Don't allow a following call to svn_fs_editor_commit(). */ + eb->completed = TRUE; + + if (eb->root != NULL) + { + svn_fs_close_root(eb->root); + eb->root = NULL; + } + + /* ### should we examine the error and attempt svn_fs_purge_txn() ? */ + err = svn_fs_abort_txn(eb->txn, scratch_pool); + + /* For safety, clear the now-useless txn. */ + eb->txn = NULL; + + return svn_error_trace(err); +} + static svn_error_t * make_editor(svn_editor_t **editor, svn_fs_txn_t *txn, - svn_boolean_t no_autocommit, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { static const svn_editor_cb_many_t editor_cbs = { - NULL /* add_directory_cb */, - NULL /* add_file_cb */, - NULL /* add_symlink_cb */, - NULL /* add_absent_cb */, - NULL /* alter_directory_cb */, - NULL /* alter_file_cb */, - NULL /* alter_symlink_cb */, - NULL /* delete_cb */, - NULL /* copy_cb */, - NULL /* move_cb */, - NULL /* rotate_cb */, - NULL /* complete_cb */, - NULL /* abort_cb */ + add_directory_cb, + add_file_cb, + add_symlink_cb, + add_absent_cb, + alter_directory_cb, + alter_file_cb, + alter_symlink_cb, + delete_cb, + copy_cb, + move_cb, + rotate_cb, + complete_cb, + abort_cb }; - struct edit_baton *eb = apr_palloc(result_pool, sizeof(*eb)); + struct edit_baton *eb = apr_pcalloc(result_pool, sizeof(*eb)); eb->txn = txn; - eb->no_autocommit = no_autocommit; + eb->cancel_func = cancel_func; + eb->cancel_baton = cancel_baton; + eb->txn_pool = result_pool; SVN_ERR(svn_editor_create(editor, eb, cancel_func, cancel_baton, result_pool, scratch_pool)); @@ -79,19 +412,19 @@ svn_error_t * svn_fs_editor_create(svn_editor_t **editor, const char **txn_name, svn_fs_t *fs, - svn_revnum_t revision, apr_uint32_t flags, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { + svn_revnum_t revision; svn_fs_txn_t *txn; - svn_boolean_t no_autocommit = (flags & SVN_FS_TXN_NO_AUTOCOMMIT) != 0; + SVN_ERR(svn_fs_youngest_rev(&revision, fs, scratch_pool)); SVN_ERR(svn_fs_begin_txn2(&txn, fs, revision, flags, result_pool)); SVN_ERR(svn_fs_txn_name(txn_name, txn, result_pool)); - return svn_error_trace(make_editor(editor, txn, no_autocommit, + return svn_error_trace(make_editor(editor, txn, cancel_func, cancel_baton, result_pool, scratch_pool)); } @@ -109,8 +442,84 @@ svn_fs_editor_create_for(svn_editor_t ** svn_fs_txn_t *txn; SVN_ERR(svn_fs_open_txn(&txn, fs, txn_name, result_pool)); - return svn_error_trace(make_editor(editor, txn, TRUE /* no_autocommit */, + return svn_error_trace(make_editor(editor, txn, cancel_func, cancel_baton, result_pool, scratch_pool)); } + +svn_error_t * +svn_fs_editor_commit(svn_revnum_t *revision, + svn_error_t **post_commit_err, + const char **conflict_path, + svn_editor_t *editor, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + struct edit_baton *eb = svn_editor_get_baton(editor); + const char *inner_conflict_path; + svn_error_t *err = NULL; + + /* make sure people are using the correct sequencing. */ + if (eb->completed) + return svn_error_create(SVN_ERR_FS_INCORRECT_EDITOR_COMPLETION, + NULL, NULL); + + *revision = SVN_INVALID_REVNUM; + *post_commit_err = NULL; + *conflict_path = NULL; + + /* Clean up internal resources (eg. eb->root). This also allows the + editor infrastructure to know this editor is "complete". */ + err = svn_editor_complete(editor); + + /* Note: docco for svn_fs_commit_txn() states that CONFLICT_PATH will + be allocated in the txn's pool. But it lies. Regardless, we want + it placed into RESULT_POOL. */ + + if (!err) + err = svn_fs_commit_txn(&inner_conflict_path, + revision, + eb->txn, + scratch_pool); + if (SVN_IS_VALID_REVNUM(*revision)) + { + if (err) + { + /* Case 3. ERR is a post-commit (cleanup) error. */ + + /* Pass responsibility via POST_COMMIT_ERR. */ + *post_commit_err = err; + err = SVN_NO_ERROR; + } + /* else: Case 1. */ + } + else + { + SVN_ERR_ASSERT(err != NULL); + if (err->apr_err == SVN_ERR_FS_CONFLICT) + { + /* Case 2. */ + + /* Copy this into the correct pool (see note above). */ + *conflict_path = apr_pstrdup(result_pool, inner_conflict_path); + + /* Return sucess. The caller should inspect CONFLICT_PATH to + determine this particular case. */ + svn_error_clear(err); + err = SVN_NO_ERROR; + } + /* else: Case 4. */ + + /* Abort the TXN. Nobody wants to use it. */ + /* ### should we examine the error and attempt svn_fs_purge_txn() ? */ + err = svn_error_compose_create( + err, + svn_fs_abort_txn(eb->txn, scratch_pool)); + } + + /* For safety, clear the now-useless txn. */ + eb->txn = NULL; + + return svn_error_trace(err); +} Modified: subversion/branches/ev2-export/subversion/libsvn_fs/fs-loader.c URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_fs/fs-loader.c?rev=1331076&r1=1331075&r2=1331076&view=diff ============================================================================== --- subversion/branches/ev2-export/subversion/libsvn_fs/fs-loader.c (original) +++ subversion/branches/ev2-export/subversion/libsvn_fs/fs-loader.c Thu Apr 26 20:37:17 2012 @@ -645,6 +645,7 @@ svn_error_t * svn_fs_commit_txn(const char **conflict_p, svn_revnum_t *new_rev, svn_fs_txn_t *txn, apr_pool_t *pool) { + svn_error_t *err; #ifdef PACK_AFTER_EVERY_COMMIT svn_fs_root_t *txn_root; svn_fs_t *fs; @@ -656,11 +657,25 @@ svn_fs_commit_txn(const char **conflict_ fs_path = svn_fs_path(fs, pool); #endif - SVN_ERR(txn->vtable->commit(conflict_p, new_rev, txn, pool)); + err = txn->vtable->commit(conflict_p, new_rev, txn, pool); + +#ifdef SVN_DEBUG + /* Check postconditions. */ + if (conflict_p) + { + SVN_ERR_ASSERT_E(! (SVN_IS_VALID_REVNUM(*new_rev) && *conflict_p != NULL), + err); + SVN_ERR_ASSERT_E((*conflict_p != NULL) + == (err && err->apr_err == SVN_ERR_FS_CONFLICT), + err); + } +#endif + + SVN_ERR(err); #ifdef PACK_AFTER_EVERY_COMMIT { - svn_error_t *err = svn_fs_pack(fs_path, NULL, NULL, NULL, NULL, pool); + err = svn_fs_pack(fs_path, NULL, NULL, NULL, NULL, pool); if (err && err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE) /* Pre-1.6 filesystem. */ svn_error_clear(err); @@ -1218,7 +1233,9 @@ svn_fs_get_file_delta_stream(svn_txdelta svn_error_t * svn_fs_get_uuid(svn_fs_t *fs, const char **uuid, apr_pool_t *pool) { - return svn_error_trace(fs->vtable->get_uuid(fs, uuid, pool)); + /* If you change this, consider changing svn_fs__identifier(). */ + *uuid = apr_pstrdup(pool, fs->uuid); + return SVN_NO_ERROR; } svn_error_t * Modified: subversion/branches/ev2-export/subversion/libsvn_fs/fs-loader.h URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_fs/fs-loader.h?rev=1331076&r1=1331075&r2=1331076&view=diff ============================================================================== --- subversion/branches/ev2-export/subversion/libsvn_fs/fs-loader.h (original) +++ subversion/branches/ev2-export/subversion/libsvn_fs/fs-loader.h Thu Apr 26 20:37:17 2012 @@ -172,7 +172,7 @@ typedef struct fs_vtable_t const svn_string_t *const *old_value_p, const svn_string_t *value, apr_pool_t *pool); - svn_error_t *(*get_uuid)(svn_fs_t *fs, const char **uuid, apr_pool_t *pool); + /* There is no get_uuid(); see svn_fs_t.uuid docstring. */ svn_error_t *(*set_uuid)(svn_fs_t *fs, const char *uuid, apr_pool_t *pool); svn_error_t *(*revision_root)(svn_fs_root_t **root_p, svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool); @@ -391,6 +391,9 @@ struct svn_fs_t /* FSAP-specific vtable and private data */ fs_vtable_t *vtable; void *fsap_data; + + /* UUID, stored by open(), create(), and set_uuid(). */ + const char *uuid; }; Modified: subversion/branches/ev2-export/subversion/libsvn_fs_base/fs.c URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_fs_base/fs.c?rev=1331076&r1=1331075&r2=1331076&view=diff ============================================================================== --- subversion/branches/ev2-export/subversion/libsvn_fs_base/fs.c (original) +++ subversion/branches/ev2-export/subversion/libsvn_fs_base/fs.c Thu Apr 26 20:37:17 2012 @@ -473,15 +473,6 @@ bdb_write_config(svn_fs_t *fs) -static svn_error_t * -base_serialized_init(svn_fs_t *fs, apr_pool_t *common_pool, apr_pool_t *pool) -{ - /* Nothing to do here. */ - return SVN_NO_ERROR; -} - - - /* Creating a new filesystem */ static fs_vtable_t fs_vtable = { @@ -489,7 +480,6 @@ static fs_vtable_t fs_vtable = { svn_fs_base__revision_prop, svn_fs_base__revision_proplist, svn_fs_base__change_rev_prop, - svn_fs_base__get_uuid, svn_fs_base__set_uuid, svn_fs_base__revision_root, svn_fs_base__begin_txn, @@ -656,6 +646,15 @@ open_databases(svn_fs_t *fs, } +/* Called by functions that initialize an svn_fs_t struct, after that + initialization is done, to populate svn_fs_t->uuid. */ +static svn_error_t * +populate_opened_fs(svn_fs_t *fs, apr_pool_t *scratch_pool) +{ + SVN_ERR(svn_fs_base__populate_uuid(fs, scratch_pool)); + return SVN_NO_ERROR; +} + static svn_error_t * base_create(svn_fs_t *fs, const char *path, apr_pool_t *pool, apr_pool_t *common_pool) @@ -691,7 +690,9 @@ base_create(svn_fs_t *fs, const char *pa if (svn_err) goto error; ((base_fs_data_t *) fs->fsap_data)->format = format; - return base_serialized_init(fs, common_pool, pool); + + SVN_ERR(populate_opened_fs(fs, pool)); + return SVN_NO_ERROR;; error: svn_error_clear(cleanup_fs(fs)); @@ -775,7 +776,8 @@ base_open(svn_fs_t *fs, const char *path if (svn_err) goto error; } - return base_serialized_init(fs, common_pool, pool); + SVN_ERR(populate_opened_fs(fs, pool)); + return SVN_NO_ERROR; error: svn_error_clear(cleanup_fs(fs)); Modified: subversion/branches/ev2-export/subversion/libsvn_fs_base/fs.h URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_fs_base/fs.h?rev=1331076&r1=1331075&r2=1331076&view=diff ============================================================================== --- subversion/branches/ev2-export/subversion/libsvn_fs_base/fs.h (original) +++ subversion/branches/ev2-export/subversion/libsvn_fs_base/fs.h Thu Apr 26 20:37:17 2012 @@ -108,9 +108,6 @@ typedef struct base_fs_data_t transaction trail alive. */ svn_boolean_t in_txn_trail; - /* The filesystem UUID (or NULL if not-yet-known; see svn_fs_get_uuid). */ - const char *uuid; - /* The format number of this FS. */ int format; Modified: subversion/branches/ev2-export/subversion/libsvn_fs_base/tree.c URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_fs_base/tree.c?rev=1331076&r1=1331075&r2=1331076&view=diff ============================================================================== --- subversion/branches/ev2-export/subversion/libsvn_fs_base/tree.c (original) +++ subversion/branches/ev2-export/subversion/libsvn_fs_base/tree.c Thu Apr 26 20:37:17 2012 @@ -3192,17 +3192,7 @@ fs_same_p(svn_boolean_t *same_p, svn_fs_t *fs2, apr_pool_t *pool) { - const char *uuid1; - const char *uuid2; - - /* Random thought: if fetching UUIDs to compare filesystems is too - expensive, one solution would be to cache the UUID in each fs - object (copying the UUID into fs->pool, of course). */ - - SVN_ERR(fs1->vtable->get_uuid(fs1, &uuid1, pool)); - SVN_ERR(fs2->vtable->get_uuid(fs2, &uuid2, pool)); - - *same_p = ! strcmp(uuid1, uuid2); + *same_p = ! strcmp(fs1->uuid, fs2->uuid); return SVN_NO_ERROR; } Modified: subversion/branches/ev2-export/subversion/libsvn_fs_base/uuid.c URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_fs_base/uuid.c?rev=1331076&r1=1331075&r2=1331076&view=diff ============================================================================== --- subversion/branches/ev2-export/subversion/libsvn_fs_base/uuid.c (original) +++ subversion/branches/ev2-export/subversion/libsvn_fs_base/uuid.c Thu Apr 26 20:37:17 2012 @@ -48,39 +48,27 @@ txn_body_get_uuid(void *baton, trail_t * svn_error_t * -svn_fs_base__get_uuid(svn_fs_t *fs, - const char **uuid, - apr_pool_t *pool) +svn_fs_base__populate_uuid(svn_fs_t *fs, + apr_pool_t *scratch_pool) { - base_fs_data_t *bfd = fs->fsap_data; SVN_ERR(svn_fs__check_fs(fs, TRUE)); - /* Check for a cached UUID first. Failing that, we hit the - database. */ - if (bfd->uuid) - { - *uuid = apr_pstrdup(pool, bfd->uuid); - } - else + /* We hit the database. */ { + const char *uuid; struct get_uuid_args args; - apr_pool_t *scratch_pool = svn_pool_create(pool); args.idx = 1; - args.uuid = uuid; + args.uuid = &uuid; SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_uuid, &args, FALSE, scratch_pool)); - if (*uuid) + if (uuid) { - *uuid = apr_pstrdup(pool, *uuid); - /* Toss what we find into the cache. */ - bfd->uuid = apr_pstrdup(fs->pool, *uuid); + fs->uuid = apr_pstrdup(fs->pool, uuid); } - - svn_pool_destroy(scratch_pool); } return SVN_NO_ERROR; @@ -109,7 +97,6 @@ svn_fs_base__set_uuid(svn_fs_t *fs, apr_pool_t *pool) { struct set_uuid_args args; - base_fs_data_t *bfd = fs->fsap_data; SVN_ERR(svn_fs__check_fs(fs, TRUE)); @@ -122,7 +109,7 @@ svn_fs_base__set_uuid(svn_fs_t *fs, /* Toss our value into the cache. */ if (uuid) - bfd->uuid = apr_pstrdup(fs->pool, uuid); + fs->uuid = apr_pstrdup(fs->pool, uuid); return SVN_NO_ERROR; } Modified: subversion/branches/ev2-export/subversion/libsvn_fs_base/uuid.h URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_fs_base/uuid.h?rev=1331076&r1=1331075&r2=1331076&view=diff ============================================================================== --- subversion/branches/ev2-export/subversion/libsvn_fs_base/uuid.h (original) +++ subversion/branches/ev2-export/subversion/libsvn_fs_base/uuid.h Thu Apr 26 20:37:17 2012 @@ -29,12 +29,15 @@ extern "C" { +/* Set FS->UUID to the the value read from the database, allocated + in FS->POOL. Use SCRATCH_POOL for temporary allocations. */ +svn_error_t *svn_fs_base__populate_uuid(svn_fs_t *fs, + apr_pool_t *scratch_pool); + + /* These functions implement some of the calls in the FS loader library's fs vtable. */ -svn_error_t *svn_fs_base__get_uuid(svn_fs_t *fs, const char **uuid, - apr_pool_t *pool); - svn_error_t *svn_fs_base__set_uuid(svn_fs_t *fs, const char *uuid, apr_pool_t *pool); Modified: subversion/branches/ev2-export/subversion/libsvn_fs_fs/caching.c URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_fs_fs/caching.c?rev=1331076&r1=1331075&r2=1331076&view=diff ============================================================================== --- subversion/branches/ev2-export/subversion/libsvn_fs_fs/caching.c (original) +++ subversion/branches/ev2-export/subversion/libsvn_fs_fs/caching.c Thu Apr 26 20:37:17 2012 @@ -252,7 +252,7 @@ svn_fs_fs__initialize_caches(svn_fs_t *f { fs_fs_data_t *ffd = fs->fsap_data; const char *prefix = apr_pstrcat(pool, - "fsfs:", ffd->uuid, + "fsfs:", fs->uuid, "/", fs->path, ":", (char *)NULL); svn_memcache_t *memcache; @@ -495,7 +495,7 @@ svn_fs_fs__initialize_txn_caches(svn_fs_ to start a new transaction later that receives the same id. Therefore, throw in a uuid as well - just to be sure. */ const char *prefix = apr_pstrcat(pool, - "fsfs:", ffd->uuid, + "fsfs:", fs->uuid, "/", fs->path, ":", txn_id, ":", svn_uuid_generate(pool), ":", Modified: subversion/branches/ev2-export/subversion/libsvn_fs_fs/fs.c URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_fs_fs/fs.c?rev=1331076&r1=1331075&r2=1331076&view=diff ============================================================================== --- subversion/branches/ev2-export/subversion/libsvn_fs_fs/fs.c (original) +++ subversion/branches/ev2-export/subversion/libsvn_fs_fs/fs.c Thu Apr 26 20:37:17 2012 @@ -73,7 +73,7 @@ fs_serialized_init(svn_fs_t *fs, apr_poo know of a better way of associating such data with the repository. */ - key = apr_pstrcat(pool, SVN_FSFS_SHARED_USERDATA_PREFIX, ffd->uuid, + key = apr_pstrcat(pool, SVN_FSFS_SHARED_USERDATA_PREFIX, fs->uuid, (char *) NULL); status = apr_pool_userdata_get(&val, key, common_pool); if (status) @@ -130,7 +130,6 @@ static fs_vtable_t fs_vtable = { svn_fs_fs__revision_prop, svn_fs_fs__revision_proplist, svn_fs_fs__change_rev_prop, - svn_fs_fs__get_uuid, svn_fs_fs__set_uuid, svn_fs_fs__revision_root, svn_fs_fs__begin_txn, Modified: subversion/branches/ev2-export/subversion/libsvn_fs_fs/fs.h URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_fs_fs/fs.h?rev=1331076&r1=1331075&r2=1331076&view=diff ============================================================================== --- subversion/branches/ev2-export/subversion/libsvn_fs_fs/fs.h (original) +++ subversion/branches/ev2-export/subversion/libsvn_fs_fs/fs.h Thu Apr 26 20:37:17 2012 @@ -218,9 +218,6 @@ typedef struct fs_fs_data_t layouts) or zero (for linear layouts). */ int max_files_per_dir; - /* The uuid of this FS. */ - const char *uuid; - /* The revision that was youngest, last time we checked. */ svn_revnum_t youngest_rev_cache; Modified: subversion/branches/ev2-export/subversion/libsvn_fs_fs/fs_fs.c URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_fs_fs/fs_fs.c?rev=1331076&r1=1331075&r2=1331076&view=diff ============================================================================== --- subversion/branches/ev2-export/subversion/libsvn_fs_fs/fs_fs.c (original) +++ subversion/branches/ev2-export/subversion/libsvn_fs_fs/fs_fs.c Thu Apr 26 20:37:17 2012 @@ -1307,7 +1307,7 @@ svn_fs_fs__open(svn_fs_t *fs, const char limit = sizeof(buf); SVN_ERR(svn_io_read_length_line(uuid_file, buf, &limit, pool)); - ffd->uuid = apr_pstrdup(fs->pool, buf); + fs->uuid = apr_pstrdup(fs->pool, buf); SVN_ERR(svn_io_file_close(uuid_file, pool)); @@ -7500,17 +7500,6 @@ svn_fs_fs__recover(svn_fs_t *fs, } svn_error_t * -svn_fs_fs__get_uuid(svn_fs_t *fs, - const char **uuid_p, - apr_pool_t *pool) -{ - fs_fs_data_t *ffd = fs->fsap_data; - - *uuid_p = apr_pstrdup(pool, ffd->uuid); - return SVN_NO_ERROR; -} - -svn_error_t * svn_fs_fs__set_uuid(svn_fs_t *fs, const char *uuid, apr_pool_t *pool) @@ -7519,7 +7508,6 @@ svn_fs_fs__set_uuid(svn_fs_t *fs, apr_size_t my_uuid_len; const char *tmp_path; const char *uuid_path = path_uuid(fs, pool); - fs_fs_data_t *ffd = fs->fsap_data; if (! uuid) uuid = svn_uuid_generate(pool); @@ -7540,7 +7528,7 @@ svn_fs_fs__set_uuid(svn_fs_t *fs, /* Remove the newline we added, and stash the UUID. */ my_uuid[my_uuid_len - 1] = '\0'; - ffd->uuid = my_uuid; + fs->uuid = my_uuid; return SVN_NO_ERROR; } @@ -8709,7 +8697,7 @@ hotcopy_incremental_check_preconditions( /* Make sure the UUID of source and destination match up. * We don't want to copy over a different repository. */ - if (strcmp(src_ffd->uuid, dst_ffd->uuid) != 0) + if (strcmp(src_fs->uuid, dst_fs->uuid) != 0) return svn_error_create(SVN_ERR_RA_UUID_MISMATCH, NULL, _("The UUID of the hotcopy source does " "not match the UUID of the hotcopy " @@ -9155,7 +9143,7 @@ hotcopy_create_empty_dest(svn_fs_t *src_ /* Create lock file and UUID. */ SVN_ERR(svn_io_file_create(path_lock(dst_fs, pool), "", pool)); - SVN_ERR(svn_fs_fs__set_uuid(dst_fs, src_ffd->uuid, pool)); + SVN_ERR(svn_fs_fs__set_uuid(dst_fs, src_fs->uuid, pool)); /* Create the min unpacked rev file. */ if (dst_ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT) Modified: subversion/branches/ev2-export/subversion/libsvn_fs_fs/fs_fs.h URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_fs_fs/fs_fs.h?rev=1331076&r1=1331075&r2=1331076&view=diff ============================================================================== --- subversion/branches/ev2-export/subversion/libsvn_fs_fs/fs_fs.h (original) +++ subversion/branches/ev2-export/subversion/libsvn_fs_fs/fs_fs.h Thu Apr 26 20:37:17 2012 @@ -355,12 +355,6 @@ svn_error_t *svn_fs_fs__create(svn_fs_t const char *path, apr_pool_t *pool); -/* Store the uuid of the repository FS in *UUID. Allocate space in - POOL. */ -svn_error_t *svn_fs_fs__get_uuid(svn_fs_t *fs, - const char **uuid, - apr_pool_t *pool); - /* Set the uuid of repository FS to UUID, if UUID is not NULL; otherwise, set the uuid of FS to a newly generated UUID. Perform temporary allocations in POOL. */ Modified: subversion/branches/ev2-export/subversion/libsvn_fs_fs/tree.c URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_fs_fs/tree.c?rev=1331076&r1=1331075&r2=1331076&view=diff ============================================================================== --- subversion/branches/ev2-export/subversion/libsvn_fs_fs/tree.c (original) +++ subversion/branches/ev2-export/subversion/libsvn_fs_fs/tree.c Thu Apr 26 20:37:17 2012 @@ -1925,17 +1925,7 @@ fs_same_p(svn_boolean_t *same_p, svn_fs_t *fs2, apr_pool_t *pool) { - const char *uuid1; - const char *uuid2; - - /* Random thought: if fetching UUIDs to compare filesystems is too - expensive, one solution would be to cache the UUID in each fs - object (copying the UUID into fs->pool, of course). */ - - SVN_ERR(fs1->vtable->get_uuid(fs1, &uuid1, pool)); - SVN_ERR(fs2->vtable->get_uuid(fs2, &uuid2, pool)); - - *same_p = ! strcmp(uuid1, uuid2); + *same_p = ! strcmp(fs1->uuid, fs2->uuid); return SVN_NO_ERROR; } Modified: subversion/branches/ev2-export/subversion/libsvn_ra_local/ra_plugin.c URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_ra_local/ra_plugin.c?rev=1331076&r1=1331075&r2=1331076&view=diff ============================================================================== --- subversion/branches/ev2-export/subversion/libsvn_ra_local/ra_plugin.c (original) +++ subversion/branches/ev2-export/subversion/libsvn_ra_local/ra_plugin.c Thu Apr 26 20:37:17 2012 @@ -76,8 +76,6 @@ get_username(svn_ra_session_t *session, apr_pool_t *pool) { svn_ra_local__session_baton_t *sess = session->priv; - svn_auth_iterstate_t *iterstate; - svn_fs_access_t *access_ctx; /* If we've already found the username don't ask for it again. */ if (! sess->username) @@ -88,6 +86,8 @@ get_username(svn_ra_session_t *session, { void *creds; svn_auth_cred_username_t *username_creds; + svn_auth_iterstate_t *iterstate; + SVN_ERR(svn_auth_first_credentials(&creds, &iterstate, SVN_AUTH_CRED_USERNAME, sess->uuid, /* realmstring */ @@ -118,6 +118,8 @@ get_username(svn_ra_session_t *session, */ if (*sess->username) { + svn_fs_access_t *access_ctx; + SVN_ERR(svn_fs_create_access(&access_ctx, sess->username, session->pool)); SVN_ERR(svn_fs_set_access(sess->fs, access_ctx)); @@ -367,11 +369,12 @@ struct deltify_etc_baton { svn_fs_t *fs; /* the fs to deltify in */ svn_repos_t *repos; /* repos for unlocking */ - const char *fs_path; /* fs-path part of split session URL */ + const char *fspath_base; /* fs-path part of split session URL */ + apr_hash_t *lock_tokens; /* tokens to unlock, if any */ - apr_pool_t *pool; /* pool for scratch work */ - svn_commit_callback2_t callback; /* the original callback */ - void *callback_baton; /* the original callback's baton */ + + svn_commit_callback2_t commit_cb; /* the original callback */ + void *commit_baton; /* the original callback's baton */ }; /* This implements 'svn_commit_callback_t'. Its invokes the original @@ -380,60 +383,107 @@ struct deltify_etc_baton BATON is 'struct deltify_etc_baton *'. */ static svn_error_t * deltify_etc(const svn_commit_info_t *commit_info, - void *baton, apr_pool_t *pool) + void *baton, + apr_pool_t *scratch_pool) { - struct deltify_etc_baton *db = baton; + struct deltify_etc_baton *deb = baton; svn_error_t *err1 = SVN_NO_ERROR; svn_error_t *err2; - apr_hash_index_t *hi; - apr_pool_t *iterpool; /* Invoke the original callback first, in case someone's waiting to know the revision number so they can go off and annotate an issue or something. */ - if (db->callback) - err1 = db->callback(commit_info, db->callback_baton, pool); + if (deb->commit_cb) + err1 = deb->commit_cb(commit_info, deb->commit_baton, scratch_pool); /* Maybe unlock the paths. */ - if (db->lock_tokens) + if (deb->lock_tokens) { - iterpool = svn_pool_create(db->pool); - for (hi = apr_hash_first(db->pool, db->lock_tokens); hi; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_hash_index_t *hi; + + for (hi = apr_hash_first(scratch_pool, deb->lock_tokens); hi; hi = apr_hash_next(hi)) { - const void *rel_path; - void *val; - const char *abs_path, *token; + const void *relpath = svn__apr_hash_index_key(hi); + const char *token = svn__apr_hash_index_val(hi); + const char *fspath; svn_pool_clear(iterpool); - apr_hash_this(hi, &rel_path, NULL, &val); - token = val; - abs_path = svn_fspath__join(db->fs_path, rel_path, iterpool); + + fspath = svn_fspath__join(deb->fspath_base, relpath, iterpool); + /* We may get errors here if the lock was broken or stolen after the commit succeeded. This is fine and should be ignored. */ - svn_error_clear(svn_repos_fs_unlock(db->repos, abs_path, token, + svn_error_clear(svn_repos_fs_unlock(deb->repos, fspath, token, FALSE, iterpool)); } + svn_pool_destroy(iterpool); } /* But, deltification shouldn't be stopped just because someone's random callback failed, so proceed unconditionally on to deltification. */ - err2 = svn_fs_deltify_revision(db->fs, commit_info->revision, db->pool); + err2 = svn_fs_deltify_revision(deb->fs, commit_info->revision, scratch_pool); + + return svn_error_compose_create(err1, err2); +} + - /* It's more interesting if the original callback failed, so let - that one dominate. */ - if (err1) +/* If LOCK_TOKENS is not NULL, then copy all tokens into the access context + of FS. The tokens' paths will be prepended with FSPATH_BASE. + + ACCESS_POOL must match (or exceed) the lifetime of the access context + that was associated with FS. Typically, this is the session pool. + + Temporary allocations are made in SCRATCH_POOL. */ +static svn_error_t * +apply_lock_tokens(svn_fs_t *fs, + const char *fspath_base, + apr_hash_t *lock_tokens, + apr_pool_t *access_pool, + apr_pool_t *scratch_pool) +{ + if (lock_tokens) { - svn_error_clear(err2); - return err1; + svn_fs_access_t *access_ctx; + + SVN_ERR(svn_fs_get_access(&access_ctx, fs)); + + /* If there is no access context, the filesystem will scream if a + lock is needed. */ + if (access_ctx) + { + apr_hash_index_t *hi; + + /* Note: we have no use for an iterpool here since the data + within the loop is copied into ACCESS_POOL. */ + + for (hi = apr_hash_first(scratch_pool, lock_tokens); hi; + hi = apr_hash_next(hi)) + { + const void *relpath = svn__apr_hash_index_key(hi); + const char *token = svn__apr_hash_index_val(hi); + const char *fspath; + + /* The path needs to live as long as ACCESS_CTX. */ + fspath = svn_fspath__join(fspath_base, relpath, access_pool); + + /* The token must live as long as ACCESS_CTX. */ + token = apr_pstrdup(access_pool, token); + + SVN_ERR(svn_fs_access_add_lock_token2(access_ctx, fspath, + token)); + } + } } - return err2; + return SVN_NO_ERROR; } + /*----------------------------------------------------------------*/ /*** The RA vtable routines ***/ @@ -679,47 +729,24 @@ svn_ra_local__get_commit_editor(svn_ra_s apr_pool_t *pool) { svn_ra_local__session_baton_t *sess = session->priv; - struct deltify_etc_baton *db = apr_palloc(pool, sizeof(*db)); - apr_hash_index_t *hi; - svn_fs_access_t *fs_access; + struct deltify_etc_baton *deb = apr_palloc(pool, sizeof(*deb)); - db->fs = sess->fs; - db->repos = sess->repos; - db->fs_path = sess->fs_path->data; + /* Prepare the baton for deltify_etc() */ + deb->fs = sess->fs; + deb->repos = sess->repos; + deb->fspath_base = sess->fs_path->data; if (! keep_locks) - db->lock_tokens = lock_tokens; + deb->lock_tokens = lock_tokens; else - db->lock_tokens = NULL; - db->pool = pool; - db->callback = callback; - db->callback_baton = callback_baton; + deb->lock_tokens = NULL; + deb->commit_cb = callback; + deb->commit_baton = callback_baton; SVN_ERR(get_username(session, pool)); /* If there are lock tokens to add, do so. */ - if (lock_tokens) - { - SVN_ERR(svn_fs_get_access(&fs_access, sess->fs)); - - /* If there is no access context, the filesystem will scream if a - lock is needed. */ - if (fs_access) - { - for (hi = apr_hash_first(pool, lock_tokens); hi; - hi = apr_hash_next(hi)) - { - void *val; - const char *abs_path, *token; - const void *key; - - apr_hash_this(hi, &key, NULL, &val); - abs_path = svn_fspath__join(sess->fs_path->data, key, pool); - token = val; - SVN_ERR(svn_fs_access_add_lock_token2(fs_access, - abs_path, token)); - } - } - } + SVN_ERR(apply_lock_tokens(sess->fs, sess->fs_path->data, lock_tokens, + session->pool, pool)); /* Copy the revprops table so we can add the username. */ revprop_table = apr_hash_copy(pool, revprop_table); @@ -730,7 +757,7 @@ svn_ra_local__get_commit_editor(svn_ra_s return svn_repos_get_commit_editor5 (editor, edit_baton, sess->repos, NULL, svn_path_uri_decode(sess->repos_url, pool), sess->fs_path->data, - revprop_table, deltify_etc, db, NULL, NULL, pool); + revprop_table, deltify_etc, deb, NULL, NULL, pool); } @@ -1514,6 +1541,62 @@ svn_ra_local__register_editor_shim_callb return SVN_NO_ERROR; } + +static svn_error_t * +svn_ra_local__get_commit_ev2(svn_editor_t **editor, + svn_ra_session_t *session, + apr_hash_t *revprops, + svn_commit_callback2_t commit_cb, + void *commit_baton, + apr_hash_t *lock_tokens, + svn_boolean_t keep_locks, + svn_ra__provide_base_cb_t provide_base_cb, + svn_ra__provide_props_cb_t provide_props_cb, + svn_ra__get_copysrc_kind_cb_t get_copysrc_kind_cb, + void *cb_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_ra_local__session_baton_t *sess = session->priv; + struct deltify_etc_baton *deb = apr_palloc(result_pool, sizeof(*deb)); + + /* NOTE: the RA callbacks are ignored. We pass everything directly to + the REPOS editor. */ + + /* Prepare the baton for deltify_etc() */ + deb->fs = sess->fs; + deb->repos = sess->repos; + deb->fspath_base = sess->fs_path->data; + if (! keep_locks) + deb->lock_tokens = lock_tokens; + else + deb->lock_tokens = NULL; + deb->commit_cb = commit_cb; + deb->commit_baton = commit_baton; + + /* Ensure there is a username (and an FS access context) associated with + the session and its FS handle. */ + SVN_ERR(get_username(session, scratch_pool)); + + /* If there are lock tokens to add, do so. */ + SVN_ERR(apply_lock_tokens(sess->fs, sess->fs_path->data, lock_tokens, + session->pool, scratch_pool)); + + /* Copy the REVPROPS and insert the author/username. */ + revprops = apr_hash_copy(scratch_pool, revprops); + apr_hash_set(revprops, SVN_PROP_REVISION_AUTHOR, APR_HASH_KEY_STRING, + svn_string_create(sess->username, scratch_pool)); + + return svn_error_trace(svn_repos__get_commit_ev2( + editor, sess->repos, NULL /* authz */, + NULL /* authz_repos_name */, NULL /* authz_user */, + revprops, + deltify_etc, deb, cancel_func, cancel_baton, + result_pool, scratch_pool)); +} + /*----------------------------------------------------------------*/ static const svn_version_t * @@ -1561,7 +1644,8 @@ static const svn_ra__vtable_t ra_local_v svn_ra_local__has_capability, svn_ra_local__replay_range, svn_ra_local__get_deleted_rev, - svn_ra_local__register_editor_shim_callbacks + svn_ra_local__register_editor_shim_callbacks, + svn_ra_local__get_commit_ev2 }; Modified: subversion/branches/ev2-export/subversion/libsvn_ra_neon/commit.c URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_ra_neon/commit.c?rev=1331076&r1=1331075&r2=1331076&view=diff ============================================================================== --- subversion/branches/ev2-export/subversion/libsvn_ra_neon/commit.c (original) +++ subversion/branches/ev2-export/subversion/libsvn_ra_neon/commit.c Thu Apr 26 20:37:17 2012 @@ -737,6 +737,7 @@ static svn_error_t * commit_open_root(vo transaction (and, optionally, a corresponding activity). */ svn_ra_neon__request_t *req; const char *header_val; + svn_error_t *err; SVN_ERR(svn_ra_neon__request_create(&req, cc->ras, "POST", cc->ras->me_resource, dir_pool)); @@ -749,33 +750,39 @@ static svn_error_t * commit_open_root(vo svn_uuid_generate(dir_pool)); #endif - SVN_ERR(svn_ra_neon__request_dispatch(NULL, req, NULL, "( create-txn )", - 201, 0, dir_pool)); - - /* Check the response headers for either the virtual transaction - details, or the real transaction details. We need to have - one or the other of those! */ - if ((header_val = ne_get_response_header(req->ne_req, - SVN_DAV_VTXN_NAME_HEADER))) - { - cc->txn_url = svn_path_url_add_component2(cc->ras->vtxn_stub, - header_val, cc->pool); - cc->txn_root_url - = svn_path_url_add_component2(cc->ras->vtxn_root_stub, - header_val, cc->pool); - } - else if ((header_val = ne_get_response_header(req->ne_req, - SVN_DAV_TXN_NAME_HEADER))) - { - cc->txn_url = svn_path_url_add_component2(cc->ras->txn_stub, - header_val, cc->pool); - cc->txn_root_url = svn_path_url_add_component2(cc->ras->txn_root_stub, - header_val, cc->pool); + err = svn_ra_neon__request_dispatch(NULL, req, NULL, "( create-txn )", + 201, 0, dir_pool); + if (!err) + { + /* Check the response headers for either the virtual transaction + details, or the real transaction details. We need to have + one or the other of those! */ + if ((header_val = ne_get_response_header(req->ne_req, + SVN_DAV_VTXN_NAME_HEADER))) + { + cc->txn_url = svn_path_url_add_component2(cc->ras->vtxn_stub, + header_val, cc->pool); + cc->txn_root_url + = svn_path_url_add_component2(cc->ras->vtxn_root_stub, + header_val, cc->pool); + } + else if ((header_val + = ne_get_response_header(req->ne_req, + SVN_DAV_TXN_NAME_HEADER))) + { + cc->txn_url = svn_path_url_add_component2(cc->ras->txn_stub, + header_val, cc->pool); + cc->txn_root_url + = svn_path_url_add_component2(cc->ras->txn_root_stub, + header_val, cc->pool); + } + else + err = svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, + _("POST request did not return transaction " + "information")); } - else - return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, - _("POST request did not return transaction " - "information")); + svn_ra_neon__request_destroy(req); + SVN_ERR(err); root->rsrc = NULL; root->txn_root_url = svn_path_url_add_component2(cc->txn_root_url, Modified: subversion/branches/ev2-export/subversion/libsvn_repos/commit.c URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_repos/commit.c?rev=1331076&r1=1331075&r2=1331076&view=diff ============================================================================== --- subversion/branches/ev2-export/subversion/libsvn_repos/commit.c (original) +++ subversion/branches/ev2-export/subversion/libsvn_repos/commit.c Thu Apr 26 20:37:17 2012 @@ -136,6 +136,17 @@ struct ev2_baton /* The repository we are editing. */ svn_repos_t *repos; + /* The authz baton for checks; NULL to skip authz. */ + svn_authz_t *authz; + + /* The repository name and user for performing authz checks. */ + const char *authz_repos_name; + const char *authz_user; + + /* Callback to provide info about the committed revision. */ + svn_commit_callback2_t commit_cb; + void *commit_baton; + /* The FS txn editor */ svn_editor_t *inner; @@ -158,6 +169,40 @@ out_of_date(const char *path, svn_node_k } +static svn_error_t * +invoke_commit_cb(svn_commit_callback2_t commit_cb, + void *commit_baton, + svn_fs_t *fs, + svn_revnum_t revision, + const char *post_commit_errstr, + apr_pool_t *scratch_pool) +{ + /* FS interface returns non-const values. */ + /* const */ svn_string_t *date; + /* const */ svn_string_t *author; + svn_commit_info_t *commit_info; + + if (commit_cb == NULL) + return SVN_NO_ERROR; + + SVN_ERR(svn_fs_revision_prop(&date, fs, revision, SVN_PROP_REVISION_DATE, + scratch_pool)); + SVN_ERR(svn_fs_revision_prop(&author, fs, revision, + SVN_PROP_REVISION_AUTHOR, + scratch_pool)); + + commit_info = svn_create_commit_info(scratch_pool); + + /* fill up the svn_commit_info structure */ + commit_info->revision = revision; + commit_info->date = date ? date->data : NULL; + commit_info->author = author ? author->data : NULL; + commit_info->post_commit_err = post_commit_errstr; + + return svn_error_trace(commit_cb(commit_info, commit_baton, scratch_pool)); +} + + /* If EDITOR_BATON contains a valid authz callback, verify that the REQUIRED access to PATH in ROOT is authorized. Return an error @@ -646,7 +691,8 @@ svn_repos__post_commit_error_str(svn_err else hook_err2 = hook_err1; - /* This implementation counts on svn_repos_fs_commit_txn() returning + /* This implementation counts on svn_repos_fs_commit_txn() and + libsvn_repos/commit.c:complete_cb() returning svn_fs_commit_txn() as the parent error with a child SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED error. If the parent error is SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED then there was no error @@ -718,7 +764,6 @@ close_edit(void *edit_baton, display it as a warning) and clear the error. */ post_commit_err = svn_repos__post_commit_error_str(err, pool); svn_error_clear(err); - err = SVN_NO_ERROR; } } else @@ -745,41 +790,19 @@ close_edit(void *edit_baton, svn_fs_abort_txn(eb->txn, pool))); } - /* Pass new revision information to the caller's callback. */ - { - svn_string_t *date, *author; - svn_commit_info_t *commit_info; - - /* Even if there was a post-commit hook failure, it's more serious - if one of the calls here fails, so we explicitly check for errors - here, while saving the possible post-commit error for later. */ - - err = svn_fs_revision_prop(&date, svn_repos_fs(eb->repos), - new_revision, SVN_PROP_REVISION_DATE, - pool); - if (! err) - { - err = svn_fs_revision_prop(&author, svn_repos_fs(eb->repos), - new_revision, SVN_PROP_REVISION_AUTHOR, - pool); - } - - if ((! err) && eb->commit_callback) - { - commit_info = svn_create_commit_info(pool); - - /* fill up the svn_commit_info structure */ - commit_info->revision = new_revision; - commit_info->date = date ? date->data : NULL; - commit_info->author = author ? author->data : NULL; - commit_info->post_commit_err = post_commit_err; - err = (*eb->commit_callback)(commit_info, - eb->commit_callback_baton, - pool); - } - } + /* At this point, the post-commit error has been converted to a string. + That information will be passed to a callback, if provided. If the + callback invocation fails in some way, that failure is returned here. + IOW, the post-commit error information is low priority compared to + other gunk here. */ - return svn_error_trace(err); + /* Pass new revision information to the caller's callback. */ + return svn_error_trace(invoke_commit_cb(eb->commit_callback, + eb->commit_callback_baton, + eb->repos->fs, + new_revision, + post_commit_err, + pool)); } @@ -973,41 +996,366 @@ svn_repos_get_commit_editor5(const svn_d } +#if 0 +static svn_error_t * +ev2_check_authz(const struct ev2_baton *eb, + const char *relpath, + svn_repos_authz_access_t required, + apr_pool_t *scratch_pool) +{ + const char *fspath; + svn_boolean_t allowed; + + if (eb->authz == NULL) + return SVN_NO_ERROR; + + if (relpath) + fspath = apr_pstrcat(scratch_pool, "/", relpath, NULL); + else + fspath = NULL; + + SVN_ERR(svn_repos_authz_check_access(eb->authz, eb->authz_repos_name, fspath, + eb->authz_user, required, + &allowed, scratch_pool)); + if (!allowed) + return svn_error_create(required & svn_authz_write + ? SVN_ERR_AUTHZ_UNWRITABLE + : SVN_ERR_AUTHZ_UNREADABLE, + NULL, "Access denied"); + + return SVN_NO_ERROR; +} +#endif + + +/* This implements svn_editor_cb_add_directory_t */ +static svn_error_t * +add_directory_cb(void *baton, + const char *relpath, + const apr_array_header_t *children, + apr_hash_t *props, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool) +{ + struct ev2_baton *eb = baton; + + SVN_ERR(svn_editor_add_directory(eb->inner, relpath, children, props, + replaces_rev)); + return SVN_NO_ERROR; +} + + +/* This implements svn_editor_cb_add_file_t */ +static svn_error_t * +add_file_cb(void *baton, + const char *relpath, + const svn_checksum_t *checksum, + svn_stream_t *contents, + apr_hash_t *props, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool) +{ + struct ev2_baton *eb = baton; + + SVN_ERR(svn_editor_add_file(eb->inner, relpath, checksum, contents, props, + replaces_rev)); + return SVN_NO_ERROR; +} + + +/* This implements svn_editor_cb_add_symlink_t */ +static svn_error_t * +add_symlink_cb(void *baton, + const char *relpath, + const char *target, + apr_hash_t *props, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool) +{ + struct ev2_baton *eb = baton; + + SVN_ERR(svn_editor_add_symlink(eb->inner, relpath, target, props, + replaces_rev)); + return SVN_NO_ERROR; +} + + +/* This implements svn_editor_cb_add_absent_t */ +static svn_error_t * +add_absent_cb(void *baton, + const char *relpath, + svn_kind_t kind, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool) +{ + struct ev2_baton *eb = baton; + + SVN_ERR(svn_editor_add_absent(eb->inner, relpath, kind, replaces_rev)); + return SVN_NO_ERROR; +} + + +/* This implements svn_editor_cb_alter_directory_t */ +static svn_error_t * +alter_directory_cb(void *baton, + const char *relpath, + svn_revnum_t revision, + apr_hash_t *props, + apr_pool_t *scratch_pool) +{ + struct ev2_baton *eb = baton; + + SVN_ERR(svn_editor_alter_directory(eb->inner, relpath, revision, props)); + return SVN_NO_ERROR; +} + + +/* This implements svn_editor_cb_alter_file_t */ +static svn_error_t * +alter_file_cb(void *baton, + const char *relpath, + svn_revnum_t revision, + apr_hash_t *props, + const svn_checksum_t *checksum, + svn_stream_t *contents, + apr_pool_t *scratch_pool) +{ + struct ev2_baton *eb = baton; + + SVN_ERR(svn_editor_alter_file(eb->inner, relpath, revision, props, + checksum, contents)); + return SVN_NO_ERROR; +} + + +/* This implements svn_editor_cb_alter_symlink_t */ +static svn_error_t * +alter_symlink_cb(void *baton, + const char *relpath, + svn_revnum_t revision, + apr_hash_t *props, + const char *target, + apr_pool_t *scratch_pool) +{ + struct ev2_baton *eb = baton; + + SVN_ERR(svn_editor_alter_symlink(eb->inner, relpath, revision, props, + target)); + return SVN_NO_ERROR; +} + + +/* This implements svn_editor_cb_delete_t */ +static svn_error_t * +delete_cb(void *baton, + const char *relpath, + svn_revnum_t revision, + apr_pool_t *scratch_pool) +{ + struct ev2_baton *eb = baton; + + SVN_ERR(svn_editor_delete(eb->inner, relpath, revision)); + return SVN_NO_ERROR; +} + + +/* This implements svn_editor_cb_copy_t */ +static svn_error_t * +copy_cb(void *baton, + const char *src_relpath, + svn_revnum_t src_revision, + const char *dst_relpath, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool) +{ + struct ev2_baton *eb = baton; + + SVN_ERR(svn_editor_copy(eb->inner, src_relpath, src_revision, dst_relpath, + replaces_rev)); + return SVN_NO_ERROR; +} + + +/* This implements svn_editor_cb_move_t */ +static svn_error_t * +move_cb(void *baton, + const char *src_relpath, + svn_revnum_t src_revision, + const char *dst_relpath, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool) +{ + struct ev2_baton *eb = baton; + + SVN_ERR(svn_editor_move(eb->inner, src_relpath, src_revision, dst_relpath, + replaces_rev)); + return SVN_NO_ERROR; +} + + +/* This implements svn_editor_cb_rotate_t */ +static svn_error_t * +rotate_cb(void *baton, + const apr_array_header_t *relpaths, + const apr_array_header_t *revisions, + apr_pool_t *scratch_pool) +{ + struct ev2_baton *eb = baton; + + SVN_ERR(svn_editor_rotate(eb->inner, relpaths, revisions)); + return SVN_NO_ERROR; +} + + +/* This implements svn_editor_cb_complete_t */ +static svn_error_t * +complete_cb(void *baton, + apr_pool_t *scratch_pool) +{ + struct ev2_baton *eb = baton; + svn_revnum_t revision; + svn_error_t *post_commit_err; + const char *conflict_path; + svn_error_t *err; + const char *post_commit_errstr; + + /* The transaction has been fully edited. Let the pre-commit hook + have a look at the thing. */ + SVN_ERR(svn_repos__hooks_pre_commit(eb->repos, eb->txn_name, scratch_pool)); + + /* Hook is done. Let's do the actual commit. */ + SVN_ERR(svn_fs_editor_commit(&revision, &post_commit_err, &conflict_path, + eb->inner, scratch_pool, scratch_pool)); + + /* Did a conflict occur during the commit process? */ + if (conflict_path != NULL) + return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL, + _("Conflict at '%s'"), conflict_path); + + /* Since did not receive an error during the commit process, and no + conflict was specified... we committed a revision. Run the hooks. + Other errors may have occurred within the FS (specified by the + POST_COMMIT_ERR localvar), but we need to run the hooks. */ + SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision)); + err = svn_repos__hooks_post_commit(eb->repos, revision, eb->txn_name, + scratch_pool); + if (err) + err = svn_error_create(SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED, err, + _("Commit succeeded, but post-commit hook failed")); + + /* Combine the FS errors with the hook errors, and stringify. */ + err = svn_error_compose_create(post_commit_err, err); + if (err) + { + post_commit_errstr = svn_repos__post_commit_error_str(err, scratch_pool); + svn_error_clear(err); + } + else + { + post_commit_errstr = NULL; + } + + return svn_error_trace(invoke_commit_cb(eb->commit_cb, eb->commit_baton, + eb->repos->fs, revision, + post_commit_errstr, + scratch_pool)); +} + + +/* This implements svn_editor_cb_abort_t */ +static svn_error_t * +abort_cb(void *baton, + apr_pool_t *scratch_pool) +{ + struct ev2_baton *eb = baton; + + SVN_ERR(svn_editor_abort(eb->inner)); + return SVN_NO_ERROR; +} + + +static svn_error_t * +apply_revprops(svn_fs_t *fs, + const char *txn_name, + apr_hash_t *revprops, + apr_pool_t *scratch_pool) +{ + svn_fs_txn_t *txn; + const apr_array_header_t *revprops_array; + + /* The FS editor has a TXN inside it, but we can't access it. Open another + based on the TXN_NAME. */ + SVN_ERR(svn_fs_open_txn(&txn, fs, txn_name, scratch_pool)); + + /* Validate and apply the revision properties. */ + revprops_array = svn_prop_hash_to_array(revprops, scratch_pool); + SVN_ERR(svn_repos_fs_change_txn_props(txn, revprops_array, scratch_pool)); + + /* ### do we need to force the txn to close, or is it enough to wait + ### for the pool to be cleared? */ + return SVN_NO_ERROR; +} + + svn_error_t * svn_repos__get_commit_ev2(svn_editor_t **editor, svn_repos_t *repos, - svn_revnum_t revision, - apr_hash_t *revprop_table, + svn_authz_t *authz, + const char *authz_repos_name, + const char *authz_user, + apr_hash_t *revprops, + svn_commit_callback2_t commit_cb, + void *commit_baton, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { static const svn_editor_cb_many_t editor_cbs = { - NULL /* add_directory_cb */, - NULL /* add_file_cb */, - NULL /* add_symlink_cb */, - NULL /* add_absent_cb */, - NULL /* alter_directory_cb */, - NULL /* alter_file_cb */, - NULL /* alter_symlink_cb */, - NULL /* delete_cb */, - NULL /* copy_cb */, - NULL /* move_cb */, - NULL /* rotate_cb */, - NULL /* complete_cb */, - NULL /* abort_cb */ + add_directory_cb, + add_file_cb, + add_symlink_cb, + add_absent_cb, + alter_directory_cb, + alter_file_cb, + alter_symlink_cb, + delete_cb, + copy_cb, + move_cb, + rotate_cb, + complete_cb, + abort_cb }; - struct ev2_baton *eb = apr_palloc(result_pool, sizeof(*eb)); + struct ev2_baton *eb; + const svn_string_t *author; + + /* Can the user modify the repository at all? */ + /* ### check against AUTHZ. */ + /* Okay... some access is allowed. Let's run the start-commit hook. */ + author = apr_hash_get(revprops, SVN_PROP_REVISION_AUTHOR, + APR_HASH_KEY_STRING); + SVN_ERR(svn_repos__hooks_start_commit(repos, author ? author->data : NULL, + repos->client_capabilities, + scratch_pool)); + + eb = apr_palloc(result_pool, sizeof(*eb)); eb->repos = repos; + eb->authz = authz; + eb->authz_repos_name = authz_repos_name; + eb->authz_user = authz_user; + eb->commit_cb = commit_cb; + eb->commit_baton = commit_baton; SVN_ERR(svn_fs_editor_create(&eb->inner, &eb->txn_name, - repos->fs, revision, - SVN_FS_TXN_CHECK_LOCKS, + repos->fs, SVN_FS_TXN_CHECK_LOCKS, cancel_func, cancel_baton, result_pool, scratch_pool)); + /* The TXN has been created. Go ahead and apply all revision properties. */ + SVN_ERR(apply_revprops(repos->fs, eb->txn_name, revprops, scratch_pool)); + + /* Wrap the FS editor within our editor. */ SVN_ERR(svn_editor_create(editor, eb, cancel_func, cancel_baton, result_pool, scratch_pool)); SVN_ERR(svn_editor_setcb_many(*editor, &editor_cbs, scratch_pool)); Modified: subversion/branches/ev2-export/subversion/libsvn_repos/hooks.c URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_repos/hooks.c?rev=1331076&r1=1331075&r2=1331076&view=diff ============================================================================== --- subversion/branches/ev2-export/subversion/libsvn_repos/hooks.c (original) +++ subversion/branches/ev2-export/subversion/libsvn_repos/hooks.c Thu Apr 26 20:37:17 2012 @@ -30,6 +30,7 @@ #include "svn_error.h" #include "svn_dirent_uri.h" #include "svn_path.h" +#include "svn_pools.h" #include "svn_repos.h" #include "svn_utf.h" #include "repos.h" @@ -212,7 +213,8 @@ run_hook_cmd(svn_string_t **result, apr_file_t *null_handle; apr_status_t apr_err; svn_error_t *err; - apr_proc_t cmd_proc; + apr_proc_t cmd_proc = {0}; + apr_pool_t *cmd_pool; if (result) { @@ -228,42 +230,39 @@ run_hook_cmd(svn_string_t **result, (apr_err, _("Can't create null stdout for hook '%s'"), cmd); } + /* Tie resources allocated for the command to a special pool which we can + * destroy in order to clean up the stderr pipe opened for the process. */ + cmd_pool = svn_pool_create(pool); + err = svn_io_start_cmd3(&cmd_proc, ".", cmd, args, env_from_env_hash(hooks_env, pool, pool), FALSE, FALSE, stdin_handle, result != NULL, - null_handle, TRUE, NULL, pool); - - if (err) - { - /* CMD_PROC is not safe to use. Bail. */ - return svn_error_createf - (SVN_ERR_REPOS_HOOK_FAILURE, err, _("Failed to start '%s' hook"), cmd); - } + null_handle, TRUE, NULL, cmd_pool); + if (!err) + err = check_hook_result(name, cmd, &cmd_proc, cmd_proc.err, pool); else { - err = check_hook_result(name, cmd, &cmd_proc, cmd_proc.err, pool); + /* The command could not be started for some reason. */ + err = svn_error_createf(SVN_ERR_REPOS_BAD_ARGS, err, + _("Failed to start '%s' hook"), cmd); } /* Hooks are fallible, and so hook failure is "expected" to occur at times. When such a failure happens we still want to close the pipe and null file */ - apr_err = apr_file_close(cmd_proc.err); - if (!err && apr_err) - return svn_error_wrap_apr - (apr_err, _("Error closing read end of stderr pipe")); - - if (result) + if (!err && result) { svn_stringbuf_t *native_stdout; - SVN_ERR(svn_stringbuf_from_aprfile(&native_stdout, cmd_proc.out, pool)); - apr_err = apr_file_close(cmd_proc.out); - if (!err && apr_err) - return svn_error_wrap_apr - (apr_err, _("Error closing read end of stderr pipe")); - - *result = svn_stringbuf__morph_into_string(native_stdout); + err = svn_stringbuf_from_aprfile(&native_stdout, cmd_proc.out, pool); + if (!err) + *result = svn_stringbuf__morph_into_string(native_stdout); } - else + + /* Close resources allocated by svn_io_start_cmd3(), such as the pipe. */ + svn_pool_destroy(cmd_pool); + + /* Close the null handle. */ + if (null_handle) { apr_err = apr_file_close(null_handle); if (!err && apr_err)