diff options
Diffstat (limited to 'subversion/libsvn_fs/fs-loader.c')
-rw-r--r-- | subversion/libsvn_fs/fs-loader.c | 1602 |
1 files changed, 1602 insertions, 0 deletions
diff --git a/subversion/libsvn_fs/fs-loader.c b/subversion/libsvn_fs/fs-loader.c new file mode 100644 index 000000000000..01d6ba1afc12 --- /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; +} |