diff options
Diffstat (limited to 'subversion/libsvn_fs')
-rw-r--r-- | subversion/libsvn_fs/access.c | 4 | ||||
-rw-r--r-- | subversion/libsvn_fs/editor.c | 850 | ||||
-rw-r--r-- | subversion/libsvn_fs/fs-loader.c | 497 | ||||
-rw-r--r-- | subversion/libsvn_fs/fs-loader.h | 52 |
4 files changed, 1221 insertions, 182 deletions
diff --git a/subversion/libsvn_fs/access.c b/subversion/libsvn_fs/access.c index 6094241..9918be4 100644 --- a/subversion/libsvn_fs/access.c +++ b/subversion/libsvn_fs/access.c @@ -24,6 +24,7 @@ #include <apr_hash.h> +#include "svn_hash.h" #include "svn_types.h" #include "svn_pools.h" #include "svn_fs.h" @@ -86,8 +87,7 @@ svn_fs_access_add_lock_token2(svn_fs_access_t *access_ctx, const char *path, const char *token) { - apr_hash_set(access_ctx->lock_tokens, - token, APR_HASH_KEY_STRING, path); + svn_hash_sets(access_ctx->lock_tokens, token, path); return SVN_NO_ERROR; } diff --git a/subversion/libsvn_fs/editor.c b/subversion/libsvn_fs/editor.c new file mode 100644 index 0000000..a75f210 --- /dev/null +++ b/subversion/libsvn_fs/editor.c @@ -0,0 +1,850 @@ +/* + * editor.c: Editor for modifying FS transactions + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ + +#include <apr_pools.h> + +#include "svn_types.h" +#include "svn_error.h" +#include "svn_pools.h" +#include "svn_fs.h" +#include "svn_props.h" +#include "svn_path.h" + +#include "svn_private_config.h" + +#include "fs-loader.h" + +#include "private/svn_fspath.h" +#include "private/svn_fs_private.h" +#include "private/svn_editor.h" + + +struct edit_baton { + /* The transaction associated with this editor. */ + svn_fs_txn_t *txn; + + /* 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; +} + + +static svn_error_t * +alter_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_t *old_props; + apr_array_header_t *propdiffs; + int i; + + SVN_ERR(svn_fs_node_proplist(&old_props, root, fspath, scratch_pool)); + + SVN_ERR(svn_prop_diffs(&propdiffs, props, old_props, scratch_pool)); + + for (i = 0; i < propdiffs->nelts; ++i) + { + const svn_prop_t *prop = &APR_ARRAY_IDX(propdiffs, i, svn_prop_t); + + svn_pool_clear(iterpool); + + /* Add, change, or delete properties. */ + SVN_ERR(svn_fs_change_node_prop(root, fspath, prop->name, prop->value, + iterpool)); + } + + svn_pool_destroy(iterpool); + return SVN_NO_ERROR; +} + + +static svn_error_t * +set_text(svn_fs_root_t *root, + const char *fspath, + const svn_checksum_t *checksum, + svn_stream_t *contents, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + svn_stream_t *fs_contents; + + /* ### 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 CHECKSUM/CONTENTS 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, + cancel_func, cancel_baton, + scratch_pool)); + + return SVN_NO_ERROR; +} + + +/* The caller wants to modify REVISION of FSPATH. Is that allowed? */ +static svn_error_t * +can_modify(svn_fs_root_t *txn_root, + const char *fspath, + svn_revnum_t revision, + apr_pool_t *scratch_pool) +{ + svn_revnum_t created_rev; + + /* Out-of-dateness check: compare the created-rev of the node + in the txn against the created-rev of FSPATH. */ + SVN_ERR(svn_fs_node_created_rev(&created_rev, txn_root, fspath, + scratch_pool)); + + /* Uncommitted nodes (eg. a descendent of a copy/move/rotate destination) + have no (committed) revision number. Let the caller go ahead and + modify these nodes. + + Note: strictly speaking, they might be performing an "illegal" edit + in certain cases, but let's just assume they're Good Little Boys. + + If CREATED_REV is invalid, that means it's already mutable in the + txn, which means it has already passed this out-of-dateness check. + (Usually, this happens when looking at a parent directory of an + already-modified node) */ + if (!SVN_IS_VALID_REVNUM(created_rev)) + return SVN_NO_ERROR; + + /* If the node is immutable (has a revision), then the caller should + have supplied a valid revision number [that they expect to change]. + The checks further below will determine the out-of-dateness of the + specified revision. */ + /* ### ugh. descendents of copy/move/rotate destinations carry along + ### their original immutable state and (thus) a valid CREATED_REV. + ### but they are logically uncommitted, so the caller will pass + ### SVN_INVALID_REVNUM. (technically, the caller could provide + ### ORIGINAL_REV, but that is semantically incorrect for the Ev2 + ### API). + ### + ### for now, we will assume the caller knows what they are doing + ### and an invalid revision implies such a descendent. in the + ### future, we could examine the ancestor chain looking for a + ### copy/move/rotate-here node and allow the modification (and the + ### converse: if no such ancestor, the caller must specify the + ### correct/intended revision to modify). + */ +#if 1 + if (!SVN_IS_VALID_REVNUM(revision)) + return SVN_NO_ERROR; +#else + if (!SVN_IS_VALID_REVNUM(revision)) + /* ### use a custom error code? */ + return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, + _("Revision for modifying '%s' is required"), + fspath); +#endif + + if (revision < created_rev) + { + /* We asked to change a node that is *older* than what we found + in the transaction. The client is out of date. */ + return svn_error_createf(SVN_ERR_FS_OUT_OF_DATE, NULL, + _("'%s' is out of date; try updating"), + fspath); + } + + if (revision > created_rev) + { + /* We asked to change a node that is *newer* than what we found + in the transaction. Given that the transaction was based off + of 'youngest', then either: + - the caller asked to modify a future node + - the caller has committed more revisions since this txn + was constructed, and is asking to modify a node in one + of those new revisions. + In either case, the node may not have changed in those new + revisions; use the node's ID to determine this case. */ + const svn_fs_id_t *txn_noderev_id; + svn_fs_root_t *rev_root; + const svn_fs_id_t *new_noderev_id; + + /* The ID of the node that we would be modifying in the txn */ + SVN_ERR(svn_fs_node_id(&txn_noderev_id, txn_root, fspath, + scratch_pool)); + + /* Get the ID from the future/new revision. */ + SVN_ERR(svn_fs_revision_root(&rev_root, svn_fs_root_fs(txn_root), + revision, scratch_pool)); + SVN_ERR(svn_fs_node_id(&new_noderev_id, rev_root, fspath, + scratch_pool)); + svn_fs_close_root(rev_root); + + /* Has the target node changed in the future? */ + if (svn_fs_compare_ids(txn_noderev_id, new_noderev_id) != 0) + { + /* Restarting the commit will base the txn on the future/new + revision, allowing the modification at REVISION. */ + /* ### use a custom error code */ + return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL, + _("'%s' has been modified since the " + "commit began (restart the commit)"), + fspath); + } + } + + return SVN_NO_ERROR; +} + + +/* Can we create a node at FSPATH in TXN_ROOT? If something already exists + at that path, then the client MAY be out of date. We then have to see if + the path was created/modified in this transaction. IOW, it is new and + can be replaced without problem. + + Note: the editor protocol disallows double-modifications. This is to + ensure somebody does not accidentally overwrite another file due to + being out-of-date. */ +static svn_error_t * +can_create(svn_fs_root_t *txn_root, + const char *fspath, + apr_pool_t *scratch_pool) +{ + svn_node_kind_t kind; + const char *cur_fspath; + + SVN_ERR(svn_fs_check_path(&kind, txn_root, fspath, scratch_pool)); + if (kind == svn_node_none) + return SVN_NO_ERROR; + + /* ### I'm not sure if this works perfectly. We might have an ancestor + ### that was modified as a result of a change on a cousin. We might + ### misinterpret that as a *-here node which brought along this + ### child. Need to write a test to verify. We may also be able to + ### test the ancestor to determine if it has been *-here in this + ### txn, or just a simple modification. */ + + /* Are any of the parents copied/moved/rotated-here? */ + for (cur_fspath = fspath; + strlen(cur_fspath) > 1; /* not the root */ + cur_fspath = svn_fspath__dirname(cur_fspath, scratch_pool)) + { + svn_revnum_t created_rev; + + SVN_ERR(svn_fs_node_created_rev(&created_rev, txn_root, cur_fspath, + scratch_pool)); + if (!SVN_IS_VALID_REVNUM(created_rev)) + { + /* The node has no created revision, meaning it is uncommitted. + Thus, it was created in this transaction, or it has already + been modified in some way (implying it has already passed a + modification check. */ + /* ### verify the node has been *-here ?? */ + return SVN_NO_ERROR; + } + } + + return svn_error_createf(SVN_ERR_FS_OUT_OF_DATE, NULL, + _("'%s' already exists, so may be out" + " of date; try updating"), + fspath); +} + + +/* 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)); + + if (SVN_IS_VALID_REVNUM(replaces_rev)) + { + SVN_ERR(can_modify(root, fspath, replaces_rev, scratch_pool)); + SVN_ERR(svn_fs_delete(root, fspath, scratch_pool)); + } + else + { + SVN_ERR(can_create(root, fspath, scratch_pool)); + } + + 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_ERR(get_root(&root, eb)); + + if (SVN_IS_VALID_REVNUM(replaces_rev)) + { + SVN_ERR(can_modify(root, fspath, replaces_rev, scratch_pool)); + SVN_ERR(svn_fs_delete(root, fspath, scratch_pool)); + } + else + { + SVN_ERR(can_create(root, fspath, scratch_pool)); + } + + SVN_ERR(svn_fs_make_file(root, fspath, scratch_pool)); + + SVN_ERR(set_text(root, fspath, checksum, 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_ERR(get_root(&root, eb)); + + if (SVN_IS_VALID_REVNUM(replaces_rev)) + { + SVN_ERR(can_modify(root, fspath, replaces_rev, scratch_pool)); + SVN_ERR(svn_fs_delete(root, fspath, scratch_pool)); + } + else + { + SVN_ERR(can_create(root, fspath, scratch_pool)); + } + + /* ### 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 + + SVN__NOT_IMPLEMENTED(); +} + + +/* This implements svn_editor_cb_add_absent_t */ +static svn_error_t * +add_absent_cb(void *baton, + const char *relpath, + svn_node_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. */ + /* ### use a custom error code */ + return svn_error_create( + SVN_ERR_UNSUPPORTED_FEATURE, NULL, + _("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, + const apr_array_header_t *children, + apr_hash_t *props, + 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)); + SVN_ERR(can_modify(root, fspath, revision, scratch_pool)); + + if (props) + SVN_ERR(alter_props(root, fspath, props, scratch_pool)); + + 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 edit_baton *eb = baton; + const char *fspath = FSPATH(relpath, scratch_pool); + svn_fs_root_t *root; + + SVN_ERR(get_root(&root, eb)); + SVN_ERR(can_modify(root, fspath, revision, scratch_pool)); + + if (contents != NULL) + { + SVN_ERR_ASSERT(checksum != NULL); + SVN_ERR(set_text(root, fspath, checksum, contents, + eb->cancel_func, eb->cancel_baton, scratch_pool)); + } + + if (props != NULL) + { + SVN_ERR(alter_props(root, fspath, props, scratch_pool)); + } + + 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 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; + const char *fspath = FSPATH(relpath, scratch_pool); + svn_fs_root_t *root; + + SVN_ERR(get_root(&root, eb)); + SVN_ERR(can_modify(root, fspath, revision, scratch_pool)); + + SVN_ERR(svn_fs_delete(root, fspath, scratch_pool)); + + 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 edit_baton *eb = baton; + const char *src_fspath = FSPATH(src_relpath, scratch_pool); + const char *dst_fspath = FSPATH(dst_relpath, scratch_pool); + svn_fs_root_t *root; + svn_fs_root_t *src_root; + + SVN_ERR(get_root(&root, eb)); + + /* Check if we can we replace the maybe-specified destination (revision). */ + if (SVN_IS_VALID_REVNUM(replaces_rev)) + { + SVN_ERR(can_modify(root, dst_fspath, replaces_rev, scratch_pool)); + SVN_ERR(svn_fs_delete(root, dst_fspath, scratch_pool)); + } + else + { + SVN_ERR(can_create(root, dst_fspath, scratch_pool)); + } + + SVN_ERR(svn_fs_revision_root(&src_root, svn_fs_root_fs(root), src_revision, + scratch_pool)); + SVN_ERR(svn_fs_copy(src_root, src_fspath, root, dst_fspath, scratch_pool)); + svn_fs_close_root(src_root); + + 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 edit_baton *eb = baton; + const char *src_fspath = FSPATH(src_relpath, scratch_pool); + const char *dst_fspath = FSPATH(dst_relpath, scratch_pool); + svn_fs_root_t *root; + svn_fs_root_t *src_root; + + SVN_ERR(get_root(&root, eb)); + + /* Check if we delete the specified source (revision), and can we replace + the maybe-specified destination (revision). */ + SVN_ERR(can_modify(root, src_fspath, src_revision, scratch_pool)); + if (SVN_IS_VALID_REVNUM(replaces_rev)) + { + SVN_ERR(can_modify(root, dst_fspath, replaces_rev, scratch_pool)); + SVN_ERR(svn_fs_delete(root, dst_fspath, scratch_pool)); + } + else + { + SVN_ERR(can_create(root, dst_fspath, scratch_pool)); + } + + /* ### would be nice to have svn_fs_move() */ + + /* Copy the src to the dst. */ + SVN_ERR(svn_fs_revision_root(&src_root, svn_fs_root_fs(root), src_revision, + scratch_pool)); + SVN_ERR(svn_fs_copy(src_root, src_fspath, root, dst_fspath, scratch_pool)); + svn_fs_close_root(src_root); + + /* Notice: we're deleting the src repos path from the dst root. */ + SVN_ERR(svn_fs_delete(root, src_fspath, scratch_pool)); + + 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 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_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 = { + 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_pcalloc(result_pool, sizeof(*eb)); + + eb->txn = txn; + 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)); + SVN_ERR(svn_editor_setcb_many(*editor, &editor_cbs, scratch_pool)); + + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs__editor_create(svn_editor_t **editor, + const char **txn_name, + svn_fs_t *fs, + 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_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, + cancel_func, cancel_baton, + result_pool, scratch_pool)); +} + + +svn_error_t * +svn_fs__editor_create_for(svn_editor_t **editor, + svn_fs_t *fs, + const char *txn_name, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + 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, + 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); +} diff --git a/subversion/libsvn_fs/fs-loader.c b/subversion/libsvn_fs/fs-loader.c index e9db910..336b84f 100644 --- a/subversion/libsvn_fs/fs-loader.c +++ b/subversion/libsvn_fs/fs-loader.c @@ -28,7 +28,9 @@ #include <apr_md5.h> #include <apr_thread_mutex.h> #include <apr_uuid.h> +#include <apr_strings.h> +#include "svn_hash.h" #include "svn_ctype.h" #include "svn_types.h" #include "svn_dso.h" @@ -43,9 +45,10 @@ #include "private/svn_fs_private.h" #include "private/svn_fs_util.h" #include "private/svn_utf_private.h" +#include "private/svn_mutex.h" +#include "private/svn_subr_private.h" #include "fs-loader.h" -#include "svn_hash.h" /* This is defined by configure on platforms which use configure, but we need to define a fallback for Windows. */ @@ -58,34 +61,43 @@ /* A pool common to all FS objects. See the documentation on the open/create functions in fs-loader.h and for svn_fs_initialize(). */ static apr_pool_t *common_pool; -#if APR_HAS_THREADS -static apr_thread_mutex_t *common_pool_lock; -#endif +svn_mutex__t *common_pool_lock; /* --- Utility functions for the loader --- */ -static const struct fs_type_defn { +struct fs_type_defn { const char *fs_type; const char *fsap_name; fs_init_func_t initfunc; -} fs_modules[] = { + struct fs_type_defn *next; +}; + +static struct fs_type_defn base_defn = { SVN_FS_TYPE_BDB, "base", #ifdef SVN_LIBSVN_FS_LINKS_FS_BASE - svn_fs_base__init + svn_fs_base__init, +#else + NULL, #endif - }, + NULL /* End of static list: this needs to be reset to NULL if the + common_pool used when setting it has been cleared. */ + }; +static struct fs_type_defn fsfs_defn = { SVN_FS_TYPE_FSFS, "fs", #ifdef SVN_LIBSVN_FS_LINKS_FS_FS - svn_fs_fs__init + svn_fs_fs__init, +#else + NULL, #endif - }, + &base_defn + }; + +static struct fs_type_defn *fs_modules = &fsfs_defn; - { NULL } -}; static svn_error_t * load_module(fs_init_func_t *initfunc, const char *name, apr_pool_t *pool) @@ -99,9 +111,18 @@ load_module(fs_init_func_t *initfunc, const char *name, apr_pool_t *pool) const char *libname; const char *funcname; apr_status_t status; - - libname = apr_psprintf(pool, "libsvn_fs_%s-%d.so.0", - name, SVN_VER_MAJOR); + const char *p; + + /* Demand a simple alphanumeric name so that the generated DSO + name is sensible. */ + for (p = name; *p; ++p) + if (!svn_ctype_isalnum(*p)) + return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL, + _("Invalid name for FS type '%s'"), + name); + + libname = apr_psprintf(pool, "libsvn_fs_%s-%d.so.%d", + name, SVN_VER_MAJOR, SVN_SOVERSION); funcname = apr_psprintf(pool, "svn_fs_%s__init", name); /* Find/load the specified library. If we get an error, assume @@ -124,30 +145,6 @@ load_module(fs_init_func_t *initfunc, const char *name, apr_pool_t *pool) return SVN_NO_ERROR; } -static svn_error_t * -acquire_fs_mutex(void) -{ -#if APR_HAS_THREADS - apr_status_t status; - status = apr_thread_mutex_lock(common_pool_lock); - if (status) - return svn_error_wrap_apr(status, _("Can't grab FS mutex")); -#endif - return SVN_NO_ERROR; -} - -static svn_error_t * -release_fs_mutex(void) -{ -#if APR_HAS_THREADS - apr_status_t status; - status = apr_thread_mutex_unlock(common_pool_lock); - if (status) - return svn_error_wrap_apr(status, _("Can't ungrab FS mutex")); -#endif - return SVN_NO_ERROR; -} - /* Fetch a library vtable by a pointer into the library definitions array. */ static svn_error_t * get_library_vtable_direct(fs_library_vtable_t **vtable, @@ -168,9 +165,6 @@ get_library_vtable_direct(fs_library_vtable_t **vtable, fst->fs_type); { - svn_error_t *err; - svn_error_t *err2; - /* Per our API compatibility rules, we cannot ensure that svn_fs_initialize is called by the application. If not, we cannot create the common pool and lock in a thread-safe fashion, @@ -183,16 +177,8 @@ get_library_vtable_direct(fs_library_vtable_t **vtable, /* Invoke the FS module's initfunc function with the common pool protected by a lock. */ - SVN_ERR(acquire_fs_mutex()); - err = initfunc(my_version, vtable, common_pool); - err2 = release_fs_mutex(); - if (err) - { - svn_error_clear(err2); - return err; - } - if (err2) - return err2; + SVN_MUTEX__WITH_LOCK(common_pool_lock, + initfunc(my_version, vtable, common_pool)); } fs_version = (*vtable)->get_version(); if (!svn_ver_equal(my_version, fs_version)) @@ -208,21 +194,77 @@ get_library_vtable_direct(fs_library_vtable_t **vtable, return SVN_NO_ERROR; } +#if defined(SVN_USE_DSO) && APR_HAS_DSO +/* Return *FST for the third party FS_TYPE */ +static svn_error_t * +get_or_allocate_third(struct fs_type_defn **fst, + const char *fs_type) +{ + while (*fst) + { + if (strcmp(fs_type, (*fst)->fs_type) == 0) + return SVN_NO_ERROR; + fst = &(*fst)->next; + } + + *fst = apr_palloc(common_pool, sizeof(struct fs_type_defn)); + (*fst)->fs_type = apr_pstrdup(common_pool, fs_type); + (*fst)->fsap_name = (*fst)->fs_type; + (*fst)->initfunc = NULL; + (*fst)->next = NULL; + + return SVN_NO_ERROR; +} +#endif + /* Fetch a library vtable by FS type. */ static svn_error_t * get_library_vtable(fs_library_vtable_t **vtable, const char *fs_type, apr_pool_t *pool) { - const struct fs_type_defn *fst; + struct fs_type_defn **fst = &fs_modules; + svn_boolean_t known = FALSE; - for (fst = fs_modules; fst->fs_type; fst++) + /* There are two FS module definitions known at compile time. We + want to check these without any locking overhead even when + dynamic third party modules are enabled. The third party modules + cannot be checked until the lock is held. */ + if (strcmp(fs_type, (*fst)->fs_type) == 0) + known = TRUE; + else { - if (strcmp(fs_type, fst->fs_type) == 0) - return get_library_vtable_direct(vtable, fst, pool); + fst = &(*fst)->next; + if (strcmp(fs_type, (*fst)->fs_type) == 0) + known = TRUE; } - return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL, - _("Unknown FS type '%s'"), fs_type); +#if defined(SVN_USE_DSO) && APR_HAS_DSO + /* Third party FS modules that are unknown at compile time. + + A third party FS is identified by the file fs-type containing a + third party name, say "foo". The loader will load the DSO with + the name "libsvn_fs_foo" and use the entry point with the name + "svn_fs_foo__init". + + Note: the BDB and FSFS modules don't follow this naming scheme + and this allows them to be used to test the third party loader. + Change the content of fs-type to "base" in a BDB filesystem or to + "fs" in an FSFS filesystem and they will be loaded as third party + modules. */ + if (!known) + { + fst = &(*fst)->next; + if (!common_pool) /* Best-effort init, see get_library_vtable_direct. */ + SVN_ERR(svn_fs_initialize(NULL)); + SVN_MUTEX__WITH_LOCK(common_pool_lock, + get_or_allocate_third(fst, fs_type)); + known = TRUE; + } +#endif + if (!known) + return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL, + _("Unknown FS type '%s'"), fs_type); + return get_library_vtable_direct(vtable, *fst, pool); } svn_error_t * @@ -303,30 +345,19 @@ write_fs_type(const char *path, const char *fs_type, apr_pool_t *pool) static apr_status_t uninit(void *data) { common_pool = NULL; -#if APR_HAS_THREADS - common_pool_lock = NULL; -#endif return APR_SUCCESS; } svn_error_t * svn_fs_initialize(apr_pool_t *pool) { -#if APR_HAS_THREADS - apr_status_t status; -#endif - /* Protect against multiple calls. */ if (common_pool) return SVN_NO_ERROR; common_pool = svn_pool_create(pool); -#if APR_HAS_THREADS - status = apr_thread_mutex_create(&common_pool_lock, - APR_THREAD_MUTEX_DEFAULT, common_pool); - if (status) - return svn_error_wrap_apr(status, _("Can't allocate FS mutex")); -#endif + base_defn.next = NULL; + SVN_ERR(svn_mutex__init(&common_pool_lock, TRUE, common_pool)); /* ### This won't work if POOL is NULL and libsvn_fs is loaded as a DSO ### (via libsvn_ra_local say) since the global common_pool will live @@ -384,6 +415,7 @@ fs_new(apr_hash_t *fs_config, apr_pool_t *pool) fs->access_ctx = NULL; fs->vtable = NULL; fs->fsap_data = NULL; + fs->uuid = NULL; return fs; } @@ -405,8 +437,6 @@ svn_error_t * svn_fs_create(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config, apr_pool_t *pool) { - svn_error_t *err; - svn_error_t *err2; fs_library_vtable_t *vtable; const char *fs_type = svn_hash__get_cstring(fs_config, @@ -420,57 +450,66 @@ svn_fs_create(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config, /* Perform the actual creation. */ *fs_p = fs_new(fs_config, pool); - SVN_ERR(acquire_fs_mutex()); - err = vtable->create(*fs_p, path, pool, common_pool); - err2 = release_fs_mutex(); - if (err) - { - svn_error_clear(err2); - return svn_error_trace(err); - } - return svn_error_trace(err2); + + SVN_MUTEX__WITH_LOCK(common_pool_lock, + vtable->create(*fs_p, path, pool, common_pool)); + SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open)); + + return SVN_NO_ERROR; } svn_error_t * svn_fs_open(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config, apr_pool_t *pool) { - svn_error_t *err; - svn_error_t *err2; fs_library_vtable_t *vtable; SVN_ERR(fs_library_vtable(&vtable, path, pool)); *fs_p = fs_new(fs_config, pool); - SVN_ERR(acquire_fs_mutex()); - err = vtable->open_fs(*fs_p, path, pool, common_pool); - err2 = release_fs_mutex(); - if (err) - { - svn_error_clear(err2); - return svn_error_trace(err); - } - return svn_error_trace(err2); + SVN_MUTEX__WITH_LOCK(common_pool_lock, + vtable->open_fs(*fs_p, path, pool, common_pool)); + SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open)); + + return SVN_NO_ERROR; } svn_error_t * svn_fs_upgrade(const char *path, apr_pool_t *pool) { - svn_error_t *err; - svn_error_t *err2; fs_library_vtable_t *vtable; svn_fs_t *fs; SVN_ERR(fs_library_vtable(&vtable, path, pool)); fs = fs_new(NULL, pool); - SVN_ERR(acquire_fs_mutex()); - err = vtable->upgrade_fs(fs, path, pool, common_pool); - err2 = release_fs_mutex(); - if (err) - { - svn_error_clear(err2); - return svn_error_trace(err); - } - return svn_error_trace(err2); + + SVN_MUTEX__WITH_LOCK(common_pool_lock, + vtable->upgrade_fs(fs, path, pool, common_pool)); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_verify(const char *path, + apr_hash_t *fs_config, + svn_revnum_t start, + svn_revnum_t end, + svn_fs_progress_notify_func_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool) +{ + fs_library_vtable_t *vtable; + svn_fs_t *fs; + + SVN_ERR(fs_library_vtable(&vtable, path, pool)); + fs = fs_new(fs_config, pool); + + SVN_MUTEX__WITH_LOCK(common_pool_lock, + vtable->verify_fs(fs, path, start, end, + notify_func, notify_baton, + cancel_func, cancel_baton, + pool, common_pool)); + return SVN_NO_ERROR; } const char * @@ -479,6 +518,15 @@ svn_fs_path(svn_fs_t *fs, apr_pool_t *pool) return apr_pstrdup(pool, fs->path); } +apr_hash_t * +svn_fs_config(svn_fs_t *fs, apr_pool_t *pool) +{ + if (fs->config) + return apr_hash_copy(pool, fs->config); + + return NULL; +} + svn_error_t * svn_fs_delete_fs(const char *path, apr_pool_t *pool) { @@ -489,16 +537,72 @@ svn_fs_delete_fs(const char *path, apr_pool_t *pool) } svn_error_t * -svn_fs_hotcopy(const char *src_path, const char *dest_path, - svn_boolean_t clean, apr_pool_t *pool) +svn_fs_hotcopy2(const char *src_path, const char *dst_path, + svn_boolean_t clean, svn_boolean_t incremental, + svn_cancel_func_t cancel_func, void *cancel_baton, + apr_pool_t *scratch_pool) { fs_library_vtable_t *vtable; - const char *fs_type; + const char *src_fs_type; + svn_fs_t *src_fs; + svn_fs_t *dst_fs; + const char *dst_fs_type; + svn_node_kind_t dst_kind; + + if (strcmp(src_path, dst_path) == 0) + return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, + _("Hotcopy source and destination are equal")); + + SVN_ERR(svn_fs_type(&src_fs_type, src_path, scratch_pool)); + SVN_ERR(get_library_vtable(&vtable, src_fs_type, scratch_pool)); + src_fs = fs_new(NULL, scratch_pool); + dst_fs = fs_new(NULL, scratch_pool); + + SVN_ERR(svn_io_check_path(dst_path, &dst_kind, scratch_pool)); + if (dst_kind == svn_node_file) + return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, + _("'%s' already exists and is a file"), + svn_dirent_local_style(dst_path, + scratch_pool)); + if (dst_kind == svn_node_unknown) + return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, + _("'%s' already exists and has an unknown " + "node kind"), + svn_dirent_local_style(dst_path, + scratch_pool)); + if (dst_kind == svn_node_dir) + { + svn_node_kind_t type_file_kind; - SVN_ERR(svn_fs_type(&fs_type, src_path, pool)); - SVN_ERR(get_library_vtable(&vtable, fs_type, pool)); - SVN_ERR(vtable->hotcopy(src_path, dest_path, clean, pool)); - return svn_error_trace(write_fs_type(dest_path, fs_type, pool)); + SVN_ERR(svn_io_check_path(svn_dirent_join(dst_path, + FS_TYPE_FILENAME, + scratch_pool), + &type_file_kind, scratch_pool)); + if (type_file_kind != svn_node_none) + { + SVN_ERR(svn_fs_type(&dst_fs_type, dst_path, scratch_pool)); + if (strcmp(src_fs_type, dst_fs_type) != 0) + return svn_error_createf( + SVN_ERR_ILLEGAL_TARGET, NULL, + _("The filesystem type of the hotcopy source " + "('%s') does not match the filesystem " + "type of the hotcopy destination ('%s')"), + src_fs_type, dst_fs_type); + } + } + + SVN_ERR(vtable->hotcopy(src_fs, dst_fs, src_path, dst_path, clean, + incremental, cancel_func, cancel_baton, + scratch_pool)); + return svn_error_trace(write_fs_type(dst_path, src_fs_type, scratch_pool)); +} + +svn_error_t * +svn_fs_hotcopy(const char *src_path, const char *dest_path, + svn_boolean_t clean, apr_pool_t *pool) +{ + return svn_error_trace(svn_fs_hotcopy2(src_path, dest_path, clean, + FALSE, NULL, NULL, pool)); } svn_error_t * @@ -509,23 +613,17 @@ svn_fs_pack(const char *path, void *cancel_baton, apr_pool_t *pool) { - svn_error_t *err; - svn_error_t *err2; fs_library_vtable_t *vtable; svn_fs_t *fs; SVN_ERR(fs_library_vtable(&vtable, path, pool)); fs = fs_new(NULL, pool); - SVN_ERR(acquire_fs_mutex()); - err = vtable->pack_fs(fs, path, notify_func, notify_baton, - cancel_func, cancel_baton, pool); - err2 = release_fs_mutex(); - if (err) - { - svn_error_clear(err2); - return svn_error_trace(err); - } - return svn_error_trace(err2); + + SVN_MUTEX__WITH_LOCK(common_pool_lock, + vtable->pack_fs(fs, path, notify_func, notify_baton, + cancel_func, cancel_baton, pool, + common_pool)); + return SVN_NO_ERROR; } svn_error_t * @@ -533,24 +631,38 @@ svn_fs_recover(const char *path, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *pool) { - svn_error_t *err; - svn_error_t *err2; fs_library_vtable_t *vtable; svn_fs_t *fs; SVN_ERR(fs_library_vtable(&vtable, path, pool)); fs = fs_new(NULL, pool); - SVN_ERR(acquire_fs_mutex()); - err = vtable->open_fs_for_recovery(fs, path, pool, common_pool); - err2 = release_fs_mutex(); - if (err) - { - svn_error_clear(err2); - return svn_error_trace(err); - } - if (! err2) - err2 = vtable->recover(fs, cancel_func, cancel_baton, pool); - return svn_error_trace(err2); + + SVN_MUTEX__WITH_LOCK(common_pool_lock, + vtable->open_fs_for_recovery(fs, path, pool, + common_pool)); + return svn_error_trace(vtable->recover(fs, cancel_func, cancel_baton, + pool)); +} + +svn_error_t * +svn_fs_verify_root(svn_fs_root_t *root, + apr_pool_t *scratch_pool) +{ + svn_fs_t *fs = root->fs; + SVN_ERR(fs->vtable->verify_root(root, scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_freeze(svn_fs_t *fs, + svn_fs_freeze_func_t freeze_func, + void *freeze_baton, + apr_pool_t *pool) +{ + SVN_ERR(fs->vtable->freeze(fs, freeze_func, freeze_baton, pool)); + + return SVN_NO_ERROR; } @@ -559,8 +671,6 @@ svn_fs_recover(const char *path, svn_error_t * svn_fs_create_berkeley(svn_fs_t *fs, const char *path) { - svn_error_t *err; - svn_error_t *err2; fs_library_vtable_t *vtable; SVN_ERR(get_library_vtable(&vtable, SVN_FS_TYPE_BDB, fs->pool)); @@ -570,34 +680,24 @@ svn_fs_create_berkeley(svn_fs_t *fs, const char *path) SVN_ERR(write_fs_type(path, SVN_FS_TYPE_BDB, fs->pool)); /* Perform the actual creation. */ - SVN_ERR(acquire_fs_mutex()); - err = vtable->create(fs, path, fs->pool, common_pool); - err2 = release_fs_mutex(); - if (err) - { - svn_error_clear(err2); - return svn_error_trace(err); - } - return svn_error_trace(err2); + SVN_MUTEX__WITH_LOCK(common_pool_lock, + vtable->create(fs, path, fs->pool, common_pool)); + SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open)); + + return SVN_NO_ERROR; } svn_error_t * svn_fs_open_berkeley(svn_fs_t *fs, const char *path) { - svn_error_t *err; - svn_error_t *err2; fs_library_vtable_t *vtable; SVN_ERR(fs_library_vtable(&vtable, path, fs->pool)); - SVN_ERR(acquire_fs_mutex()); - err = vtable->open_fs(fs, path, fs->pool, common_pool); - err2 = release_fs_mutex(); - if (err) - { - svn_error_clear(err2); - return svn_error_trace(err); - } - return svn_error_trace(err2); + SVN_MUTEX__WITH_LOCK(common_pool_lock, + vtable->open_fs(fs, path, fs->pool, common_pool)); + SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open)); + + return SVN_NO_ERROR; } const char * @@ -616,8 +716,8 @@ svn_error_t * svn_fs_hotcopy_berkeley(const char *src_path, const char *dest_path, svn_boolean_t clean_logs, apr_pool_t *pool) { - return svn_error_trace(svn_fs_hotcopy(src_path, dest_path, clean_logs, - pool)); + return svn_error_trace(svn_fs_hotcopy2(src_path, dest_path, clean_logs, + FALSE, NULL, NULL, pool)); } svn_error_t * @@ -669,22 +769,33 @@ 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) { -#ifdef PACK_AFTER_EVERY_COMMIT - svn_fs_root_t *txn_root; - svn_fs_t *fs; - const char *fs_path; + svn_error_t *err; *new_rev = SVN_INVALID_REVNUM; - SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); - fs = svn_fs_root_fs(txn_root); - fs_path = svn_fs_path(fs, pool); + if (conflict_p) + *conflict_p = NULL; + + 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(txn->vtable->commit(conflict_p, new_rev, txn, pool)); + SVN_ERR(err); #ifdef PACK_AFTER_EVERY_COMMIT { - svn_error_t *err = svn_fs_pack(fs_path, NULL, NULL, NULL, NULL, pool); + svn_fs_t *fs = txn->fs; + const char *fs_path = svn_fs_path(fs, 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); @@ -986,6 +1097,21 @@ svn_fs_closest_copy(svn_fs_root_t **root_p, const char **path_p, } svn_error_t * +svn_fs_get_mergeinfo2(svn_mergeinfo_catalog_t *catalog, + svn_fs_root_t *root, + const apr_array_header_t *paths, + svn_mergeinfo_inheritance_t inherit, + svn_boolean_t include_descendants, + svn_boolean_t adjust_inherited_mergeinfo, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + return svn_error_trace(root->vtable->get_mergeinfo( + catalog, root, paths, inherit, include_descendants, + adjust_inherited_mergeinfo, result_pool, scratch_pool)); +} + +svn_error_t * svn_fs_get_mergeinfo(svn_mergeinfo_catalog_t *catalog, svn_fs_root_t *root, const apr_array_header_t *paths, @@ -996,7 +1122,7 @@ svn_fs_get_mergeinfo(svn_mergeinfo_catalog_t *catalog, return svn_error_trace(root->vtable->get_mergeinfo(catalog, root, paths, inherit, include_descendants, - pool)); + TRUE, pool, pool)); } svn_error_t * @@ -1108,6 +1234,27 @@ svn_fs_file_contents(svn_stream_t **contents, svn_fs_root_t *root, } svn_error_t * +svn_fs_try_process_file_contents(svn_boolean_t *success, + svn_fs_root_t *root, + const char *path, + svn_fs_process_contents_func_t processor, + void* baton, + apr_pool_t *pool) +{ + /* if the FS doesn't implement this function, report a "failed" attempt */ + if (root->vtable->try_process_file_contents == NULL) + { + *success = FALSE; + return SVN_NO_ERROR; + } + + return svn_error_trace(root->vtable->try_process_file_contents( + success, + root, path, + processor, baton, pool)); +} + +svn_error_t * svn_fs_make_file(svn_fs_root_t *root, const char *path, apr_pool_t *pool) { SVN_ERR(svn_fs__path_valid(path, pool)); @@ -1227,7 +1374,9 @@ svn_fs_get_file_delta_stream(svn_txdelta_stream_t **stream_p, 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 * @@ -1404,11 +1553,11 @@ svn_error_t * svn_fs_print_modules(svn_stringbuf_t *output, apr_pool_t *pool) { - const struct fs_type_defn *defn; + const struct fs_type_defn *defn = fs_modules; fs_library_vtable_t *vtable; apr_pool_t *iterpool = svn_pool_create(pool); - for (defn = fs_modules; defn->fs_type != NULL; ++defn) + while (defn) { char *line; svn_error_t *err; @@ -1421,6 +1570,7 @@ svn_fs_print_modules(svn_stringbuf_t *output, if (err->apr_err == SVN_ERR_FS_UNKNOWN_FS_TYPE) { svn_error_clear(err); + defn = defn->next; continue; } else @@ -1430,6 +1580,7 @@ svn_fs_print_modules(svn_stringbuf_t *output, line = apr_psprintf(iterpool, "* fs_%s : %s\n", defn->fsap_name, vtable->get_description()); svn_stringbuf_appendcstr(output, line); + defn = defn->next; } svn_pool_destroy(iterpool); diff --git a/subversion/libsvn_fs/fs-loader.h b/subversion/libsvn_fs/fs-loader.h index 0f01931..532ff05 100644 --- a/subversion/libsvn_fs/fs-loader.h +++ b/subversion/libsvn_fs/fs-loader.h @@ -25,7 +25,7 @@ #ifndef LIBSVN_FS_FS_H #define LIBSVN_FS_FS_H -#include "svn_version.h" +#include "svn_types.h" #include "svn_fs.h" #ifdef __cplusplus @@ -36,13 +36,14 @@ extern "C" { /* The FS loader library implements the a front end to "filesystem abstract providers" (FSAPs), which implement the svn_fs API. - The loader library divides up the FS API into five categories: + The loader library divides up the FS API into several categories: - Top-level functions, which operate on paths to an FS - Functions which operate on an FS object - Functions which operate on a transaction object - Functions which operate on a root object - Functions which operate on a history object + - Functions which operate on a noderev-ID object Some generic fields of the FS, transaction, root, and history objects are defined by the loader library; the rest are stored in @@ -86,9 +87,21 @@ typedef struct fs_library_vtable_t apr_pool_t *common_pool); svn_error_t *(*upgrade_fs)(svn_fs_t *fs, const char *path, apr_pool_t *pool, apr_pool_t *common_pool); + svn_error_t *(*verify_fs)(svn_fs_t *fs, const char *path, + svn_revnum_t start, + svn_revnum_t end, + svn_fs_progress_notify_func_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool, + apr_pool_t *common_pool); svn_error_t *(*delete_fs)(const char *path, apr_pool_t *pool); - svn_error_t *(*hotcopy)(const char *src_path, const char *dest_path, - svn_boolean_t clean, apr_pool_t *pool); + svn_error_t *(*hotcopy)(svn_fs_t *src_fs, svn_fs_t *dst_fs, + const char *src_path, const char *dst_path, + svn_boolean_t clean, svn_boolean_t incremental, + svn_cancel_func_t cancel_func, void *cancel_baton, + apr_pool_t *pool); const char *(*get_description)(void); svn_error_t *(*recover)(svn_fs_t *fs, svn_cancel_func_t cancel_func, void *cancel_baton, @@ -96,7 +109,7 @@ typedef struct fs_library_vtable_t svn_error_t *(*pack_fs)(svn_fs_t *fs, const char *path, svn_fs_pack_notify_t notify_func, void *notify_baton, svn_cancel_func_t cancel_func, void *cancel_baton, - apr_pool_t *pool); + apr_pool_t *pool, apr_pool_t *common_pool); /* Provider-specific functions should go here, even if they could go in an object vtable, so that they are all kept together. */ @@ -111,6 +124,14 @@ typedef struct fs_library_vtable_t into the FS vtable. */ svn_fs_id_t *(*parse_id)(const char *data, apr_size_t len, apr_pool_t *pool); + /* Allow an FSAP to call svn_fs_open(), which is in a higher-level library + (libsvn_fs-1.so) and cannot easily be moved to libsvn_fs_util. */ + svn_error_t *(*set_svn_fs_open)(svn_fs_t *fs, + svn_error_t *(*svn_fs_open_)(svn_fs_t **, + const char *, + apr_hash_t *, + apr_pool_t *)); + } fs_library_vtable_t; /* This is the type of symbol an FS module defines to fetch the @@ -161,7 +182,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); @@ -191,6 +212,11 @@ typedef struct fs_vtable_t svn_fs_get_locks_callback_t get_locks_func, void *get_locks_baton, apr_pool_t *pool); + svn_error_t *(*verify_root)(svn_fs_root_t *root, + apr_pool_t *pool); + svn_error_t *(*freeze)(svn_fs_t *fs, + svn_fs_freeze_func_t freeze_func, + void *freeze_baton, apr_pool_t *pool); svn_error_t *(*bdb_set_errcall)(svn_fs_t *fs, void (*handler)(const char *errpfx, char *msg)); @@ -292,6 +318,12 @@ typedef struct root_vtable_t svn_error_t *(*file_contents)(svn_stream_t **contents, svn_fs_root_t *root, const char *path, apr_pool_t *pool); + svn_error_t *(*try_process_file_contents)(svn_boolean_t *success, + svn_fs_root_t *target_root, + const char *target_path, + svn_fs_process_contents_func_t processor, + void* baton, + apr_pool_t *pool); svn_error_t *(*make_file)(svn_fs_root_t *root, const char *path, apr_pool_t *pool); svn_error_t *(*apply_textdelta)(svn_txdelta_window_handler_t *contents_p, @@ -322,12 +354,15 @@ typedef struct root_vtable_t svn_fs_root_t *ancestor_root, const char *ancestor_path, apr_pool_t *pool); + /* Mergeinfo. */ svn_error_t *(*get_mergeinfo)(svn_mergeinfo_catalog_t *catalog, svn_fs_root_t *root, const apr_array_header_t *paths, svn_mergeinfo_inheritance_t inherit, svn_boolean_t include_descendants, - apr_pool_t *pool); + svn_boolean_t adjust_inherited_mergeinfo, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); } root_vtable_t; @@ -377,6 +412,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; }; |