summaryrefslogtreecommitdiff
path: root/subversion/libsvn_fs
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_fs')
-rw-r--r--subversion/libsvn_fs/access.c105
-rw-r--r--subversion/libsvn_fs/editor.c850
-rw-r--r--subversion/libsvn_fs/fs-loader.c1602
-rw-r--r--subversion/libsvn_fs/fs-loader.h502
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