diff options
Diffstat (limited to 'subversion/libsvn_fs')
-rw-r--r-- | subversion/libsvn_fs/access.c | 105 | ||||
-rw-r--r-- | subversion/libsvn_fs/editor.c | 850 | ||||
-rw-r--r-- | subversion/libsvn_fs/fs-loader.c | 1602 | ||||
-rw-r--r-- | subversion/libsvn_fs/fs-loader.h | 502 |
4 files changed, 3059 insertions, 0 deletions
diff --git a/subversion/libsvn_fs/access.c b/subversion/libsvn_fs/access.c new file mode 100644 index 0000000000000..9918be4c4f7ff --- /dev/null +++ b/subversion/libsvn_fs/access.c @@ -0,0 +1,105 @@ +/* + * access.c: shared code to manipulate svn_fs_access_t objects + * + * ==================================================================== + * 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_hash.h> + +#include "svn_hash.h" +#include "svn_types.h" +#include "svn_pools.h" +#include "svn_fs.h" +#include "private/svn_fs_private.h" + +#include "fs-loader.h" + + + +svn_error_t * +svn_fs_create_access(svn_fs_access_t **access_ctx, + const char *username, + apr_pool_t *pool) +{ + svn_fs_access_t *ac; + + SVN_ERR_ASSERT(username != NULL); + + ac = apr_pcalloc(pool, sizeof(*ac)); + ac->username = apr_pstrdup(pool, username); + ac->lock_tokens = apr_hash_make(pool); + *access_ctx = ac; + + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_set_access(svn_fs_t *fs, + svn_fs_access_t *access_ctx) +{ + fs->access_ctx = access_ctx; + + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_get_access(svn_fs_access_t **access_ctx, + svn_fs_t *fs) +{ + *access_ctx = fs->access_ctx; + + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_access_get_username(const char **username, + svn_fs_access_t *access_ctx) +{ + *username = access_ctx->username; + + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_access_add_lock_token2(svn_fs_access_t *access_ctx, + const char *path, + const char *token) +{ + svn_hash_sets(access_ctx->lock_tokens, token, path); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_access_add_lock_token(svn_fs_access_t *access_ctx, + const char *token) +{ + return svn_fs_access_add_lock_token2(access_ctx, (const char *) 1, token); +} + +apr_hash_t * +svn_fs__access_get_lock_tokens(svn_fs_access_t *access_ctx) +{ + return access_ctx->lock_tokens; +} diff --git a/subversion/libsvn_fs/editor.c b/subversion/libsvn_fs/editor.c new file mode 100644 index 0000000000000..a75f21043c1cd --- /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 new file mode 100644 index 0000000000000..01d6ba1afc12c --- /dev/null +++ b/subversion/libsvn_fs/fs-loader.c @@ -0,0 +1,1602 @@ +/* + * fs_loader.c: Front-end to the various FS back ends + * + * ==================================================================== + * 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 <string.h> +#include <apr.h> +#include <apr_hash.h> +#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" +#include "svn_version.h" +#include "svn_fs.h" +#include "svn_path.h" +#include "svn_xml.h" +#include "svn_pools.h" +#include "svn_string.h" +#include "svn_private_config.h" + +#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" + +/* This is defined by configure on platforms which use configure, but + we need to define a fallback for Windows. */ +#ifndef DEFAULT_FS_TYPE +#define DEFAULT_FS_TYPE "fsfs" +#endif + +#define FS_TYPE_FILENAME "fs-type" + +/* 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; +svn_mutex__t *common_pool_lock; + + +/* --- Utility functions for the loader --- */ + +struct fs_type_defn { + const char *fs_type; + const char *fsap_name; + fs_init_func_t initfunc; + 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, +#else + NULL, +#endif + NULL + }; + +static struct fs_type_defn fsfs_defn = + { + SVN_FS_TYPE_FSFS, "fs", +#ifdef SVN_LIBSVN_FS_LINKS_FS_FS + svn_fs_fs__init, +#else + NULL, +#endif + &base_defn + }; + +static struct fs_type_defn *fs_modules = &fsfs_defn; + + +static svn_error_t * +load_module(fs_init_func_t *initfunc, const char *name, apr_pool_t *pool) +{ + *initfunc = NULL; + +#if defined(SVN_USE_DSO) && APR_HAS_DSO + { + apr_dso_handle_t *dso; + apr_dso_handle_sym_t symbol; + const char *libname; + const char *funcname; + apr_status_t status; + 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 + the library doesn't exist. The library will be unloaded when + pool is destroyed. */ + SVN_ERR(svn_dso_load(&dso, libname)); + if (! dso) + return SVN_NO_ERROR; + + /* find the initialization routine */ + status = apr_dso_sym(&symbol, dso, funcname); + if (status) + return svn_error_wrap_apr(status, _("'%s' does not define '%s()'"), + libname, funcname); + + *initfunc = (fs_init_func_t) symbol; + } +#endif /* APR_HAS_DSO */ + + 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, + const struct fs_type_defn *fst, + apr_pool_t *pool) +{ + fs_init_func_t initfunc = NULL; + const svn_version_t *my_version = svn_fs_version(); + const svn_version_t *fs_version; + + initfunc = fst->initfunc; + if (! initfunc) + SVN_ERR(load_module(&initfunc, fst->fsap_name, pool)); + + if (! initfunc) + return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL, + _("Failed to load module for FS type '%s'"), + fst->fs_type); + + { + /* 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, + nor can we clean up the common pool if libsvn_fs is dynamically + unloaded. This function makes a best effort by creating the + common pool as a child of the global pool; the window of failure + due to thread collision is small. */ + if (!common_pool) + SVN_ERR(svn_fs_initialize(NULL)); + + /* Invoke the FS module's initfunc function with the common + pool protected by a lock. */ + 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)) + return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL, + _("Mismatched FS module version for '%s':" + " found %d.%d.%d%s," + " expected %d.%d.%d%s"), + fst->fs_type, + my_version->major, my_version->minor, + my_version->patch, my_version->tag, + fs_version->major, fs_version->minor, + fs_version->patch, fs_version->tag); + 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) +{ + struct fs_type_defn **fst = &fs_modules; + svn_boolean_t known = FALSE; + + /* 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 + { + fst = &(*fst)->next; + if (strcmp(fs_type, (*fst)->fs_type) == 0) + known = TRUE; + } + +#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 * +svn_fs_type(const char **fs_type, const char *path, apr_pool_t *pool) +{ + const char *filename; + char buf[128]; + svn_error_t *err; + apr_file_t *file; + apr_size_t len; + + /* Read the fsap-name file to get the FSAP name, or assume the (old) + default. For old repositories I suppose we could check some + other file, DB_CONFIG or strings say, but for now just check the + directory exists. */ + filename = svn_dirent_join(path, FS_TYPE_FILENAME, pool); + err = svn_io_file_open(&file, filename, APR_READ|APR_BUFFERED, 0, pool); + if (err && APR_STATUS_IS_ENOENT(err->apr_err)) + { + svn_node_kind_t kind; + svn_error_t *err2 = svn_io_check_path(path, &kind, pool); + if (err2) + { + svn_error_clear(err2); + return err; + } + if (kind == svn_node_dir) + { + svn_error_clear(err); + *fs_type = SVN_FS_TYPE_BDB; + return SVN_NO_ERROR; + } + return err; + } + else if (err) + return err; + + len = sizeof(buf); + SVN_ERR(svn_io_read_length_line(file, buf, &len, pool)); + SVN_ERR(svn_io_file_close(file, pool)); + *fs_type = apr_pstrdup(pool, buf); + + return SVN_NO_ERROR; +} + +/* Fetch the library vtable for an existing FS. */ +static svn_error_t * +fs_library_vtable(fs_library_vtable_t **vtable, const char *path, + apr_pool_t *pool) +{ + const char *fs_type; + + SVN_ERR(svn_fs_type(&fs_type, path, pool)); + + /* Fetch the library vtable by name, now that we've chosen one. */ + return svn_error_trace(get_library_vtable(vtable, fs_type, pool)); +} + +static svn_error_t * +write_fs_type(const char *path, const char *fs_type, apr_pool_t *pool) +{ + const char *filename; + apr_file_t *file; + + filename = svn_dirent_join(path, FS_TYPE_FILENAME, pool); + SVN_ERR(svn_io_file_open(&file, filename, + APR_WRITE|APR_CREATE|APR_TRUNCATE|APR_BUFFERED, + APR_OS_DEFAULT, pool)); + SVN_ERR(svn_io_file_write_full(file, fs_type, strlen(fs_type), NULL, + pool)); + SVN_ERR(svn_io_file_write_full(file, "\n", 1, NULL, pool)); + return svn_error_trace(svn_io_file_close(file, pool)); +} + + +/* --- Functions for operating on filesystems by pathname --- */ + +static apr_status_t uninit(void *data) +{ + common_pool = NULL; + return APR_SUCCESS; +} + +svn_error_t * +svn_fs_initialize(apr_pool_t *pool) +{ + /* Protect against multiple calls. */ + if (common_pool) + return SVN_NO_ERROR; + + common_pool = svn_pool_create(pool); + 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 + ### longer than the DSO, which gets unloaded when the pool used to + ### load it is cleared, and so when the handler runs it will refer to + ### a function that no longer exists. libsvn_ra_local attempts to + ### work around this by explicitly calling svn_fs_initialize. */ + apr_pool_cleanup_register(common_pool, NULL, uninit, apr_pool_cleanup_null); + return SVN_NO_ERROR; +} + +/* A default warning handling function. */ +static void +default_warning_func(void *baton, svn_error_t *err) +{ + /* The one unforgiveable sin is to fail silently. Dumping to stderr + or /dev/tty is not acceptable default behavior for server + processes, since those may both be equivalent to /dev/null. */ + SVN_ERR_MALFUNCTION_NO_RETURN(); +} + +svn_error_t * +svn_fs__path_valid(const char *path, apr_pool_t *pool) +{ + /* UTF-8 encoded string without NULs. */ + if (! svn_utf__cstring_is_valid(path)) + { + return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL, + _("Path '%s' is not in UTF-8"), path); + } + + /* No "." or ".." elements. */ + if (svn_path_is_backpath_present(path) + || svn_path_is_dotpath_present(path)) + { + return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL, + _("Path '%s' contains '.' or '..' element"), + path); + } + + /* That's good enough. */ + return SVN_NO_ERROR; +} + +/* Allocate svn_fs_t structure. */ +static svn_fs_t * +fs_new(apr_hash_t *fs_config, apr_pool_t *pool) +{ + svn_fs_t *fs = apr_palloc(pool, sizeof(*fs)); + fs->pool = pool; + fs->path = NULL; + fs->warning = default_warning_func; + fs->warning_baton = NULL; + fs->config = fs_config; + fs->access_ctx = NULL; + fs->vtable = NULL; + fs->fsap_data = NULL; + fs->uuid = NULL; + return fs; +} + +svn_fs_t * +svn_fs_new(apr_hash_t *fs_config, apr_pool_t *pool) +{ + return fs_new(fs_config, pool); +} + +void +svn_fs_set_warning_func(svn_fs_t *fs, svn_fs_warning_callback_t warning, + void *warning_baton) +{ + fs->warning = warning; + fs->warning_baton = warning_baton; +} + +svn_error_t * +svn_fs_create(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config, + apr_pool_t *pool) +{ + fs_library_vtable_t *vtable; + + const char *fs_type = svn_hash__get_cstring(fs_config, + SVN_FS_CONFIG_FS_TYPE, + DEFAULT_FS_TYPE); + SVN_ERR(get_library_vtable(&vtable, fs_type, pool)); + + /* Create the FS directory and write out the fsap-name file. */ + SVN_ERR(svn_io_dir_make_sgid(path, APR_OS_DEFAULT, pool)); + SVN_ERR(write_fs_type(path, fs_type, pool)); + + /* Perform the actual creation. */ + *fs_p = fs_new(fs_config, pool); + + 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) +{ + fs_library_vtable_t *vtable; + + SVN_ERR(fs_library_vtable(&vtable, path, pool)); + *fs_p = fs_new(fs_config, pool); + 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) +{ + fs_library_vtable_t *vtable; + svn_fs_t *fs; + + SVN_ERR(fs_library_vtable(&vtable, path, pool)); + fs = fs_new(NULL, pool); + + 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 * +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) +{ + fs_library_vtable_t *vtable; + + SVN_ERR(fs_library_vtable(&vtable, path, pool)); + return svn_error_trace(vtable->delete_fs(path, pool)); +} + +svn_error_t * +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 *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_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 * +svn_fs_pack(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) +{ + fs_library_vtable_t *vtable; + svn_fs_t *fs; + + SVN_ERR(fs_library_vtable(&vtable, path, pool)); + fs = fs_new(NULL, pool); + + 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 * +svn_fs_recover(const char *path, + 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(NULL, pool); + + 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; +} + + +/* --- Berkeley-specific functions --- */ + +svn_error_t * +svn_fs_create_berkeley(svn_fs_t *fs, const char *path) +{ + fs_library_vtable_t *vtable; + + SVN_ERR(get_library_vtable(&vtable, SVN_FS_TYPE_BDB, fs->pool)); + + /* Create the FS directory and write out the fsap-name file. */ + SVN_ERR(svn_io_dir_make_sgid(path, APR_OS_DEFAULT, fs->pool)); + SVN_ERR(write_fs_type(path, SVN_FS_TYPE_BDB, fs->pool)); + + /* Perform the actual creation. */ + 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) +{ + fs_library_vtable_t *vtable; + + SVN_ERR(fs_library_vtable(&vtable, path, fs->pool)); + 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 * +svn_fs_berkeley_path(svn_fs_t *fs, apr_pool_t *pool) +{ + return svn_fs_path(fs, pool); +} + +svn_error_t * +svn_fs_delete_berkeley(const char *path, apr_pool_t *pool) +{ + return svn_error_trace(svn_fs_delete_fs(path, pool)); +} + +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_hotcopy2(src_path, dest_path, clean_logs, + FALSE, NULL, NULL, pool)); +} + +svn_error_t * +svn_fs_berkeley_recover(const char *path, apr_pool_t *pool) +{ + return svn_error_trace(svn_fs_recover(path, NULL, NULL, pool)); +} + +svn_error_t * +svn_fs_set_berkeley_errcall(svn_fs_t *fs, + void (*handler)(const char *errpfx, char *msg)) +{ + return svn_error_trace(fs->vtable->bdb_set_errcall(fs, handler)); +} + +svn_error_t * +svn_fs_berkeley_logfiles(apr_array_header_t **logfiles, + const char *path, + svn_boolean_t only_unused, + apr_pool_t *pool) +{ + fs_library_vtable_t *vtable; + + SVN_ERR(fs_library_vtable(&vtable, path, pool)); + return svn_error_trace(vtable->bdb_logfiles(logfiles, path, only_unused, + pool)); +} + + +/* --- Transaction functions --- */ + +svn_error_t * +svn_fs_begin_txn2(svn_fs_txn_t **txn_p, svn_fs_t *fs, svn_revnum_t rev, + apr_uint32_t flags, apr_pool_t *pool) +{ + return svn_error_trace(fs->vtable->begin_txn(txn_p, fs, rev, flags, pool)); +} + + +svn_error_t * +svn_fs_begin_txn(svn_fs_txn_t **txn_p, svn_fs_t *fs, svn_revnum_t rev, + apr_pool_t *pool) +{ + return svn_error_trace(svn_fs_begin_txn2(txn_p, fs, rev, 0, pool)); +} + + +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; + + *new_rev = SVN_INVALID_REVNUM; + 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(err); + +#ifdef PACK_AFTER_EVERY_COMMIT + { + 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); + else if (err) + /* Real error. */ + return svn_error_trace(err); + } +#endif + + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_fs_abort_txn(svn_fs_txn_t *txn, apr_pool_t *pool) +{ + return svn_error_trace(txn->vtable->abort(txn, pool)); +} + +svn_error_t * +svn_fs_purge_txn(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool) +{ + return svn_error_trace(fs->vtable->purge_txn(fs, txn_id, pool)); +} + +svn_error_t * +svn_fs_txn_name(const char **name_p, svn_fs_txn_t *txn, apr_pool_t *pool) +{ + *name_p = apr_pstrdup(pool, txn->id); + return SVN_NO_ERROR; +} + +svn_revnum_t +svn_fs_txn_base_revision(svn_fs_txn_t *txn) +{ + return txn->base_rev; +} + +svn_error_t * +svn_fs_open_txn(svn_fs_txn_t **txn, svn_fs_t *fs, const char *name, + apr_pool_t *pool) +{ + return svn_error_trace(fs->vtable->open_txn(txn, fs, name, pool)); +} + +svn_error_t * +svn_fs_list_transactions(apr_array_header_t **names_p, svn_fs_t *fs, + apr_pool_t *pool) +{ + return svn_error_trace(fs->vtable->list_transactions(names_p, fs, pool)); +} + +svn_error_t * +svn_fs_txn_prop(svn_string_t **value_p, svn_fs_txn_t *txn, + const char *propname, apr_pool_t *pool) +{ + return svn_error_trace(txn->vtable->get_prop(value_p, txn, propname, pool)); +} + +svn_error_t * +svn_fs_txn_proplist(apr_hash_t **table_p, svn_fs_txn_t *txn, apr_pool_t *pool) +{ + return svn_error_trace(txn->vtable->get_proplist(table_p, txn, pool)); +} + +svn_error_t * +svn_fs_change_txn_prop(svn_fs_txn_t *txn, const char *name, + const svn_string_t *value, apr_pool_t *pool) +{ + return svn_error_trace(txn->vtable->change_prop(txn, name, value, pool)); +} + +svn_error_t * +svn_fs_change_txn_props(svn_fs_txn_t *txn, const apr_array_header_t *props, + apr_pool_t *pool) +{ + return svn_error_trace(txn->vtable->change_props(txn, props, pool)); +} + + +/* --- Root functions --- */ + +svn_error_t * +svn_fs_revision_root(svn_fs_root_t **root_p, svn_fs_t *fs, svn_revnum_t rev, + apr_pool_t *pool) +{ + /* We create a subpool for each root object to allow us to implement + svn_fs_close_root. */ + apr_pool_t *subpool = svn_pool_create(pool); + return svn_error_trace(fs->vtable->revision_root(root_p, fs, rev, subpool)); +} + +svn_error_t * +svn_fs_txn_root(svn_fs_root_t **root_p, svn_fs_txn_t *txn, apr_pool_t *pool) +{ + /* We create a subpool for each root object to allow us to implement + svn_fs_close_root. */ + apr_pool_t *subpool = svn_pool_create(pool); + return svn_error_trace(txn->vtable->root(root_p, txn, subpool)); +} + +void +svn_fs_close_root(svn_fs_root_t *root) +{ + svn_pool_destroy(root->pool); +} + +svn_fs_t * +svn_fs_root_fs(svn_fs_root_t *root) +{ + return root->fs; +} + +svn_boolean_t +svn_fs_is_txn_root(svn_fs_root_t *root) +{ + return root->is_txn_root; +} + +svn_boolean_t +svn_fs_is_revision_root(svn_fs_root_t *root) +{ + return !root->is_txn_root; +} + +const char * +svn_fs_txn_root_name(svn_fs_root_t *root, apr_pool_t *pool) +{ + return root->is_txn_root ? apr_pstrdup(pool, root->txn) : NULL; +} + +svn_revnum_t +svn_fs_txn_root_base_revision(svn_fs_root_t *root) +{ + return root->is_txn_root ? root->rev : SVN_INVALID_REVNUM; +} + +svn_revnum_t +svn_fs_revision_root_revision(svn_fs_root_t *root) +{ + return root->is_txn_root ? SVN_INVALID_REVNUM : root->rev; +} + +svn_error_t * +svn_fs_paths_changed2(apr_hash_t **changed_paths_p, svn_fs_root_t *root, + apr_pool_t *pool) +{ + return root->vtable->paths_changed(changed_paths_p, root, pool); +} + +svn_error_t * +svn_fs_paths_changed(apr_hash_t **changed_paths_p, svn_fs_root_t *root, + apr_pool_t *pool) +{ + apr_hash_t *changed_paths_new_structs; + apr_hash_index_t *hi; + + SVN_ERR(svn_fs_paths_changed2(&changed_paths_new_structs, root, pool)); + *changed_paths_p = apr_hash_make(pool); + for (hi = apr_hash_first(pool, changed_paths_new_structs); + hi; + hi = apr_hash_next(hi)) + { + const void *vkey; + apr_ssize_t klen; + void *vval; + svn_fs_path_change2_t *val; + svn_fs_path_change_t *change; + apr_hash_this(hi, &vkey, &klen, &vval); + val = vval; + change = apr_palloc(pool, sizeof(*change)); + change->node_rev_id = val->node_rev_id; + change->change_kind = val->change_kind; + change->text_mod = val->text_mod; + change->prop_mod = val->prop_mod; + apr_hash_set(*changed_paths_p, vkey, klen, change); + } + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_check_path(svn_node_kind_t *kind_p, svn_fs_root_t *root, + const char *path, apr_pool_t *pool) +{ + return svn_error_trace(root->vtable->check_path(kind_p, root, path, pool)); +} + +svn_error_t * +svn_fs_node_history(svn_fs_history_t **history_p, svn_fs_root_t *root, + const char *path, apr_pool_t *pool) +{ + return svn_error_trace(root->vtable->node_history(history_p, root, path, + pool)); +} + +svn_error_t * +svn_fs_is_dir(svn_boolean_t *is_dir, svn_fs_root_t *root, const char *path, + apr_pool_t *pool) +{ + svn_node_kind_t kind; + + SVN_ERR(root->vtable->check_path(&kind, root, path, pool)); + *is_dir = (kind == svn_node_dir); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_is_file(svn_boolean_t *is_file, svn_fs_root_t *root, const char *path, + apr_pool_t *pool) +{ + svn_node_kind_t kind; + + SVN_ERR(root->vtable->check_path(&kind, root, path, pool)); + *is_file = (kind == svn_node_file); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_node_id(const svn_fs_id_t **id_p, svn_fs_root_t *root, + const char *path, apr_pool_t *pool) +{ + return svn_error_trace(root->vtable->node_id(id_p, root, path, pool)); +} + +svn_error_t * +svn_fs_node_created_rev(svn_revnum_t *revision, svn_fs_root_t *root, + const char *path, apr_pool_t *pool) +{ + return svn_error_trace(root->vtable->node_created_rev(revision, root, path, + pool)); +} + +svn_error_t * +svn_fs_node_origin_rev(svn_revnum_t *revision, svn_fs_root_t *root, + const char *path, apr_pool_t *pool) +{ + return svn_error_trace(root->vtable->node_origin_rev(revision, root, path, + pool)); +} + +svn_error_t * +svn_fs_node_created_path(const char **created_path, svn_fs_root_t *root, + const char *path, apr_pool_t *pool) +{ + return svn_error_trace(root->vtable->node_created_path(created_path, root, + path, pool)); +} + +svn_error_t * +svn_fs_node_prop(svn_string_t **value_p, svn_fs_root_t *root, + const char *path, const char *propname, apr_pool_t *pool) +{ + return svn_error_trace(root->vtable->node_prop(value_p, root, path, + propname, pool)); +} + +svn_error_t * +svn_fs_node_proplist(apr_hash_t **table_p, svn_fs_root_t *root, + const char *path, apr_pool_t *pool) +{ + return svn_error_trace(root->vtable->node_proplist(table_p, root, path, + pool)); +} + +svn_error_t * +svn_fs_change_node_prop(svn_fs_root_t *root, const char *path, + const char *name, const svn_string_t *value, + apr_pool_t *pool) +{ + return svn_error_trace(root->vtable->change_node_prop(root, path, name, + value, pool)); +} + +svn_error_t * +svn_fs_props_changed(svn_boolean_t *changed_p, svn_fs_root_t *root1, + const char *path1, svn_fs_root_t *root2, + const char *path2, apr_pool_t *pool) +{ + return svn_error_trace(root1->vtable->props_changed(changed_p, + root1, path1, + root2, path2, + pool)); +} + +svn_error_t * +svn_fs_copied_from(svn_revnum_t *rev_p, const char **path_p, + svn_fs_root_t *root, const char *path, apr_pool_t *pool) +{ + return svn_error_trace(root->vtable->copied_from(rev_p, path_p, root, path, + pool)); +} + +svn_error_t * +svn_fs_closest_copy(svn_fs_root_t **root_p, const char **path_p, + svn_fs_root_t *root, const char *path, apr_pool_t *pool) +{ + return svn_error_trace(root->vtable->closest_copy(root_p, path_p, + root, path, pool)); +} + +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, + svn_mergeinfo_inheritance_t inherit, + svn_boolean_t include_descendants, + apr_pool_t *pool) +{ + return svn_error_trace(root->vtable->get_mergeinfo(catalog, root, paths, + inherit, + include_descendants, + TRUE, pool, pool)); +} + +svn_error_t * +svn_fs_merge(const char **conflict_p, svn_fs_root_t *source_root, + const char *source_path, svn_fs_root_t *target_root, + const char *target_path, svn_fs_root_t *ancestor_root, + const char *ancestor_path, apr_pool_t *pool) +{ + return svn_error_trace(target_root->vtable->merge(conflict_p, + source_root, source_path, + target_root, target_path, + ancestor_root, + ancestor_path, pool)); +} + +svn_error_t * +svn_fs_dir_entries(apr_hash_t **entries_p, svn_fs_root_t *root, + const char *path, apr_pool_t *pool) +{ + return svn_error_trace(root->vtable->dir_entries(entries_p, root, path, + pool)); +} + +svn_error_t * +svn_fs_make_dir(svn_fs_root_t *root, const char *path, apr_pool_t *pool) +{ + SVN_ERR(svn_fs__path_valid(path, pool)); + return svn_error_trace(root->vtable->make_dir(root, path, pool)); +} + +svn_error_t * +svn_fs_delete(svn_fs_root_t *root, const char *path, apr_pool_t *pool) +{ + return svn_error_trace(root->vtable->delete_node(root, path, pool)); +} + +svn_error_t * +svn_fs_copy(svn_fs_root_t *from_root, const char *from_path, + svn_fs_root_t *to_root, const char *to_path, apr_pool_t *pool) +{ + SVN_ERR(svn_fs__path_valid(to_path, pool)); + return svn_error_trace(to_root->vtable->copy(from_root, from_path, + to_root, to_path, pool)); +} + +svn_error_t * +svn_fs_revision_link(svn_fs_root_t *from_root, svn_fs_root_t *to_root, + const char *path, apr_pool_t *pool) +{ + return svn_error_trace(to_root->vtable->revision_link(from_root, to_root, + path, pool)); +} + +svn_error_t * +svn_fs_file_length(svn_filesize_t *length_p, svn_fs_root_t *root, + const char *path, apr_pool_t *pool) +{ + return svn_error_trace(root->vtable->file_length(length_p, root, path, + pool)); +} + +svn_error_t * +svn_fs_file_checksum(svn_checksum_t **checksum, + svn_checksum_kind_t kind, + svn_fs_root_t *root, + const char *path, + svn_boolean_t force, + apr_pool_t *pool) +{ + SVN_ERR(root->vtable->file_checksum(checksum, kind, root, path, pool)); + + if (force && (*checksum == NULL || (*checksum)->kind != kind)) + { + svn_stream_t *contents, *checksum_contents; + + SVN_ERR(svn_fs_file_contents(&contents, root, path, pool)); + checksum_contents = svn_stream_checksummed2(contents, checksum, NULL, + kind, TRUE, pool); + + /* This will force a read of any remaining data (which is all of it in + this case) and dump the checksum into checksum->digest. */ + SVN_ERR(svn_stream_close(checksum_contents)); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_file_md5_checksum(unsigned char digest[], + svn_fs_root_t *root, + const char *path, + apr_pool_t *pool) +{ + svn_checksum_t *md5sum; + + SVN_ERR(svn_fs_file_checksum(&md5sum, svn_checksum_md5, root, path, TRUE, + pool)); + memcpy(digest, md5sum->digest, APR_MD5_DIGESTSIZE); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_file_contents(svn_stream_t **contents, svn_fs_root_t *root, + const char *path, apr_pool_t *pool) +{ + return svn_error_trace(root->vtable->file_contents(contents, root, path, + pool)); +} + +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)); + return svn_error_trace(root->vtable->make_file(root, path, pool)); +} + +svn_error_t * +svn_fs_apply_textdelta(svn_txdelta_window_handler_t *contents_p, + void **contents_baton_p, svn_fs_root_t *root, + const char *path, const char *base_checksum, + const char *result_checksum, apr_pool_t *pool) +{ + svn_checksum_t *base, *result; + + /* TODO: If we ever rev this API, we should make the supplied checksums + svn_checksum_t structs. */ + SVN_ERR(svn_checksum_parse_hex(&base, svn_checksum_md5, base_checksum, + pool)); + SVN_ERR(svn_checksum_parse_hex(&result, svn_checksum_md5, result_checksum, + pool)); + + return svn_error_trace(root->vtable->apply_textdelta(contents_p, + contents_baton_p, + root, + path, + base, + result, + pool)); +} + +svn_error_t * +svn_fs_apply_text(svn_stream_t **contents_p, svn_fs_root_t *root, + const char *path, const char *result_checksum, + apr_pool_t *pool) +{ + svn_checksum_t *result; + + /* TODO: If we ever rev this API, we should make the supplied checksum an + svn_checksum_t struct. */ + SVN_ERR(svn_checksum_parse_hex(&result, svn_checksum_md5, result_checksum, + pool)); + + return svn_error_trace(root->vtable->apply_text(contents_p, root, path, + result, pool)); +} + +svn_error_t * +svn_fs_contents_changed(svn_boolean_t *changed_p, svn_fs_root_t *root1, + const char *path1, svn_fs_root_t *root2, + const char *path2, apr_pool_t *pool) +{ + return svn_error_trace(root1->vtable->contents_changed(changed_p, + root1, path1, + root2, path2, + pool)); +} + +svn_error_t * +svn_fs_youngest_rev(svn_revnum_t *youngest_p, svn_fs_t *fs, apr_pool_t *pool) +{ + return svn_error_trace(fs->vtable->youngest_rev(youngest_p, fs, pool)); +} + +svn_error_t * +svn_fs_deltify_revision(svn_fs_t *fs, svn_revnum_t revision, apr_pool_t *pool) +{ + return svn_error_trace(fs->vtable->deltify(fs, revision, pool)); +} + +svn_error_t * +svn_fs_revision_prop(svn_string_t **value_p, svn_fs_t *fs, svn_revnum_t rev, + const char *propname, apr_pool_t *pool) +{ + return svn_error_trace(fs->vtable->revision_prop(value_p, fs, rev, + propname, pool)); +} + +svn_error_t * +svn_fs_revision_proplist(apr_hash_t **table_p, svn_fs_t *fs, svn_revnum_t rev, + apr_pool_t *pool) +{ + return svn_error_trace(fs->vtable->revision_proplist(table_p, fs, rev, + pool)); +} + +svn_error_t * +svn_fs_change_rev_prop2(svn_fs_t *fs, svn_revnum_t rev, const char *name, + const svn_string_t *const *old_value_p, + const svn_string_t *value, apr_pool_t *pool) +{ + return svn_error_trace(fs->vtable->change_rev_prop(fs, rev, name, + old_value_p, + value, pool)); +} + +svn_error_t * +svn_fs_change_rev_prop(svn_fs_t *fs, svn_revnum_t rev, const char *name, + const svn_string_t *value, apr_pool_t *pool) +{ + return svn_error_trace( + svn_fs_change_rev_prop2(fs, rev, name, NULL, value, pool)); +} + +svn_error_t * +svn_fs_get_file_delta_stream(svn_txdelta_stream_t **stream_p, + svn_fs_root_t *source_root, + const char *source_path, + svn_fs_root_t *target_root, + const char *target_path, apr_pool_t *pool) +{ + return svn_error_trace(target_root->vtable->get_file_delta_stream( + stream_p, + source_root, source_path, + target_root, target_path, pool)); +} + +svn_error_t * +svn_fs_get_uuid(svn_fs_t *fs, const char **uuid, apr_pool_t *pool) +{ + /* If you change this, consider changing svn_fs__identifier(). */ + *uuid = apr_pstrdup(pool, fs->uuid); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_set_uuid(svn_fs_t *fs, const char *uuid, apr_pool_t *pool) +{ + if (! uuid) + { + uuid = svn_uuid_generate(pool); + } + else + { + apr_uuid_t parsed_uuid; + apr_status_t apr_err = apr_uuid_parse(&parsed_uuid, uuid); + if (apr_err) + return svn_error_createf(SVN_ERR_BAD_UUID, NULL, + _("Malformed UUID '%s'"), uuid); + } + return svn_error_trace(fs->vtable->set_uuid(fs, uuid, pool)); +} + +svn_error_t * +svn_fs_lock(svn_lock_t **lock, svn_fs_t *fs, const char *path, + const char *token, const char *comment, + svn_boolean_t is_dav_comment, apr_time_t expiration_date, + svn_revnum_t current_rev, svn_boolean_t steal_lock, + apr_pool_t *pool) +{ + /* Enforce that the comment be xml-escapable. */ + if (comment) + { + if (! svn_xml_is_xml_safe(comment, strlen(comment))) + return svn_error_create + (SVN_ERR_XML_UNESCAPABLE_DATA, NULL, + _("Lock comment contains illegal characters")); + } + + /* Enforce that the token be an XML-safe URI. */ + if (token) + { + const char *c; + + if (strncmp(token, "opaquelocktoken:", 16)) + return svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL, + _("Lock token URI '%s' has bad scheme; " + "expected '%s'"), + token, "opaquelocktoken"); + + for (c = token; *c; c++) + if (! svn_ctype_isascii(*c)) + return svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL, + _("Lock token '%s' is not ASCII " + "at byte %u"), + token, (unsigned)(c - token)); + + /* strlen(token) == c - token. */ + if (! svn_xml_is_xml_safe(token, c - token)) + return svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL, + _("Lock token URI '%s' is not XML-safe"), + token); + } + + if (expiration_date < 0) + return svn_error_create + (SVN_ERR_INCORRECT_PARAMS, NULL, + _("Negative expiration date passed to svn_fs_lock")); + + return svn_error_trace(fs->vtable->lock(lock, fs, path, token, comment, + is_dav_comment, expiration_date, + current_rev, steal_lock, pool)); +} + +svn_error_t * +svn_fs_generate_lock_token(const char **token, svn_fs_t *fs, apr_pool_t *pool) +{ + return svn_error_trace(fs->vtable->generate_lock_token(token, fs, pool)); +} + +svn_error_t * +svn_fs_unlock(svn_fs_t *fs, const char *path, const char *token, + svn_boolean_t break_lock, apr_pool_t *pool) +{ + return svn_error_trace(fs->vtable->unlock(fs, path, token, break_lock, + pool)); +} + +svn_error_t * +svn_fs_get_lock(svn_lock_t **lock, svn_fs_t *fs, const char *path, + apr_pool_t *pool) +{ + return svn_error_trace(fs->vtable->get_lock(lock, fs, path, pool)); +} + +svn_error_t * +svn_fs_get_locks2(svn_fs_t *fs, const char *path, svn_depth_t depth, + svn_fs_get_locks_callback_t get_locks_func, + void *get_locks_baton, apr_pool_t *pool) +{ + SVN_ERR_ASSERT((depth == svn_depth_empty) || + (depth == svn_depth_files) || + (depth == svn_depth_immediates) || + (depth == svn_depth_infinity)); + return svn_error_trace(fs->vtable->get_locks(fs, path, depth, + get_locks_func, + get_locks_baton, pool)); +} + +svn_error_t * +svn_fs_get_locks(svn_fs_t *fs, const char *path, + svn_fs_get_locks_callback_t get_locks_func, + void *get_locks_baton, apr_pool_t *pool) +{ + return svn_error_trace(svn_fs_get_locks2(fs, path, svn_depth_infinity, + get_locks_func, get_locks_baton, + pool)); +} + + +/* --- History functions --- */ + +svn_error_t * +svn_fs_history_prev(svn_fs_history_t **prev_history_p, + svn_fs_history_t *history, svn_boolean_t cross_copies, + apr_pool_t *pool) +{ + return svn_error_trace(history->vtable->prev(prev_history_p, history, + cross_copies, pool)); +} + +svn_error_t * +svn_fs_history_location(const char **path, svn_revnum_t *revision, + svn_fs_history_t *history, apr_pool_t *pool) +{ + return svn_error_trace(history->vtable->location(path, revision, history, + pool)); +} + + +/* --- Node-ID functions --- */ + +svn_fs_id_t * +svn_fs_parse_id(const char *data, apr_size_t len, apr_pool_t *pool) +{ + fs_library_vtable_t *vtable; + svn_error_t *err; + + err = get_library_vtable(&vtable, SVN_FS_TYPE_BDB, pool); + if (err) + { + svn_error_clear(err); + return NULL; + } + return vtable->parse_id(data, len, pool); +} + +svn_string_t * +svn_fs_unparse_id(const svn_fs_id_t *id, apr_pool_t *pool) +{ + return id->vtable->unparse(id, pool); +} + +svn_boolean_t +svn_fs_check_related(const svn_fs_id_t *a, const svn_fs_id_t *b) +{ + return (a->vtable->compare(a, b) != -1); +} + +int +svn_fs_compare_ids(const svn_fs_id_t *a, const svn_fs_id_t *b) +{ + return a->vtable->compare(a, b); +} + +svn_error_t * +svn_fs_print_modules(svn_stringbuf_t *output, + apr_pool_t *pool) +{ + const struct fs_type_defn *defn = fs_modules; + fs_library_vtable_t *vtable; + apr_pool_t *iterpool = svn_pool_create(pool); + + while (defn) + { + char *line; + svn_error_t *err; + + svn_pool_clear(iterpool); + + err = get_library_vtable_direct(&vtable, defn, iterpool); + if (err) + { + if (err->apr_err == SVN_ERR_FS_UNKNOWN_FS_TYPE) + { + svn_error_clear(err); + defn = defn->next; + continue; + } + else + return err; + } + + 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); + + return SVN_NO_ERROR; +} + +svn_fs_path_change2_t * +svn_fs_path_change2_create(const svn_fs_id_t *node_rev_id, + svn_fs_path_change_kind_t change_kind, + apr_pool_t *pool) +{ + return svn_fs__path_change_create_internal(node_rev_id, change_kind, pool); +} + +/* Return the library version number. */ +const svn_version_t * +svn_fs_version(void) +{ + SVN_VERSION_BODY; +} diff --git a/subversion/libsvn_fs/fs-loader.h b/subversion/libsvn_fs/fs-loader.h new file mode 100644 index 0000000000000..532ff05110424 --- /dev/null +++ b/subversion/libsvn_fs/fs-loader.h @@ -0,0 +1,502 @@ +/* + * fs_loader.h: Declarations for the FS loader library + * + * ==================================================================== + * 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. + * ==================================================================== + */ + + +#ifndef LIBSVN_FS_FS_H +#define LIBSVN_FS_FS_H + +#include "svn_types.h" +#include "svn_fs.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/* 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 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 + the "fsap_data" field which is defined by the FSAP. Likewise, some + of the very simple svn_fs API functions (such as svn_fs_root_fs) + are defined by the loader library, while the rest are implemented + through vtable calls defined by the FSAP. + + If you are considering writing a new database-backed filesystem + implementation, it may be appropriate to add a second, lower-level + abstraction to the libsvn_fs_base library which currently + implements the BDB filesystem type. Consult the dev list for + details on the "FSP-level" abstraction concept. +*/ + + + +/*** Top-level library vtable type ***/ + +typedef struct fs_library_vtable_t +{ + /* This field should always remain first in the vtable. + Apart from that, it can be changed however you like, since exact + version equality is required between loader and module. This policy + was weaker during 1.1.x, but only in ways which do not conflict with + this statement, now that the minor version has increased. */ + const svn_version_t *(*get_version)(void); + + /* The open_fs/create/open_fs_for_recovery/upgrade_fs functions are + serialized so that they may use the common_pool parameter to + allocate fs-global objects such as the bdb env cache. */ + svn_error_t *(*create)(svn_fs_t *fs, const char *path, apr_pool_t *pool, + apr_pool_t *common_pool); + svn_error_t *(*open_fs)(svn_fs_t *fs, const char *path, apr_pool_t *pool, + apr_pool_t *common_pool); + /* open_for_recovery() is like open(), but used to fill in an fs pointer + that will be passed to recover(). We assume that the open() method + might not be immediately appropriate for recovery. */ + svn_error_t *(*open_fs_for_recovery)(svn_fs_t *fs, const char *path, + apr_pool_t *pool, + 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)(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, + apr_pool_t *pool); + 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 *common_pool); + + /* Provider-specific functions should go here, even if they could go + in an object vtable, so that they are all kept together. */ + svn_error_t *(*bdb_logfiles)(apr_array_header_t **logfiles, + const char *path, svn_boolean_t only_unused, + apr_pool_t *pool); + + /* This is to let the base provider implement the deprecated + svn_fs_parse_id, which we've decided doesn't belong in the FS + API. If we change our minds and decide to add a real + svn_fs_parse_id variant which takes an FS object, it should go + 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 + library vtable. The LOADER_VERSION parameter must remain first in + the list, and the function must use the C calling convention on all + platforms, so that the init functions can safely read the version + parameter. The COMMON_POOL parameter must be a pool with a greater + lifetime than the fs module so that fs global state can be kept + in it and cleaned up on termination before the fs module is unloaded. + Calls to these functions are globally serialized so that they have + exclusive access to the COMMON_POOL parameter. + + ### need to force this to be __cdecl on Windows... how?? */ +typedef svn_error_t *(*fs_init_func_t)(const svn_version_t *loader_version, + fs_library_vtable_t **vtable, + apr_pool_t* common_pool); + +/* Here are the declarations for the FS module init functions. If we + are using DSO loading, they won't actually be linked into + libsvn_fs. Note that these private functions have a common_pool + parameter that may be used for fs module scoped variables such as + the bdb cache. This will be the same common_pool that is passed + to the create and open functions and these init functions (as well + as the open and create functions) are globally serialized so that + they have exclusive access to the common_pool. */ +svn_error_t *svn_fs_base__init(const svn_version_t *loader_version, + fs_library_vtable_t **vtable, + apr_pool_t* common_pool); +svn_error_t *svn_fs_fs__init(const svn_version_t *loader_version, + fs_library_vtable_t **vtable, + apr_pool_t* common_pool); + + + +/*** vtable types for the abstract FS objects ***/ + +typedef struct fs_vtable_t +{ + svn_error_t *(*youngest_rev)(svn_revnum_t *youngest_p, svn_fs_t *fs, + apr_pool_t *pool); + svn_error_t *(*revision_prop)(svn_string_t **value_p, svn_fs_t *fs, + svn_revnum_t rev, const char *propname, + apr_pool_t *pool); + svn_error_t *(*revision_proplist)(apr_hash_t **table_p, svn_fs_t *fs, + svn_revnum_t rev, apr_pool_t *pool); + svn_error_t *(*change_rev_prop)(svn_fs_t *fs, svn_revnum_t rev, + const char *name, + const svn_string_t *const *old_value_p, + const svn_string_t *value, + 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); + svn_error_t *(*begin_txn)(svn_fs_txn_t **txn_p, svn_fs_t *fs, + svn_revnum_t rev, apr_uint32_t flags, + apr_pool_t *pool); + svn_error_t *(*open_txn)(svn_fs_txn_t **txn, svn_fs_t *fs, + const char *name, apr_pool_t *pool); + svn_error_t *(*purge_txn)(svn_fs_t *fs, const char *txn_id, + apr_pool_t *pool); + svn_error_t *(*list_transactions)(apr_array_header_t **names_p, + svn_fs_t *fs, apr_pool_t *pool); + svn_error_t *(*deltify)(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool); + svn_error_t *(*lock)(svn_lock_t **lock, svn_fs_t *fs, + const char *path, const char *token, + const char *comment, svn_boolean_t is_dav_comment, + apr_time_t expiration_date, + svn_revnum_t current_rev, svn_boolean_t steal_lock, + apr_pool_t *pool); + svn_error_t *(*generate_lock_token)(const char **token, svn_fs_t *fs, + apr_pool_t *pool); + svn_error_t *(*unlock)(svn_fs_t *fs, const char *path, const char *token, + svn_boolean_t break_lock, apr_pool_t *pool); + svn_error_t *(*get_lock)(svn_lock_t **lock, svn_fs_t *fs, + const char *path, apr_pool_t *pool); + svn_error_t *(*get_locks)(svn_fs_t *fs, const char *path, svn_depth_t depth, + 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)); +} fs_vtable_t; + + +typedef struct txn_vtable_t +{ + svn_error_t *(*commit)(const char **conflict_p, svn_revnum_t *new_rev, + svn_fs_txn_t *txn, apr_pool_t *pool); + svn_error_t *(*abort)(svn_fs_txn_t *txn, apr_pool_t *pool); + svn_error_t *(*get_prop)(svn_string_t **value_p, svn_fs_txn_t *txn, + const char *propname, apr_pool_t *pool); + svn_error_t *(*get_proplist)(apr_hash_t **table_p, svn_fs_txn_t *txn, + apr_pool_t *pool); + svn_error_t *(*change_prop)(svn_fs_txn_t *txn, const char *name, + const svn_string_t *value, apr_pool_t *pool); + svn_error_t *(*root)(svn_fs_root_t **root_p, svn_fs_txn_t *txn, + apr_pool_t *pool); + svn_error_t *(*change_props)(svn_fs_txn_t *txn, const apr_array_header_t *props, + apr_pool_t *pool); +} txn_vtable_t; + + +/* Some of these operations accept multiple root arguments. Since the + roots may not all have the same vtable, we need a rule to determine + which root's vtable is used. The rule is: if one of the roots is + named "target", we use that root's vtable; otherwise, we use the + first root argument's vtable. + These callbacks correspond to svn_fs_* functions in include/svn_fs.h, + see there for details. + Note: delete_node() corresponds to svn_fs_delete(). */ +typedef struct root_vtable_t +{ + /* Determining what has changed in a root */ + svn_error_t *(*paths_changed)(apr_hash_t **changed_paths_p, + svn_fs_root_t *root, + apr_pool_t *pool); + + /* Generic node operations */ + svn_error_t *(*check_path)(svn_node_kind_t *kind_p, svn_fs_root_t *root, + const char *path, apr_pool_t *pool); + svn_error_t *(*node_history)(svn_fs_history_t **history_p, + svn_fs_root_t *root, const char *path, + apr_pool_t *pool); + svn_error_t *(*node_id)(const svn_fs_id_t **id_p, svn_fs_root_t *root, + const char *path, apr_pool_t *pool); + svn_error_t *(*node_created_rev)(svn_revnum_t *revision, + svn_fs_root_t *root, const char *path, + apr_pool_t *pool); + svn_error_t *(*node_origin_rev)(svn_revnum_t *revision, + svn_fs_root_t *root, const char *path, + apr_pool_t *pool); + svn_error_t *(*node_created_path)(const char **created_path, + svn_fs_root_t *root, const char *path, + apr_pool_t *pool); + svn_error_t *(*delete_node)(svn_fs_root_t *root, const char *path, + apr_pool_t *pool); + svn_error_t *(*copied_from)(svn_revnum_t *rev_p, const char **path_p, + svn_fs_root_t *root, const char *path, + apr_pool_t *pool); + svn_error_t *(*closest_copy)(svn_fs_root_t **root_p, const char **path_p, + svn_fs_root_t *root, const char *path, + apr_pool_t *pool); + + /* Property operations */ + svn_error_t *(*node_prop)(svn_string_t **value_p, svn_fs_root_t *root, + const char *path, const char *propname, + apr_pool_t *pool); + svn_error_t *(*node_proplist)(apr_hash_t **table_p, svn_fs_root_t *root, + const char *path, apr_pool_t *pool); + svn_error_t *(*change_node_prop)(svn_fs_root_t *root, const char *path, + const char *name, + const svn_string_t *value, + apr_pool_t *pool); + svn_error_t *(*props_changed)(int *changed_p, svn_fs_root_t *root1, + const char *path1, svn_fs_root_t *root2, + const char *path2, apr_pool_t *pool); + + /* Directories */ + svn_error_t *(*dir_entries)(apr_hash_t **entries_p, svn_fs_root_t *root, + const char *path, apr_pool_t *pool); + svn_error_t *(*make_dir)(svn_fs_root_t *root, const char *path, + apr_pool_t *pool); + svn_error_t *(*copy)(svn_fs_root_t *from_root, const char *from_path, + svn_fs_root_t *to_root, const char *to_path, + apr_pool_t *pool); + svn_error_t *(*revision_link)(svn_fs_root_t *from_root, + svn_fs_root_t *to_root, + const char *path, + apr_pool_t *pool); + + /* Files */ + svn_error_t *(*file_length)(svn_filesize_t *length_p, svn_fs_root_t *root, + const char *path, apr_pool_t *pool); + svn_error_t *(*file_checksum)(svn_checksum_t **checksum, + svn_checksum_kind_t kind, svn_fs_root_t *root, + const char *path, apr_pool_t *pool); + 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, + void **contents_baton_p, + svn_fs_root_t *root, const char *path, + svn_checksum_t *base_checksum, + svn_checksum_t *result_checksum, + apr_pool_t *pool); + svn_error_t *(*apply_text)(svn_stream_t **contents_p, svn_fs_root_t *root, + const char *path, svn_checksum_t *result_checksum, + apr_pool_t *pool); + svn_error_t *(*contents_changed)(int *changed_p, svn_fs_root_t *root1, + const char *path1, svn_fs_root_t *root2, + const char *path2, apr_pool_t *pool); + svn_error_t *(*get_file_delta_stream)(svn_txdelta_stream_t **stream_p, + svn_fs_root_t *source_root, + const char *source_path, + svn_fs_root_t *target_root, + const char *target_path, + apr_pool_t *pool); + + /* Merging. */ + svn_error_t *(*merge)(const char **conflict_p, + svn_fs_root_t *source_root, + const char *source_path, + svn_fs_root_t *target_root, + const char *target_path, + 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, + svn_boolean_t adjust_inherited_mergeinfo, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); +} root_vtable_t; + + +typedef struct history_vtable_t +{ + svn_error_t *(*prev)(svn_fs_history_t **prev_history_p, + svn_fs_history_t *history, svn_boolean_t cross_copies, + apr_pool_t *pool); + svn_error_t *(*location)(const char **path, svn_revnum_t *revision, + svn_fs_history_t *history, apr_pool_t *pool); +} history_vtable_t; + + +typedef struct id_vtable_t +{ + svn_string_t *(*unparse)(const svn_fs_id_t *id, apr_pool_t *pool); + int (*compare)(const svn_fs_id_t *a, const svn_fs_id_t *b); +} id_vtable_t; + + + +/*** Definitions of the abstract FS object types ***/ + +/* These are transaction properties that correspond to the bitfields + in the 'flags' argument to svn_fs_lock(). */ +#define SVN_FS__PROP_TXN_CHECK_LOCKS SVN_PROP_PREFIX "check-locks" +#define SVN_FS__PROP_TXN_CHECK_OOD SVN_PROP_PREFIX "check-ood" + +struct svn_fs_t +{ + /* The pool in which this fs object is allocated */ + apr_pool_t *pool; + + /* The path to the repository's top-level directory */ + char *path; + + /* A callback for printing warning messages */ + svn_fs_warning_callback_t warning; + void *warning_baton; + + /* The filesystem configuration */ + apr_hash_t *config; + + /* An access context indicating who's using the fs */ + svn_fs_access_t *access_ctx; + + /* FSAP-specific vtable and private data */ + fs_vtable_t *vtable; + void *fsap_data; + + /* UUID, stored by open(), create(), and set_uuid(). */ + const char *uuid; +}; + + +struct svn_fs_txn_t +{ + /* The filesystem to which this transaction belongs */ + svn_fs_t *fs; + + /* The revision on which this transaction is based, or + SVN_INVALID_REVISION if the transaction is not based on a + revision at all */ + svn_revnum_t base_rev; + + /* The ID of this transaction */ + const char *id; + + /* FSAP-specific vtable and private data */ + txn_vtable_t *vtable; + void *fsap_data; +}; + + +struct svn_fs_root_t +{ + /* A pool managing this root (and only this root!) */ + apr_pool_t *pool; + + /* The filesystem to which this root belongs */ + svn_fs_t *fs; + + /* The kind of root this is */ + svn_boolean_t is_txn_root; + + /* For transaction roots, the name of the transaction */ + const char *txn; + + /* For transaction roots, flags describing the txn's behavior. */ + apr_uint32_t txn_flags; + + /* For revision roots, the number of the revision; for transaction + roots, the number of the revision on which the transaction is + based. */ + svn_revnum_t rev; + + /* FSAP-specific vtable and private data */ + root_vtable_t *vtable; + void *fsap_data; +}; + + +struct svn_fs_history_t +{ + /* FSAP-specific vtable and private data */ + history_vtable_t *vtable; + void *fsap_data; +}; + + +struct svn_fs_id_t +{ + /* FSAP-specific vtable and private data */ + id_vtable_t *vtable; + void *fsap_data; +}; + + +struct svn_fs_access_t +{ + /* An authenticated username using the fs */ + const char *username; + + /* A collection of lock-tokens supplied by the fs caller. + Hash maps (const char *) UUID --> (void *) 1 + fs functions should really only be interested whether a UUID + exists as a hash key at all; the value is irrelevant. */ + apr_hash_t *lock_tokens; +}; + + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif |