diff options
author | peter <peter@FreeBSD.org> | 2013-06-18 02:07:41 +0000 |
---|---|---|
committer | peter <peter@FreeBSD.org> | 2013-06-18 02:07:41 +0000 |
commit | d25dac7fcc6acc838b71bbda8916fd9665c709ab (patch) | |
tree | 135691142dc0e75a5e5d97b5074d03436435b8e0 /subversion/libsvn_fs_base/fs.c | |
download | FreeBSD-src-d25dac7fcc6acc838b71bbda8916fd9665c709ab.zip FreeBSD-src-d25dac7fcc6acc838b71bbda8916fd9665c709ab.tar.gz |
Import trimmed svn-1.8.0-rc3
Diffstat (limited to 'subversion/libsvn_fs_base/fs.c')
-rw-r--r-- | subversion/libsvn_fs_base/fs.c | 1436 |
1 files changed, 1436 insertions, 0 deletions
diff --git a/subversion/libsvn_fs_base/fs.c b/subversion/libsvn_fs_base/fs.c new file mode 100644 index 0000000..bee921b --- /dev/null +++ b/subversion/libsvn_fs_base/fs.c @@ -0,0 +1,1436 @@ +/* fs.c --- creating, opening and closing filesystems + * + * ==================================================================== + * 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 <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <apr_general.h> +#include <apr_pools.h> +#include <apr_file_io.h> + +#include "svn_hash.h" +#include "svn_pools.h" +#include "svn_fs.h" +#include "svn_path.h" +#include "svn_utf.h" +#include "svn_delta.h" +#include "svn_version.h" +#include "fs.h" +#include "err.h" +#include "dag.h" +#include "revs-txns.h" +#include "uuid.h" +#include "tree.h" +#include "id.h" +#include "lock.h" +#define SVN_WANT_BDB +#include "svn_private_config.h" + +#include "bdb/bdb-err.h" +#include "bdb/bdb_compat.h" +#include "bdb/env.h" +#include "bdb/nodes-table.h" +#include "bdb/rev-table.h" +#include "bdb/txn-table.h" +#include "bdb/copies-table.h" +#include "bdb/changes-table.h" +#include "bdb/reps-table.h" +#include "bdb/strings-table.h" +#include "bdb/uuids-table.h" +#include "bdb/locks-table.h" +#include "bdb/lock-tokens-table.h" +#include "bdb/node-origins-table.h" +#include "bdb/miscellaneous-table.h" +#include "bdb/checksum-reps-table.h" + +#include "../libsvn_fs/fs-loader.h" +#include "private/svn_fs_util.h" + + +/* Checking for return values, and reporting errors. */ + +/* Check that we're using the right Berkeley DB version. */ +/* FIXME: This check should be abstracted into the DB back-end layer. */ +static svn_error_t * +check_bdb_version(void) +{ + int major, minor, patch; + + db_version(&major, &minor, &patch); + + /* First, check that we're using a reasonably correct of Berkeley DB. */ + if ((major < SVN_FS_WANT_DB_MAJOR) + || (major == SVN_FS_WANT_DB_MAJOR && minor < SVN_FS_WANT_DB_MINOR) + || (major == SVN_FS_WANT_DB_MAJOR && minor == SVN_FS_WANT_DB_MINOR + && patch < SVN_FS_WANT_DB_PATCH)) + return svn_error_createf(SVN_ERR_FS_GENERAL, 0, + _("Bad database version: got %d.%d.%d," + " should be at least %d.%d.%d"), + major, minor, patch, + SVN_FS_WANT_DB_MAJOR, + SVN_FS_WANT_DB_MINOR, + SVN_FS_WANT_DB_PATCH); + + /* Now, check that the version we're running against is the same as + the one we compiled with. */ + if (major != DB_VERSION_MAJOR || minor != DB_VERSION_MINOR) + return svn_error_createf(SVN_ERR_FS_GENERAL, 0, + _("Bad database version:" + " compiled with %d.%d.%d," + " running against %d.%d.%d"), + DB_VERSION_MAJOR, + DB_VERSION_MINOR, + DB_VERSION_PATCH, + major, minor, patch); + return SVN_NO_ERROR; +} + + + +/* Cleanup functions. */ + +/* Close a database in the filesystem FS. + DB_PTR is a pointer to the DB pointer in *FS to close. + NAME is the name of the database, for use in error messages. */ +static svn_error_t * +cleanup_fs_db(svn_fs_t *fs, DB **db_ptr, const char *name) +{ + /* If the BDB environment is panicked, don't do anything, since + attempting to close the database will fail anyway. */ + base_fs_data_t *bfd = fs->fsap_data; + if (*db_ptr && !svn_fs_bdb__get_panic(bfd->bdb)) + { + DB *db = *db_ptr; + char *msg = apr_psprintf(fs->pool, "closing '%s' database", name); + int db_err; + + *db_ptr = 0; + db_err = db->close(db, 0); + if (db_err == DB_RUNRECOVERY) + { + /* We can ignore DB_RUNRECOVERY errors from DB->close, but + must set the panic flag in the environment baton. The + error will be propagated appropriately from + svn_fs_bdb__close. */ + svn_fs_bdb__set_panic(bfd->bdb); + db_err = 0; + } + +#if SVN_BDB_HAS_DB_INCOMPLETE + /* We can ignore DB_INCOMPLETE on db->close and db->sync; it + * just means someone else was using the db at the same time + * we were. See the Berkeley documentation at: + * http://www.sleepycat.com/docs/ref/program/errorret.html#DB_INCOMPLETE + * http://www.sleepycat.com/docs/api_c/db_close.html + */ + if (db_err == DB_INCOMPLETE) + db_err = 0; +#endif /* SVN_BDB_HAS_DB_INCOMPLETE */ + + SVN_ERR(BDB_WRAP(fs, msg, db_err)); + } + + return SVN_NO_ERROR; +} + +/* Close whatever Berkeley DB resources are allocated to FS. */ +static svn_error_t * +cleanup_fs(svn_fs_t *fs) +{ + base_fs_data_t *bfd = fs->fsap_data; + bdb_env_baton_t *bdb = (bfd ? bfd->bdb : NULL); + + if (!bdb) + return SVN_NO_ERROR; + + /* Close the databases. */ + SVN_ERR(cleanup_fs_db(fs, &bfd->nodes, "nodes")); + SVN_ERR(cleanup_fs_db(fs, &bfd->revisions, "revisions")); + SVN_ERR(cleanup_fs_db(fs, &bfd->transactions, "transactions")); + SVN_ERR(cleanup_fs_db(fs, &bfd->copies, "copies")); + SVN_ERR(cleanup_fs_db(fs, &bfd->changes, "changes")); + SVN_ERR(cleanup_fs_db(fs, &bfd->representations, "representations")); + SVN_ERR(cleanup_fs_db(fs, &bfd->strings, "strings")); + SVN_ERR(cleanup_fs_db(fs, &bfd->uuids, "uuids")); + SVN_ERR(cleanup_fs_db(fs, &bfd->locks, "locks")); + SVN_ERR(cleanup_fs_db(fs, &bfd->lock_tokens, "lock-tokens")); + SVN_ERR(cleanup_fs_db(fs, &bfd->node_origins, "node-origins")); + SVN_ERR(cleanup_fs_db(fs, &bfd->checksum_reps, "checksum-reps")); + SVN_ERR(cleanup_fs_db(fs, &bfd->miscellaneous, "miscellaneous")); + + /* Finally, close the environment. */ + bfd->bdb = 0; + { + svn_error_t *err = svn_fs_bdb__close(bdb); + if (err) + return svn_error_createf + (err->apr_err, err, + _("Berkeley DB error for filesystem '%s'" + " while closing environment:\n"), + fs->path); + } + return SVN_NO_ERROR; +} + +#if 0 /* Set to 1 for instrumenting. */ +static void print_fs_stats(svn_fs_t *fs) +{ + base_fs_data_t *bfd = fs->fsap_data; + DB_TXN_STAT *t; + DB_LOCK_STAT *l; + int db_err; + + /* Print transaction statistics for this DB env. */ + if ((db_err = bfd->bdb->env->txn_stat(bfd->bdb->env, &t, 0)) != 0) + fprintf(stderr, "Error running bfd->bdb->env->txn_stat(): %s", + db_strerror(db_err)); + else + { + printf("*** DB transaction stats, right before closing env:\n"); + printf(" Number of transactions currently active: %d\n", + t->st_nactive); + printf(" Max number of active transactions at any one time: %d\n", + t->st_maxnactive); + printf(" Number of transactions that have begun: %d\n", + t->st_nbegins); + printf(" Number of transactions that have aborted: %d\n", + t->st_naborts); + printf(" Number of transactions that have committed: %d\n", + t->st_ncommits); + printf(" Number of times a thread was forced to wait: %d\n", + t->st_region_wait); + printf(" Number of times a thread didn't need to wait: %d\n", + t->st_region_nowait); + printf("*** End DB transaction stats.\n\n"); + } + + /* Print transaction statistics for this DB env. */ + if ((db_err = bfd->bdb->env->lock_stat(bfd->bdb->env, &l, 0)) != 0) + fprintf(stderr, "Error running bfd->bdb->env->lock_stat(): %s", + db_strerror(db_err)); + else + { + printf("*** DB lock stats, right before closing env:\n"); + printf(" The number of current locks: %d\n", + l->st_nlocks); + printf(" Max number of locks at any one time: %d\n", + l->st_maxnlocks); + printf(" Number of current lockers: %d\n", + l->st_nlockers); + printf(" Max number of lockers at any one time: %d\n", + l->st_maxnlockers); + printf(" Number of current objects: %d\n", + l->st_nobjects); + printf(" Max number of objects at any one time: %d\n", + l->st_maxnobjects); + printf(" Total number of locks requested: %d\n", + l->st_nrequests); + printf(" Total number of locks released: %d\n", + l->st_nreleases); + printf(" Total number of lock reqs failed because " + "DB_LOCK_NOWAIT was set: %d\n", l->st_nnowaits); + printf(" Total number of locks not immediately available " + "due to conflicts: %d\n", l->st_nconflicts); + printf(" Number of deadlocks detected: %d\n", l->st_ndeadlocks); + printf(" Number of times a thread waited before " + "obtaining the region lock: %d\n", l->st_region_wait); + printf(" Number of times a thread didn't have to wait: %d\n", + l->st_region_nowait); + printf("*** End DB lock stats.\n\n"); + } + +} +#else +# define print_fs_stats(fs) +#endif /* 0/1 */ + +/* An APR pool cleanup function for a filesystem. DATA must be a + pointer to the filesystem to clean up. + + When the filesystem object's pool is freed, we want the resources + held by Berkeley DB to go away, just like everything else. So we + register this cleanup function with the filesystem's pool, and let + it take care of closing the databases, the environment, and any + other DB objects we might be using. APR calls this function before + actually freeing the pool's memory. + + It's a pity that we can't return an svn_error_t object from an APR + cleanup function. For now, we return the rather generic + SVN_ERR_FS_CLEANUP, and pass the real svn_error_t to the registered + warning callback. */ + +static apr_status_t +cleanup_fs_apr(void *data) +{ + svn_fs_t *fs = data; + svn_error_t *err; + + print_fs_stats(fs); + + err = cleanup_fs(fs); + if (! err) + return APR_SUCCESS; + + /* Darn. An error during cleanup. Call the warning handler to + try and do something "right" with this error. Note that + the default will simply abort(). */ + (*fs->warning)(fs->warning_baton, err); + + svn_error_clear(err); + + return SVN_ERR_FS_CLEANUP; +} + + +static svn_error_t * +base_bdb_set_errcall(svn_fs_t *fs, + void (*db_errcall_fcn)(const char *errpfx, char *msg)) +{ + base_fs_data_t *bfd = fs->fsap_data; + + SVN_ERR(svn_fs__check_fs(fs, TRUE)); + bfd->bdb->error_info->user_callback = db_errcall_fcn; + + return SVN_NO_ERROR; +} + + +/* Write the DB_CONFIG file. */ +static svn_error_t * +bdb_write_config(svn_fs_t *fs) +{ + const char *dbconfig_file_name = + svn_dirent_join(fs->path, BDB_CONFIG_FILE, fs->pool); + apr_file_t *dbconfig_file = NULL; + int i; + + static const char dbconfig_contents[] = + "# This is the configuration file for the Berkeley DB environment\n" + "# used by your Subversion repository.\n" + "# You must run 'svnadmin recover' whenever you modify this file,\n" + "# for your changes to take effect.\n" + "\n" + "### Lock subsystem\n" + "#\n" + "# Make sure you read the documentation at:\n" + "#\n" + "# http://docs.oracle.com/cd/E17076_02/html/programmer_reference/lock_max.html\n" + "#\n" + "# before tweaking these values.\n" + "#\n" + "set_lk_max_locks 2000\n" + "set_lk_max_lockers 2000\n" + "set_lk_max_objects 2000\n" + "\n" + "### Log file subsystem\n" + "#\n" + "# Make sure you read the documentation at:\n" + "#\n" + "# http://docs.oracle.com/cd/E17076_02/html/api_reference/C/envset_lg_bsize.html\n" + "# http://docs.oracle.com/cd/E17076_02/html/api_reference/C/envset_lg_max.html\n" + "# http://docs.oracle.com/cd/E17076_02/html/programmer_reference/log_limits.html\n" + "#\n" + "# Increase the size of the in-memory log buffer from the default\n" + "# of 32 Kbytes to 256 Kbytes. Decrease the log file size from\n" + "# 10 Mbytes to 1 Mbyte. This will help reduce the amount of disk\n" + "# space required for hot backups. The size of the log file must be\n" + "# at least four times the size of the in-memory log buffer.\n" + "#\n" + "# Note: Decreasing the in-memory buffer size below 256 Kbytes will hurt\n" + "# hurt commit performance. For details, see:\n" + "#\n" + "# http://svn.haxx.se/dev/archive-2002-02/0141.shtml\n" + "#\n" + "set_lg_bsize 262144\n" + "set_lg_max 1048576\n" + "#\n" + "# If you see \"log region out of memory\" errors, bump lg_regionmax.\n" + "# For more information, see:\n" + "#\n" + "# http://docs.oracle.com/cd/E17076_02/html/programmer_reference/log_config.html\n" + "# http://svn.haxx.se/users/archive-2004-10/1000.shtml\n" + "#\n" + "set_lg_regionmax 131072\n" + "#\n" + /* ### Configure this with "svnadmin create --bdb-cache-size" */ + "# The default cache size in BDB is only 256k. As explained in\n" + "# http://svn.haxx.se/dev/archive-2004-12/0368.shtml, this is too\n" + "# small for most applications. Bump this number if \"db_stat -m\"\n" + "# shows too many cache misses.\n" + "#\n" + "set_cachesize 0 1048576 1\n"; + + /* Run-time configurable options. + Each option set consists of a minimum required BDB version, a + config hash key, a header, an inactive form and an active + form. We always write the header; then, depending on the + run-time configuration and the BDB version we're compiling + against, we write either the active or inactive form of the + value. */ + static const struct + { + int bdb_major; + int bdb_minor; + const char *config_key; + const char *header; + const char *inactive; + const char *active; + } dbconfig_options[] = { + /* Controlled by "svnadmin create --bdb-txn-nosync" */ + { 4, 0, SVN_FS_CONFIG_BDB_TXN_NOSYNC, + /* header */ + "#\n" + "# Disable fsync of log files on transaction commit. Read the\n" + "# documentation about DB_TXN_NOSYNC at:\n" + "#\n" + "# http://docs.oracle.com/cd/E17076_02/html/programmer_reference/log_config.html\n" + "#\n" + "# [requires Berkeley DB 4.0]\n" + "#\n", + /* inactive */ + "#set_flags DB_TXN_NOSYNC\n", + /* active */ + "set_flags DB_TXN_NOSYNC\n" }, + /* Controlled by "svnadmin create --bdb-log-keep" */ + { 4, 2, SVN_FS_CONFIG_BDB_LOG_AUTOREMOVE, + /* header */ + "#\n" + "# Enable automatic removal of unused transaction log files.\n" + "# Read the documentation about DB_LOG_AUTOREMOVE at:\n" + "#\n" + "# http://docs.oracle.com/cd/E17076_02/html/programmer_reference/log_config.html\n" + "#\n" + "# [requires Berkeley DB 4.2]\n" + "#\n", + /* inactive */ + "#set_flags DB_LOG_AUTOREMOVE\n", + /* active */ + "set_flags DB_LOG_AUTOREMOVE\n" }, + }; + static const int dbconfig_options_length = + sizeof(dbconfig_options)/sizeof(*dbconfig_options); + + + SVN_ERR(svn_io_file_open(&dbconfig_file, dbconfig_file_name, + APR_WRITE | APR_CREATE, APR_OS_DEFAULT, + fs->pool)); + + SVN_ERR(svn_io_file_write_full(dbconfig_file, dbconfig_contents, + sizeof(dbconfig_contents) - 1, NULL, + fs->pool)); + + /* Write the variable DB_CONFIG flags. */ + for (i = 0; i < dbconfig_options_length; ++i) + { + void *value = NULL; + const char *choice; + + if (fs->config) + { + value = svn_hash_gets(fs->config, dbconfig_options[i].config_key); + } + + SVN_ERR(svn_io_file_write_full(dbconfig_file, + dbconfig_options[i].header, + strlen(dbconfig_options[i].header), + NULL, fs->pool)); + + if (((DB_VERSION_MAJOR == dbconfig_options[i].bdb_major + && DB_VERSION_MINOR >= dbconfig_options[i].bdb_minor) + || DB_VERSION_MAJOR > dbconfig_options[i].bdb_major) + && value != NULL && strcmp(value, "0") != 0) + choice = dbconfig_options[i].active; + else + choice = dbconfig_options[i].inactive; + + SVN_ERR(svn_io_file_write_full(dbconfig_file, choice, strlen(choice), + NULL, fs->pool)); + } + + return svn_io_file_close(dbconfig_file, fs->pool); +} + +static svn_error_t * +base_bdb_verify_root(svn_fs_root_t *root, + apr_pool_t *scratch_pool) +{ + /* Verifying is currently a no op for BDB. */ + return SVN_NO_ERROR; +} + +static svn_error_t * +base_bdb_freeze(svn_fs_t *fs, + svn_fs_freeze_func_t freeze_func, + void *freeze_baton, + apr_pool_t *pool) +{ + SVN__NOT_IMPLEMENTED(); +} + + +/* Creating a new filesystem */ + +static fs_vtable_t fs_vtable = { + svn_fs_base__youngest_rev, + svn_fs_base__revision_prop, + svn_fs_base__revision_proplist, + svn_fs_base__change_rev_prop, + svn_fs_base__set_uuid, + svn_fs_base__revision_root, + svn_fs_base__begin_txn, + svn_fs_base__open_txn, + svn_fs_base__purge_txn, + svn_fs_base__list_transactions, + svn_fs_base__deltify, + svn_fs_base__lock, + svn_fs_base__generate_lock_token, + svn_fs_base__unlock, + svn_fs_base__get_lock, + svn_fs_base__get_locks, + base_bdb_verify_root, + base_bdb_freeze, + base_bdb_set_errcall, +}; + +/* Where the format number is stored. */ +#define FORMAT_FILE "format" + +/* Depending on CREATE, create or open the environment and databases + for filesystem FS in PATH. Use POOL for temporary allocations. */ +static svn_error_t * +open_databases(svn_fs_t *fs, + svn_boolean_t create, + int format, + const char *path, + apr_pool_t *pool) +{ + base_fs_data_t *bfd; + + SVN_ERR(svn_fs__check_fs(fs, FALSE)); + + bfd = apr_pcalloc(fs->pool, sizeof(*bfd)); + fs->vtable = &fs_vtable; + fs->fsap_data = bfd; + + /* Initialize the fs's path. */ + fs->path = apr_pstrdup(fs->pool, path); + + if (create) + SVN_ERR(bdb_write_config(fs)); + + /* Create the Berkeley DB environment. */ + { + svn_error_t *err = svn_fs_bdb__open(&(bfd->bdb), path, + SVN_BDB_STANDARD_ENV_FLAGS, + 0666, fs->pool); + if (err) + { + if (create) + return svn_error_createf + (err->apr_err, err, + _("Berkeley DB error for filesystem '%s'" + " while creating environment:\n"), + fs->path); + else + return svn_error_createf + (err->apr_err, err, + _("Berkeley DB error for filesystem '%s'" + " while opening environment:\n"), + fs->path); + } + } + + /* We must register the FS cleanup function *after* opening the + environment, so that it's run before the environment baton + cleanup. */ + apr_pool_cleanup_register(fs->pool, fs, cleanup_fs_apr, + apr_pool_cleanup_null); + + + /* Create the databases in the environment. */ + SVN_ERR(BDB_WRAP(fs, (create + ? N_("creating 'nodes' table") + : N_("opening 'nodes' table")), + svn_fs_bdb__open_nodes_table(&bfd->nodes, + bfd->bdb->env, + create))); + SVN_ERR(BDB_WRAP(fs, (create + ? N_("creating 'revisions' table") + : N_("opening 'revisions' table")), + svn_fs_bdb__open_revisions_table(&bfd->revisions, + bfd->bdb->env, + create))); + SVN_ERR(BDB_WRAP(fs, (create + ? N_("creating 'transactions' table") + : N_("opening 'transactions' table")), + svn_fs_bdb__open_transactions_table(&bfd->transactions, + bfd->bdb->env, + create))); + SVN_ERR(BDB_WRAP(fs, (create + ? N_("creating 'copies' table") + : N_("opening 'copies' table")), + svn_fs_bdb__open_copies_table(&bfd->copies, + bfd->bdb->env, + create))); + SVN_ERR(BDB_WRAP(fs, (create + ? N_("creating 'changes' table") + : N_("opening 'changes' table")), + svn_fs_bdb__open_changes_table(&bfd->changes, + bfd->bdb->env, + create))); + SVN_ERR(BDB_WRAP(fs, (create + ? N_("creating 'representations' table") + : N_("opening 'representations' table")), + svn_fs_bdb__open_reps_table(&bfd->representations, + bfd->bdb->env, + create))); + SVN_ERR(BDB_WRAP(fs, (create + ? N_("creating 'strings' table") + : N_("opening 'strings' table")), + svn_fs_bdb__open_strings_table(&bfd->strings, + bfd->bdb->env, + create))); + SVN_ERR(BDB_WRAP(fs, (create + ? N_("creating 'uuids' table") + : N_("opening 'uuids' table")), + svn_fs_bdb__open_uuids_table(&bfd->uuids, + bfd->bdb->env, + create))); + SVN_ERR(BDB_WRAP(fs, (create + ? N_("creating 'locks' table") + : N_("opening 'locks' table")), + svn_fs_bdb__open_locks_table(&bfd->locks, + bfd->bdb->env, + create))); + SVN_ERR(BDB_WRAP(fs, (create + ? N_("creating 'lock-tokens' table") + : N_("opening 'lock-tokens' table")), + svn_fs_bdb__open_lock_tokens_table(&bfd->lock_tokens, + bfd->bdb->env, + create))); + + if (format >= SVN_FS_BASE__MIN_NODE_ORIGINS_FORMAT) + { + SVN_ERR(BDB_WRAP(fs, (create + ? N_("creating 'node-origins' table") + : N_("opening 'node-origins' table")), + svn_fs_bdb__open_node_origins_table(&bfd->node_origins, + bfd->bdb->env, + create))); + } + + if (format >= SVN_FS_BASE__MIN_MISCELLANY_FORMAT) + { + SVN_ERR(BDB_WRAP(fs, (create + ? N_("creating 'miscellaneous' table") + : N_("opening 'miscellaneous' table")), + svn_fs_bdb__open_miscellaneous_table(&bfd->miscellaneous, + bfd->bdb->env, + create))); + } + + if (format >= SVN_FS_BASE__MIN_REP_SHARING_FORMAT) + { + SVN_ERR(BDB_WRAP(fs, (create + ? N_("creating 'checksum-reps' table") + : N_("opening 'checksum-reps' table")), + svn_fs_bdb__open_checksum_reps_table(&bfd->checksum_reps, + bfd->bdb->env, + create))); + } + + return SVN_NO_ERROR; +} + + +/* Called by functions that initialize an svn_fs_t struct, after that + initialization is done, to populate svn_fs_t->uuid. */ +static svn_error_t * +populate_opened_fs(svn_fs_t *fs, apr_pool_t *scratch_pool) +{ + SVN_ERR(svn_fs_base__populate_uuid(fs, scratch_pool)); + return SVN_NO_ERROR; +} + +static svn_error_t * +base_create(svn_fs_t *fs, const char *path, apr_pool_t *pool, + apr_pool_t *common_pool) +{ + int format = SVN_FS_BASE__FORMAT_NUMBER; + svn_error_t *svn_err; + + /* See if compatibility with older versions was explicitly requested. */ + if (fs->config) + { + if (svn_hash_gets(fs->config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE)) + format = 1; + else if (svn_hash_gets(fs->config, SVN_FS_CONFIG_PRE_1_5_COMPATIBLE)) + format = 2; + else if (svn_hash_gets(fs->config, SVN_FS_CONFIG_PRE_1_6_COMPATIBLE)) + format = 3; + } + + /* Create the environment and databases. */ + svn_err = open_databases(fs, TRUE, format, path, pool); + if (svn_err) goto error; + + /* Initialize the DAG subsystem. */ + svn_err = svn_fs_base__dag_init_fs(fs); + if (svn_err) goto error; + + /* This filesystem is ready. Stamp it with a format number. */ + svn_err = svn_io_write_version_file( + svn_dirent_join(fs->path, FORMAT_FILE, pool), format, pool); + if (svn_err) goto error; + + ((base_fs_data_t *) fs->fsap_data)->format = format; + + SVN_ERR(populate_opened_fs(fs, pool)); + return SVN_NO_ERROR;; + +error: + svn_error_clear(cleanup_fs(fs)); + return svn_err; +} + + +/* Gaining access to an existing Berkeley DB-based filesystem. */ + +svn_error_t * +svn_fs_base__test_required_feature_format(svn_fs_t *fs, + const char *feature, + int requires) +{ + base_fs_data_t *bfd = fs->fsap_data; + if (bfd->format < requires) + return svn_error_createf + (SVN_ERR_UNSUPPORTED_FEATURE, NULL, + _("The '%s' feature requires version %d of the filesystem schema; " + "filesystem '%s' uses only version %d"), + feature, requires, fs->path, bfd->format); + return SVN_NO_ERROR; +} + +/* Return the error SVN_ERR_FS_UNSUPPORTED_FORMAT if FS's format + number is not the same as the format number supported by this + Subversion. */ +static svn_error_t * +check_format(int format) +{ + /* We currently support any format less than the compiled format number + simultaneously. */ + if (format <= SVN_FS_BASE__FORMAT_NUMBER) + return SVN_NO_ERROR; + + return svn_error_createf( + SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL, + _("Expected FS format '%d'; found format '%d'"), + SVN_FS_BASE__FORMAT_NUMBER, format); +} + +static svn_error_t * +base_open(svn_fs_t *fs, const char *path, apr_pool_t *pool, + apr_pool_t *common_pool) +{ + int format; + svn_error_t *svn_err; + svn_boolean_t write_format_file = FALSE; + + /* Read the FS format number. */ + svn_err = svn_io_read_version_file(&format, + svn_dirent_join(path, FORMAT_FILE, pool), + pool); + if (svn_err && APR_STATUS_IS_ENOENT(svn_err->apr_err)) + { + /* Pre-1.2 filesystems did not have a format file (you could say + they were format "0"), so they get upgraded on the fly. + However, we stopped "upgrading on the fly" in 1.5, so older + filesystems should only be bumped to 1.3, which is format "1". */ + svn_error_clear(svn_err); + svn_err = SVN_NO_ERROR; + format = 1; + write_format_file = TRUE; + } + else if (svn_err) + goto error; + + /* Create the environment and databases. */ + svn_err = open_databases(fs, FALSE, format, path, pool); + if (svn_err) goto error; + + ((base_fs_data_t *) fs->fsap_data)->format = format; + SVN_ERR(check_format(format)); + + /* If we lack a format file, write one. */ + if (write_format_file) + { + svn_err = svn_io_write_version_file(svn_dirent_join(path, FORMAT_FILE, + pool), + format, pool); + if (svn_err) goto error; + } + + SVN_ERR(populate_opened_fs(fs, pool)); + return SVN_NO_ERROR; + + error: + svn_error_clear(cleanup_fs(fs)); + return svn_err; +} + + +/* Running recovery on a Berkeley DB-based filesystem. */ + + +/* Recover a database at PATH. Perform catastrophic recovery if FATAL + is TRUE. Use POOL for temporary allocation. */ +static svn_error_t * +bdb_recover(const char *path, svn_boolean_t fatal, apr_pool_t *pool) +{ + bdb_env_baton_t *bdb; + + /* Here's the comment copied from db_recover.c: + + Initialize the environment -- we don't actually do anything + else, that all that's needed to run recovery. + + Note that we specify a private environment, as we're about to + create a region, and we don't want to leave it around. If we + leave the region around, the application that should create it + will simply join it instead, and will then be running with + incorrectly sized (and probably terribly small) caches. */ + + /* Note that since we're using a private environment, we shoudl + /not/ initialize locking. We want the environment files to go + away. */ + + SVN_ERR(svn_fs_bdb__open(&bdb, path, + ((fatal ? DB_RECOVER_FATAL : DB_RECOVER) + | SVN_BDB_PRIVATE_ENV_FLAGS), + 0666, pool)); + return svn_fs_bdb__close(bdb); +} + +static svn_error_t * +base_open_for_recovery(svn_fs_t *fs, const char *path, apr_pool_t *pool, + apr_pool_t *common_pool) +{ + /* Just stash the path in the fs pointer - it's all we really need. */ + fs->path = apr_pstrdup(fs->pool, path); + + return SVN_NO_ERROR; +} + +static svn_error_t * +base_upgrade(svn_fs_t *fs, const char *path, apr_pool_t *pool, + apr_pool_t *common_pool) +{ + const char *version_file_path; + int old_format_number; + svn_error_t *err; + + version_file_path = svn_dirent_join(path, FORMAT_FILE, pool); + + /* Read the old number so we've got it on hand later on. */ + err = svn_io_read_version_file(&old_format_number, version_file_path, pool); + if (err && APR_STATUS_IS_ENOENT(err->apr_err)) + { + /* Pre-1.2 filesystems do not have a 'format' file. */ + old_format_number = 0; + svn_error_clear(err); + err = SVN_NO_ERROR; + } + SVN_ERR(err); + + /* Bump the format file's stored version number. */ + SVN_ERR(svn_io_write_version_file(version_file_path, + SVN_FS_BASE__FORMAT_NUMBER, pool)); + + /* Check and see if we need to record the "bump" revision. */ + if (old_format_number < SVN_FS_BASE__MIN_FORWARD_DELTAS_FORMAT) + { + apr_pool_t *subpool = svn_pool_create(pool); + svn_revnum_t youngest_rev; + const char *value; + + /* Open the filesystem in a subpool (so we can control its + closure) and do our fiddling. + + NOTE: By using base_open() here instead of open_databases(), + we will end up re-reading the format file that we just wrote. + But it's better to use the existing encapsulation of "opening + the filesystem" rather than duplicating (or worse, partially + duplicating) that logic here. */ + SVN_ERR(base_open(fs, path, subpool, common_pool)); + + /* Fetch the youngest rev, and record it */ + SVN_ERR(svn_fs_base__youngest_rev(&youngest_rev, fs, subpool)); + value = apr_psprintf(subpool, "%ld", youngest_rev); + SVN_ERR(svn_fs_base__miscellaneous_set + (fs, SVN_FS_BASE__MISC_FORWARD_DELTA_UPGRADE, + value, subpool)); + svn_pool_destroy(subpool); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +base_verify(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) +{ + /* Verifying is currently a no op for BDB. */ + return SVN_NO_ERROR; +} + +static svn_error_t * +base_bdb_recover(svn_fs_t *fs, + svn_cancel_func_t cancel_func, void *cancel_baton, + apr_pool_t *pool) +{ + /* The fs pointer is a fake created in base_open_for_recovery above. + We only care about the path. */ + return bdb_recover(fs->path, FALSE, pool); +} + +static svn_error_t * +base_bdb_pack(svn_fs_t *fs, + const char *path, + svn_fs_pack_notify_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel, + void *cancel_baton, + apr_pool_t *pool, + apr_pool_t *common_pool) +{ + /* Packing is currently a no op for BDB. */ + return SVN_NO_ERROR; +} + + + +/* Running the 'archive' command on a Berkeley DB-based filesystem. */ + + +static svn_error_t * +base_bdb_logfiles(apr_array_header_t **logfiles, + const char *path, + svn_boolean_t only_unused, + apr_pool_t *pool) +{ + bdb_env_baton_t *bdb; + char **filelist; + char **filename; + u_int32_t flags = only_unused ? 0 : DB_ARCH_LOG; + + *logfiles = apr_array_make(pool, 4, sizeof(const char *)); + + SVN_ERR(svn_fs_bdb__open(&bdb, path, + SVN_BDB_STANDARD_ENV_FLAGS, + 0666, pool)); + SVN_BDB_ERR(bdb, bdb->env->log_archive(bdb->env, &filelist, flags)); + + if (filelist == NULL) + return svn_fs_bdb__close(bdb); + + for (filename = filelist; *filename != NULL; ++filename) + { + APR_ARRAY_PUSH(*logfiles, const char *) = apr_pstrdup(pool, *filename); + } + + free(filelist); + + return svn_fs_bdb__close(bdb); +} + + + +/* Copying a live Berkeley DB-base filesystem. */ + +/** + * Delete all unused log files from DBD enviroment at @a live_path that exist + * in @a backup_path. + */ +static svn_error_t * +svn_fs_base__clean_logs(const char *live_path, + const char *backup_path, + apr_pool_t *pool) +{ + apr_array_header_t *logfiles; + + SVN_ERR(base_bdb_logfiles(&logfiles, + live_path, + TRUE, /* Only unused logs */ + pool)); + + { /* Process unused logs from live area */ + int idx; + apr_pool_t *sub_pool = svn_pool_create(pool); + + /* Process log files. */ + for (idx = 0; idx < logfiles->nelts; idx++) + { + const char *log_file = APR_ARRAY_IDX(logfiles, idx, const char *); + const char *live_log_path; + const char *backup_log_path; + + svn_pool_clear(sub_pool); + live_log_path = svn_dirent_join(live_path, log_file, sub_pool); + backup_log_path = svn_dirent_join(backup_path, log_file, sub_pool); + + { /* Compare files. No point in using MD5 and wasting CPU cycles as we + got full copies of both logs */ + + svn_boolean_t files_match = FALSE; + svn_node_kind_t kind; + + /* Check to see if there is a corresponding log file in the backup + directory */ + SVN_ERR(svn_io_check_path(backup_log_path, &kind, pool)); + + /* If the copy of the log exists, compare them */ + if (kind == svn_node_file) + SVN_ERR(svn_io_files_contents_same_p(&files_match, + live_log_path, + backup_log_path, + sub_pool)); + + /* If log files do not match, go to the next log file. */ + if (!files_match) + continue; + } + + SVN_ERR(svn_io_remove_file2(live_log_path, FALSE, sub_pool)); + } + + svn_pool_destroy(sub_pool); + } + + return SVN_NO_ERROR; +} + + +/* DB_ENV->get_flags() and DB->get_pagesize() don't exist prior to + Berkeley DB 4.2. */ +#if SVN_BDB_VERSION_AT_LEAST(4, 2) + +/* Open the BDB environment at PATH and compare its configuration + flags with FLAGS. If every flag in FLAGS is set in the + environment, then set *MATCH to true. Else set *MATCH to false. */ +static svn_error_t * +check_env_flags(svn_boolean_t *match, + u_int32_t flags, + const char *path, + apr_pool_t *pool) +{ + bdb_env_baton_t *bdb; +#if SVN_BDB_VERSION_AT_LEAST(4, 7) + int flag_state; +#else + u_int32_t envflags; +#endif + + SVN_ERR(svn_fs_bdb__open(&bdb, path, + SVN_BDB_STANDARD_ENV_FLAGS, + 0666, pool)); +#if SVN_BDB_VERSION_AT_LEAST(4, 7) + SVN_BDB_ERR(bdb, bdb->env->log_get_config(bdb->env, flags, &flag_state)); +#else + SVN_BDB_ERR(bdb, bdb->env->get_flags(bdb->env, &envflags)); +#endif + + SVN_ERR(svn_fs_bdb__close(bdb)); + +#if SVN_BDB_VERSION_AT_LEAST(4, 7) + if (flag_state == 0) +#else + if (flags & envflags) +#endif + *match = TRUE; + else + *match = FALSE; + + return SVN_NO_ERROR; +} + + +/* Set *PAGESIZE to the size of pages used to hold items in the + database environment located at PATH. +*/ +static svn_error_t * +get_db_pagesize(u_int32_t *pagesize, + const char *path, + apr_pool_t *pool) +{ + bdb_env_baton_t *bdb; + DB *nodes_table; + + SVN_ERR(svn_fs_bdb__open(&bdb, path, + SVN_BDB_STANDARD_ENV_FLAGS, + 0666, pool)); + + /* ### We're only asking for the pagesize on the 'nodes' table. + Is this enough? We never call DB->set_pagesize() on any of + our tables, so presumably BDB is using the same default + pagesize for all our databases, right? */ + SVN_BDB_ERR(bdb, svn_fs_bdb__open_nodes_table(&nodes_table, bdb->env, + FALSE)); + SVN_BDB_ERR(bdb, nodes_table->get_pagesize(nodes_table, pagesize)); + SVN_BDB_ERR(bdb, nodes_table->close(nodes_table, 0)); + + return svn_fs_bdb__close(bdb); +} +#endif /* SVN_BDB_VERSION_AT_LEAST(4, 2) */ + + +/* Copy FILENAME from SRC_DIR to DST_DIR in byte increments of size + CHUNKSIZE. The read/write buffer of size CHUNKSIZE will be + allocated in POOL. If ALLOW_MISSING is set, we won't make a fuss + if FILENAME isn't found in SRC_DIR; otherwise, we will. */ +static svn_error_t * +copy_db_file_safely(const char *src_dir, + const char *dst_dir, + const char *filename, + u_int32_t chunksize, + svn_boolean_t allow_missing, + apr_pool_t *pool) +{ + apr_file_t *s = NULL, *d = NULL; /* init to null important for APR */ + const char *file_src_path = svn_dirent_join(src_dir, filename, pool); + const char *file_dst_path = svn_dirent_join(dst_dir, filename, pool); + svn_error_t *err; + char *buf; + + /* Open source file. If it's missing and that's allowed, there's + nothing more to do here. */ + err = svn_io_file_open(&s, file_src_path, + (APR_READ | APR_LARGEFILE), + APR_OS_DEFAULT, pool); + if (err && APR_STATUS_IS_ENOENT(err->apr_err) && allow_missing) + { + svn_error_clear(err); + return SVN_NO_ERROR; + } + SVN_ERR(err); + + /* Open destination file. */ + SVN_ERR(svn_io_file_open(&d, file_dst_path, (APR_WRITE | APR_CREATE | + APR_LARGEFILE), + APR_OS_DEFAULT, pool)); + + /* Allocate our read/write buffer. */ + buf = apr_palloc(pool, chunksize); + + /* Copy bytes till the cows come home. */ + while (1) + { + apr_size_t bytes_this_time = chunksize; + svn_error_t *read_err, *write_err; + + /* Read 'em. */ + if ((read_err = svn_io_file_read(s, buf, &bytes_this_time, pool))) + { + if (APR_STATUS_IS_EOF(read_err->apr_err)) + svn_error_clear(read_err); + else + { + svn_error_clear(svn_io_file_close(s, pool)); + svn_error_clear(svn_io_file_close(d, pool)); + return read_err; + } + } + + /* Write 'em. */ + if ((write_err = svn_io_file_write_full(d, buf, bytes_this_time, NULL, + pool))) + { + svn_error_clear(svn_io_file_close(s, pool)); + svn_error_clear(svn_io_file_close(d, pool)); + return write_err; + } + + /* read_err is either NULL, or a dangling pointer - but it is only a + dangling pointer if it used to be an EOF error. */ + if (read_err) + { + SVN_ERR(svn_io_file_close(s, pool)); + SVN_ERR(svn_io_file_close(d, pool)); + break; /* got EOF on read, all files closed, all done. */ + } + } + + return SVN_NO_ERROR; +} + + + + +static svn_error_t * +base_hotcopy(svn_fs_t *src_fs, + svn_fs_t *dst_fs, + const char *src_path, + const char *dest_path, + svn_boolean_t clean_logs, + svn_boolean_t incremental, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool) +{ + svn_error_t *err; + u_int32_t pagesize; + svn_boolean_t log_autoremove = FALSE; + int format; + + if (incremental) + return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, + _("BDB repositories do not support incremental " + "hotcopy")); + + /* Check the FS format number to be certain that we know how to + hotcopy this FS. Pre-1.2 filesystems did not have a format file (you + could say they were format "0"), so we will error here. This is not + optimal, but since this has been the case since 1.2.0, and no one has + complained, it apparently isn't much of a concern. (We did not check + the 'format' file in 1.2.x, but we did blindly try to copy 'locks', + which would have errored just the same.) */ + SVN_ERR(svn_io_read_version_file( + &format, svn_dirent_join(src_path, FORMAT_FILE, pool), pool)); + SVN_ERR(check_format(format)); + + /* If using Berkeley DB 4.2 or later, note whether the DB_LOG_AUTO_REMOVE + feature is on. If it is, we have a potential race condition: + another process might delete a logfile while we're in the middle + of copying all the logfiles. (This is not a huge deal; at worst, + the hotcopy fails with a file-not-found error.) */ +#if SVN_BDB_VERSION_AT_LEAST(4, 2) + err = check_env_flags(&log_autoremove, +#if SVN_BDB_VERSION_AT_LEAST(4, 7) + DB_LOG_AUTO_REMOVE, + /* DB_LOG_AUTO_REMOVE was named DB_LOG_AUTOREMOVE before Berkeley DB 4.7. */ +#else + DB_LOG_AUTOREMOVE, +#endif + src_path, pool); +#endif + SVN_ERR(err); + + /* Copy the DB_CONFIG file. */ + SVN_ERR(svn_io_dir_file_copy(src_path, dest_path, "DB_CONFIG", pool)); + + /* In order to copy the database files safely and atomically, we + must copy them in chunks which are multiples of the page-size + used by BDB. See sleepycat docs for details, or svn issue #1818. */ +#if SVN_BDB_VERSION_AT_LEAST(4, 2) + SVN_ERR(get_db_pagesize(&pagesize, src_path, pool)); + if (pagesize < SVN__STREAM_CHUNK_SIZE) + { + /* use the largest multiple of BDB pagesize we can. */ + int multiple = SVN__STREAM_CHUNK_SIZE / pagesize; + pagesize *= multiple; + } +#else + /* default to 128K chunks, which should be safe. + BDB almost certainly uses a power-of-2 pagesize. */ + pagesize = (4096 * 32); +#endif + + /* Copy the databases. */ + SVN_ERR(copy_db_file_safely(src_path, dest_path, + "nodes", pagesize, FALSE, pool)); + SVN_ERR(copy_db_file_safely(src_path, dest_path, + "transactions", pagesize, FALSE, pool)); + SVN_ERR(copy_db_file_safely(src_path, dest_path, + "revisions", pagesize, FALSE, pool)); + SVN_ERR(copy_db_file_safely(src_path, dest_path, + "copies", pagesize, FALSE, pool)); + SVN_ERR(copy_db_file_safely(src_path, dest_path, + "changes", pagesize, FALSE, pool)); + SVN_ERR(copy_db_file_safely(src_path, dest_path, + "representations", pagesize, FALSE, pool)); + SVN_ERR(copy_db_file_safely(src_path, dest_path, + "strings", pagesize, FALSE, pool)); + SVN_ERR(copy_db_file_safely(src_path, dest_path, + "uuids", pagesize, TRUE, pool)); + SVN_ERR(copy_db_file_safely(src_path, dest_path, + "locks", pagesize, TRUE, pool)); + SVN_ERR(copy_db_file_safely(src_path, dest_path, + "lock-tokens", pagesize, TRUE, pool)); + SVN_ERR(copy_db_file_safely(src_path, dest_path, + "node-origins", pagesize, TRUE, pool)); + SVN_ERR(copy_db_file_safely(src_path, dest_path, + "checksum-reps", pagesize, TRUE, pool)); + SVN_ERR(copy_db_file_safely(src_path, dest_path, + "miscellaneous", pagesize, TRUE, pool)); + + { + apr_array_header_t *logfiles; + int idx; + apr_pool_t *subpool; + + SVN_ERR(base_bdb_logfiles(&logfiles, + src_path, + FALSE, /* All logs */ + pool)); + + /* Process log files. */ + subpool = svn_pool_create(pool); + for (idx = 0; idx < logfiles->nelts; idx++) + { + svn_pool_clear(subpool); + err = svn_io_dir_file_copy(src_path, dest_path, + APR_ARRAY_IDX(logfiles, idx, + const char *), + subpool); + if (err) + { + if (log_autoremove) + return + svn_error_quick_wrap + (err, + _("Error copying logfile; the DB_LOG_AUTOREMOVE feature\n" + "may be interfering with the hotcopy algorithm. If\n" + "the problem persists, try deactivating this feature\n" + "in DB_CONFIG")); + else + return svn_error_trace(err); + } + } + svn_pool_destroy(subpool); + } + + /* Since this is a copy we will have exclusive access to the repository. */ + err = bdb_recover(dest_path, TRUE, pool); + if (err) + { + if (log_autoremove) + return + svn_error_quick_wrap + (err, + _("Error running catastrophic recovery on hotcopy; the\n" + "DB_LOG_AUTOREMOVE feature may be interfering with the\n" + "hotcopy algorithm. If the problem persists, try deactivating\n" + "this feature in DB_CONFIG")); + else + return svn_error_trace(err); + } + + /* Only now that the hotcopied filesystem is complete, + stamp it with a format file. */ + SVN_ERR(svn_io_write_version_file( + svn_dirent_join(dest_path, FORMAT_FILE, pool), format, pool)); + + if (clean_logs) + SVN_ERR(svn_fs_base__clean_logs(src_path, dest_path, pool)); + + return SVN_NO_ERROR; +} + + + +/* Deleting a Berkeley DB-based filesystem. */ + + +static svn_error_t * +base_delete_fs(const char *path, + apr_pool_t *pool) +{ + /* First, use the Berkeley DB library function to remove any shared + memory segments. */ + SVN_ERR(svn_fs_bdb__remove(path, pool)); + + /* Remove the environment directory. */ + return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool); +} + +static const svn_version_t * +base_version(void) +{ + SVN_VERSION_BODY; +} + +static const char * +base_get_description(void) +{ + return _("Module for working with a Berkeley DB repository."); +} + +static svn_error_t * +base_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 *)) +{ + return SVN_NO_ERROR; +} + + +/* Base FS library vtable, used by the FS loader library. */ +static fs_library_vtable_t library_vtable = { + base_version, + base_create, + base_open, + base_open_for_recovery, + base_upgrade, + base_verify, + base_delete_fs, + base_hotcopy, + base_get_description, + base_bdb_recover, + base_bdb_pack, + base_bdb_logfiles, + svn_fs_base__id_parse, + base_set_svn_fs_open +}; + +svn_error_t * +svn_fs_base__init(const svn_version_t *loader_version, + fs_library_vtable_t **vtable, apr_pool_t* common_pool) +{ + static const svn_version_checklist_t checklist[] = + { + { "svn_subr", svn_subr_version }, + { "svn_delta", svn_delta_version }, + { NULL, NULL } + }; + + /* Simplified version check to make sure we can safely use the + VTABLE parameter. The FS loader does a more exhaustive check. */ + if (loader_version->major != SVN_VER_MAJOR) + return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL, + _("Unsupported FS loader version (%d) for bdb"), + loader_version->major); + SVN_ERR(svn_ver_check_list(base_version(), checklist)); + SVN_ERR(check_bdb_version()); + SVN_ERR(svn_fs_bdb__init(common_pool)); + + *vtable = &library_vtable; + return SVN_NO_ERROR; +} |