📄 repos.c
字号:
APR_EOL_STR
APR_EOL_STR
"Visit http://subversion.tigris.org/ for more information."
APR_EOL_STR;
SVN_ERR_W (svn_io_file_create (readme_file_name, readme_contents, pool),
"Creating readme file");
}
/* Write the top-level FORMAT file. */
SVN_ERR (svn_io_write_version_file
(svn_path_join (path, SVN_REPOS__FORMAT, pool),
SVN_REPOS__VERSION, pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_repos_create (svn_repos_t **repos_p,
const char *path,
const char *unused_1,
const char *unused_2,
apr_hash_t *config,
apr_hash_t *fs_config,
apr_pool_t *pool)
{
svn_repos_t *repos;
/* Allocate a repository object. */
repos = apr_pcalloc (pool, sizeof (*repos));
/* Initialize the repository paths. */
init_repos_dirs (repos, path, pool);
/* Create the various files and subdirectories for the repository. */
SVN_ERR_W (create_repos_structure (repos, path, pool),
"Repository creation failed");
/* Create a Berkeley DB environment for the filesystem. */
SVN_ERR (svn_fs_create (&repos->fs, repos->db_path, fs_config, pool));
*repos_p = repos;
return SVN_NO_ERROR;
}
/* Check if @a path is the root of a repository by checking if the
* path contains the expected files and directories. Return TRUE
* on errors (which would be permission errors, probably) so that
* we the user will see them after we try to open the repository
* for real. */
static svn_boolean_t
check_repos_path (const char *path,
apr_pool_t *pool)
{
svn_node_kind_t kind;
svn_error_t *err;
err = svn_io_check_path (svn_path_join (path, SVN_REPOS__FORMAT, pool),
&kind, pool);
if (err)
{
svn_error_clear (err);
return TRUE;
}
if (kind != svn_node_file)
return FALSE;
/* Check the db/ subdir, but allow it to be a symlink (Subversion
works just fine if it's a symlink). */
err = svn_io_check_resolved_path
(svn_path_join (path, SVN_REPOS__DB_DIR, pool), &kind, pool);
if (err)
{
svn_error_clear (err);
return TRUE;
}
if (kind != svn_node_dir)
return FALSE;
return TRUE;
}
/* Verify that the repository's 'format' file is a suitable version. */
static svn_error_t *
check_repos_version (const char *path,
apr_pool_t *pool)
{
int version;
const char *format_path;
format_path = svn_path_join (path, SVN_REPOS__FORMAT, pool);
SVN_ERR (svn_io_read_version_file (&version, format_path, pool));
if (version != SVN_REPOS__VERSION)
return svn_error_createf
(SVN_ERR_REPOS_UNSUPPORTED_VERSION, NULL,
"Expected version '%d' of repository; found version '%d'",
SVN_REPOS__VERSION, version);
return SVN_NO_ERROR;
}
/* Set *REPOS_P to a repository at PATH which has been opened with
some kind of lock. LOCKTYPE is one of APR_FLOCK_SHARED (for
standard readers/writers), or APR_FLOCK_EXCLUSIVE (for processes
that need exclusive access, like db_recover.) OPEN_FS indicates
whether the database should be opened and placed into repos->fs.
Do all allocation in POOL. When POOL is destroyed, the lock will
be released as well. */
static svn_error_t *
get_repos (svn_repos_t **repos_p,
const char *path,
svn_boolean_t exclusive,
svn_boolean_t nonblocking,
svn_boolean_t open_fs,
apr_pool_t *pool)
{
svn_repos_t *repos;
/* Verify the validity of our repository format. */
SVN_ERR (check_repos_version (path, pool));
/* Allocate a repository object. */
repos = apr_pcalloc (pool, sizeof (*repos));
/* Initialize the repository paths. */
init_repos_dirs (repos, path, pool);
/* Locking. */
{
const char *lockfile_path;
svn_error_t *err;
/* Get a filehandle for the repository's db lockfile. */
lockfile_path = svn_repos_db_lockfile (repos, pool);
err = svn_io_file_lock2 (lockfile_path, exclusive, nonblocking, pool);
if (err != NULL && APR_STATUS_IS_EAGAIN (err->apr_err))
return err;
SVN_ERR_W (err, "Error opening db lockfile");
}
/* Open up the Berkeley filesystem only after obtaining the lock. */
if (open_fs)
SVN_ERR (svn_fs_open (&repos->fs, repos->db_path, NULL, pool));
*repos_p = repos;
return SVN_NO_ERROR;
}
const char *
svn_repos_find_root_path (const char *path,
apr_pool_t *pool)
{
const char *candidate = path;
while (1)
{
if (check_repos_path (candidate, pool))
break;
if (candidate[0] == '\0' || strcmp(candidate, "/") == 0)
return NULL;
candidate = svn_path_dirname (candidate, pool);
}
return candidate;
}
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. */
SVN_ERR (get_repos (repos_p, path, FALSE, FALSE, TRUE, pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_repos_delete (const char *path,
apr_pool_t *pool)
{
const char *db_path = svn_path_join (path, SVN_REPOS__DB_DIR, pool);
/* Delete the Berkeley environment... */
SVN_ERR (svn_fs_delete_fs (db_path, pool));
/* ...then blow away everything else. */
SVN_ERR (svn_io_remove_dir (path, pool));
return SVN_NO_ERROR;
}
svn_fs_t *
svn_repos_fs (svn_repos_t *repos)
{
if (! repos)
return NULL;
return repos->fs;
}
/* This code uses repository locking, which is motivated by the
* need to support DB_RUN_RECOVERY. Here's how it works:
*
* Every accessor of a repository's database takes out a shared lock
* on the repository -- both readers and writers get shared locks, and
* there can be an unlimited number of shared locks simultaneously.
*
* Sometimes, a db access returns the error DB_RUN_RECOVERY. When
* this happens, we need to run svn_fs_berkeley_recover() on the db
* with no other accessors present. So we take out an exclusive lock
* on the repository. From the moment we request the exclusive lock,
* no more shared locks are granted, and when the last shared lock
* disappears, the exclusive lock is granted. As soon as we get it,
* we can run recovery.
*
* We assume that once any berkeley call returns DB_RUN_RECOVERY, they
* all do, until recovery is run.
*/
svn_error_t *
svn_repos_recover2 (const char *path,
svn_boolean_t nonblocking,
svn_error_t *(*start_callback) (void *baton),
void *start_callback_baton,
apr_pool_t *pool)
{
svn_repos_t *repos;
apr_pool_t *subpool = svn_pool_create (pool);
/* Fetch a repository object initialized with an EXCLUSIVE lock on
the database. This will at least prevent others from trying to
read or write to it while we run recovery. */
SVN_ERR (get_repos (&repos, path, TRUE, nonblocking,
FALSE, /* don't try to open the db yet. */
subpool));
if (start_callback)
SVN_ERR (start_callback (start_callback_baton));
/* Recover the database to a consistent state. */
SVN_ERR (svn_fs_berkeley_recover (repos->db_path, subpool));
/* Close shop and free the subpool, to release the exclusive lock. */
svn_pool_destroy (subpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_repos_recover (const char *path,
apr_pool_t *pool)
{
return svn_repos_recover2 (path, FALSE, NULL, NULL, pool);
}
svn_error_t *svn_repos_db_logfiles (apr_array_header_t **logfiles,
const char *path,
svn_boolean_t only_unused,
apr_pool_t *pool)
{
svn_repos_t *repos;
int i;
SVN_ERR (get_repos (&repos, path,
FALSE, FALSE,
FALSE, /* Do not open fs. */
pool));
SVN_ERR (svn_fs_berkeley_logfiles (logfiles,
svn_repos_db_env (repos, pool),
only_unused,
pool));
/* Loop, printing log files. */
for (i = 0; i < (*logfiles)->nelts; i++)
{
const char ** log_file = &(APR_ARRAY_IDX (*logfiles, i, const char *));
*log_file = svn_path_join(SVN_REPOS__DB_DIR, *log_file, pool);
}
return SVN_NO_ERROR;
}
/** Hot copy structure copy context.
*/
struct hotcopy_ctx_t {
const char *dest; /* target location to construct */
unsigned int src_len; /* len of the source path*/
};
/** Called by (svn_io_dir_walk).
* Copies the repository structure with exception of
* @c SVN_REPOS__DB_DIR and @c SVN_REPOS__LOCK_DIR.
* Those directories are handled separetly.
* @a baton is a pointer to (struct hotcopy_ctx_t) specifying
* destination path to copy to and the length of the source path.
*
* @copydoc svn_io_dir_walk()
*/
static svn_error_t *hotcopy_structure (void *baton,
const char *path,
const apr_finfo_t *finfo,
apr_pool_t *pool)
{
const struct hotcopy_ctx_t *ctx = ((struct hotcopy_ctx_t *) baton);
const char *sub_path;
const char *target;
if (strlen (path) == ctx->src_len)
{
sub_path = "";
}
else
{
sub_path = &path[ctx->src_len+1];
/* Check if we are inside db directory and if so skip it */
if (svn_path_compare_paths(
svn_path_get_longest_ancestor (SVN_REPOS__DB_DIR, sub_path, pool),
SVN_REPOS__DB_DIR) == 0)
return SVN_NO_ERROR;
if (svn_path_compare_paths(
svn_path_get_longest_ancestor (SVN_REPOS__LOCK_DIR,
sub_path, pool),
SVN_REPOS__LOCK_DIR) == 0)
return SVN_NO_ERROR;
}
target = svn_path_join (ctx->dest, sub_path, pool);
if (finfo->filetype == APR_DIR)
{
SVN_ERR (create_repos_dir (target, pool));
}
else if (finfo->filetype == APR_REG)
{
SVN_ERR(svn_io_copy_file(path, target, TRUE, pool));
}
return SVN_NO_ERROR;
}
/** Obtain a lock on db logs lock file. Create one if it does not exist.
*/
static svn_error_t *
lock_db_logs_file (svn_repos_t *repos,
svn_boolean_t exclusive,
apr_pool_t *pool)
{
const char * lock_file = svn_repos_db_logs_lockfile (repos, pool);
/* Try to create a lock file, in case if it is missing. As in case of the
repositories created before hotcopy functionality. */
svn_error_clear (create_db_logs_lock (repos, pool));
SVN_ERR (svn_io_file_lock2 (lock_file, exclusive, FALSE, pool));
return SVN_NO_ERROR;
}
/* Make a copy of a repository with hot backup of fs. */
svn_error_t *
svn_repos_hotcopy (const char *src_path,
const char *dst_path,
svn_boolean_t clean_logs,
apr_pool_t *pool)
{
svn_repos_t *src_repos;
svn_repos_t *dst_repos;
struct hotcopy_ctx_t hotcopy_context;
/* Try to open original repository */
SVN_ERR (get_repos (&src_repos, src_path,
FALSE, FALSE,
FALSE, /* don't try to open the db yet. */
pool));
/* If we are going to clean logs, then get an exclusive lock on
db-logs.lock, to ensure that no one else will work with logs.
If we are just copying, then get a shared lock to ensure that
no one else will clean logs while we copying them */
SVN_ERR (lock_db_logs_file (src_repos, clean_logs, pool));
/* Copy the repository to a new path, with exception of
specially handled directories */
hotcopy_context.dest = dst_path;
hotcopy_context.src_len = strlen (src_path);
SVN_ERR (svn_io_dir_walk (src_path,
0,
hotcopy_structure,
&hotcopy_context,
pool));
/* Prepare dst_repos object so that we may create locks,
so that we may open repository */
dst_repos = apr_pcalloc (pool, sizeof (*dst_repos));
init_repos_dirs (dst_repos, dst_path, pool);
SVN_ERR (create_locks (dst_repos, pool));
SVN_ERR (svn_io_dir_make_sgid (dst_repos->db_path, APR_OS_DEFAULT, pool));
/* Open repository, since before we only initialized the directories.
Above is a work around because lock creation functions expect a
pointer to (svn_repos_t) with initialized paths. */
/* Exclusively lock the new repository.
No one should be accessing it at the moment */
SVN_ERR (get_repos (&dst_repos, dst_path,
TRUE, FALSE,
FALSE, /* don't try to open the db yet. */
pool));
SVN_ERR (svn_fs_hotcopy (src_repos->db_path, dst_repos->db_path,
clean_logs, pool));
return SVN_NO_ERROR;
}
/* Return the library version number. */
const svn_version_t *
svn_repos_version (void)
{
SVN_VERSION_BODY;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -