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 *(*delete_fs)(const char *path, apr_pool_t *pool);
svn_error_t *(*hotcopy)(const char *src_path, const char *dest_path,
svn_boolean_t clean, apr_pool_t *pool);
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);
/* 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);
} fs_library_vtable_t;
svn_error_t *
svn_repos_open(svn_repos_t **repos_p,
const char *path,
apr_pool_t *pool)
{
/* Fetch a repository object initialized with a shared read/write
lock on the database. */
* The Repository object, created by svn_repos_open() and
svn_repos_create(). */
struct svn_repos_t
{
/* A Subversion filesystem object. */
svn_fs_t *fs;
/* The path to the repository's top-level directory. */
char *path;
/* The path to the repository's conf directory. */
char *conf_path;
/* The path to the repository's hooks directory. */
char *hook_path;
/* The path to the repository's locks directory. */
char *lock_path;
/* The path to the Berkeley DB filesystem environment. */
char *db_path;
/* The format number of this repository. */
int format;
/* The FS backend in use within this repository. */
const char *fs_type;
/* If non-null, a list of all the capabilities the client (on the
current connection) has self-reported. Each element is a
'const char *', one of SVN_RA_CAPABILITY_*.
Note: it is somewhat counterintuitive that we store the client's
capabilities, which are session-specific, on the repository
object. You'd think the capabilities here would represent the
*repository's* capabilities, but no, they represent the
client's -- we just don't have any other place to persist them. */
apr_array_header_t *client_capabilities;
/* Maps SVN_REPOS_CAPABILITY_foo keys to "yes" or "no" values.
If a capability is not yet discovered, it is absent from the table.
Most likely the keys and values are constants anyway (and
sufficiently well-informed internal code may just compare against
those constants' addresses, therefore). */
apr_hash_t *repository_capabilities;
};
svn_error_t *
svn_fs_fs__create(svn_fs_t *fs,
const char *path,
apr_pool_t *pool)
{
int format = SVN_FS_FS__FORMAT_NUMBER;
fs_fs_data_t *ffd = fs->fsap_data;
fs->path = apr_pstrdup(pool, path);
/* See if we had an explicitly requested pre-1.4- or pre-1.5-compatible. */
if (fs->config)
{
if (apr_hash_get(fs->config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE,
APR_HASH_KEY_STRING))
format = 1;
else if (apr_hash_get(fs->config, SVN_FS_CONFIG_PRE_1_5_COMPATIBLE,
APR_HASH_KEY_STRING))
format = 2;
}
ffd->format = format;
/* Allocate a repository object, filling in the format we will create. */
repos = create_svn_repos_t(path, pool);
repos->format = SVN_REPOS__FORMAT_NUMBER;
/* Discover the type of the filesystem we are about to create. */
if (fs_config)
{
repos->fs_type = apr_hash_get(fs_config, SVN_FS_CONFIG_FS_TYPE,
APR_HASH_KEY_STRING);
if (apr_hash_get(fs_config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE,
APR_HASH_KEY_STRING))
repos->format = SVN_REPOS__FORMAT_NUMBER_LEGACY;
}
if (! repos->fs_type)
repos->fs_type = DEFAULT_FS_TYPE;
/* Don't create a repository inside another repository. */
root_path = svn_repos_find_root_path(path, pool);
if (root_path != NULL)
return svn_error_createf(SVN_ERR_REPOS_BAD_ARGS, NULL, _("'%s' is a "
"subdirectory of an existing repository rooted "
"at '%s'"), path, root_path);
/* Create the various files and subdirectories for the repository. */
SVN_ERR_W(create_repos_structure(repos, path, fs_config, pool),
_("Repository creation failed"));
/* Lock if needed. */
SVN_ERR(lock_repos(repos, FALSE, FALSE, pool));
/* Create an environment for the filesystem. */
if ((err = svn_fs_create(&repos->fs, repos->db_path, fs_config, pool)))
{
/* If there was an error making the filesytem, e.g. unknown/supported
* filesystem type. Clean up after ourselves. Yes this is safe because
* create_repos_structure will fail if the path existed before we started
* so we can't accidentally remove a directory that previously existed. */
svn_error_clear(svn_io_remove_dir2(path, FALSE, NULL, NULL, pool));
return err;
}
/* This repository is ready. Stamp it with a format number. */
SVN_ERR(svn_io_write_version_file
(svn_path_join(path, SVN_REPOS__FORMAT, pool),
repos->format, pool));
/* Array of available subcommands.
* The entire list must be terminated with an entry of nulls.
*/
static const svn_opt_subcommand_desc_t cmd_table[] =
{
{"crashtest", subcommand_crashtest, {0}, N_
("usage: svnadmin crashtest REPOS_PATH
"
"Open the repository at REPOS_PATH, then abort, thus simulating
"
"a process that crashes while holding an open repository handle.
"),
{0} },
/* The RA layer vtable. */
typedef struct svn_ra__vtable_t {
/* This field should always remain first in the vtable. */
const svn_version_t *(*get_version)(void);
/* Return a short description of the RA implementation, as a localized
* string. */
const char *(*get_description)(void);
/* Return a list of actual URI schemes supported by this implementation.
* The returned array is NULL-terminated. */
const char * const *(*get_schemes)(apr_pool_t *pool);
/* Implementations of the public API functions. */
/* All fields in SESSION, except priv, have been initialized by the
time this is called. SESSION->priv may be set by this function. */
svn_error_t *(*open_session)(svn_ra_session_t *session,
const char *repos_URL,
const svn_ra_callbacks2_t *callbacks,
void *callback_baton,
apr_hash_t *config,
apr_pool_t *pool);
/* URL is guaranteed to have what get_repos_root() returns as a prefix. */
svn_error_t *(*reparent)(svn_ra_session_t *session,
const char *url,