#include "svn_pools.h"
#include "svn_path.h"
#include "svn_dirent_uri.h"
#include "fs_fs.h"
#include "hotcopy.h"
#include "util.h"
#include "recovery.h"
#include "revprops.h"
#include "rep-cache.h"
#include "../libsvn_fs/fs-loader.h"
#include "svn_private_config.h"
static svn_error_t *
hotcopy_io_dir_file_copy(svn_boolean_t *skipped_p,
const char *src_path,
const char *dst_path,
const char *file,
apr_pool_t *scratch_pool)
{
const svn_io_dirent2_t *src_dirent;
const svn_io_dirent2_t *dst_dirent;
const char *src_target;
const char *dst_target;
dst_target = svn_dirent_join(dst_path, file, scratch_pool);
SVN_ERR(svn_io_stat_dirent2(&dst_dirent, dst_target, FALSE, TRUE,
scratch_pool, scratch_pool));
if (dst_dirent->kind != svn_node_none)
{
src_target = svn_dirent_join(src_path, file, scratch_pool);
SVN_ERR(svn_io_stat_dirent2(&src_dirent, src_target, FALSE, FALSE,
scratch_pool, scratch_pool));
if (src_dirent->kind == dst_dirent->kind &&
src_dirent->special == dst_dirent->special &&
src_dirent->filesize == dst_dirent->filesize &&
src_dirent->mtime <= dst_dirent->mtime)
return SVN_NO_ERROR;
}
if (skipped_p)
*skipped_p = FALSE;
return svn_error_trace(svn_io_dir_file_copy(src_path, dst_path, file,
scratch_pool));
}
static svn_error_t *
entry_name_to_utf8(const char **name_p,
const char *name,
const char *parent,
apr_pool_t *pool)
{
svn_error_t *err = svn_path_cstring_to_utf8(name_p, name, pool);
if (err && err->apr_err == APR_EINVAL)
{
return svn_error_createf(err->apr_err, err,
_("Error converting entry "
"in directory '%s' to UTF-8"),
svn_dirent_local_style(parent, pool));
}
return err;
}
static svn_error_t *
hotcopy_io_copy_dir_recursively(svn_boolean_t *skipped_p,
const char *src,
const char *dst_parent,
const char *dst_basename,
svn_boolean_t copy_perms,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
svn_node_kind_t kind;
apr_status_t status;
const char *dst_path;
apr_dir_t *this_dir;
apr_finfo_t this_entry;
apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME;
apr_pool_t *subpool = svn_pool_create(pool);
dst_path = svn_dirent_join(dst_parent, dst_basename, pool);
SVN_ERR(svn_io_check_path(src, &kind, subpool));
if (kind != svn_node_dir)
return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
_("Source '%s' is not a directory"),
svn_dirent_local_style(src, pool));
SVN_ERR(svn_io_check_path(dst_parent, &kind, subpool));
if (kind != svn_node_dir)
return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
_("Destination '%s' is not a directory"),
svn_dirent_local_style(dst_parent, pool));
SVN_ERR(svn_io_check_path(dst_path, &kind, subpool));
SVN_ERR(svn_io_make_dir_recursively(dst_path, pool));
SVN_ERR(svn_io_dir_open(&this_dir, src, subpool));
for (status = apr_dir_read(&this_entry, flags, this_dir);
status == APR_SUCCESS;
status = apr_dir_read(&this_entry, flags, this_dir))
{
if ((this_entry.name[0] == '.')
&& ((this_entry.name[1] == '\0')
|| ((this_entry.name[1] == '.')
&& (this_entry.name[2] == '\0'))))
{
continue;
}
else
{
const char *entryname_utf8;
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
SVN_ERR(entry_name_to_utf8(&entryname_utf8, this_entry.name,
src, subpool));
if (this_entry.filetype == APR_REG)
{
SVN_ERR(hotcopy_io_dir_file_copy(skipped_p, src, dst_path,
entryname_utf8, subpool));
}
else if (this_entry.filetype == APR_LNK)
{
const char *src_target = svn_dirent_join(src, entryname_utf8,
subpool);
const char *dst_target = svn_dirent_join(dst_path,
entryname_utf8,
subpool);
SVN_ERR(svn_io_copy_link(src_target, dst_target,
subpool));
}
else if (this_entry.filetype == APR_DIR)
{
const char *src_target;
if (strcmp(src, dst_parent) == 0
&& strcmp(entryname_utf8, dst_basename) == 0)
continue;
src_target = svn_dirent_join(src, entryname_utf8, subpool);
SVN_ERR(hotcopy_io_copy_dir_recursively(skipped_p,
src_target,
dst_path,
entryname_utf8,
copy_perms,
cancel_func,
cancel_baton,
subpool));
}
}
}
if (! (APR_STATUS_IS_ENOENT(status)))
return svn_error_wrap_apr(status, _("Can't read directory '%s'"),
svn_dirent_local_style(src, pool));
status = apr_dir_close(this_dir);
if (status)
return svn_error_wrap_apr(status, _("Error closing directory '%s'"),
svn_dirent_local_style(src, pool));
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
static svn_error_t *
hotcopy_copy_shard_file(svn_boolean_t *skipped_p,
const char *src_subdir,
const char *dst_subdir,
svn_revnum_t rev,
int max_files_per_dir,
apr_pool_t *scratch_pool)
{
const char *src_subdir_shard = src_subdir,
*dst_subdir_shard = dst_subdir;
if (max_files_per_dir)
{
const char *shard = apr_psprintf(scratch_pool, "%ld",
rev / max_files_per_dir);
src_subdir_shard = svn_dirent_join(src_subdir, shard, scratch_pool);
dst_subdir_shard = svn_dirent_join(dst_subdir, shard, scratch_pool);
if (rev % max_files_per_dir == 0)
{
SVN_ERR(svn_io_make_dir_recursively(dst_subdir_shard, scratch_pool));
SVN_ERR(svn_io_copy_perms(dst_subdir, dst_subdir_shard,
scratch_pool));
}
}
SVN_ERR(hotcopy_io_dir_file_copy(skipped_p,
src_subdir_shard, dst_subdir_shard,
apr_psprintf(scratch_pool, "%ld", rev),
scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
hotcopy_copy_packed_shard(svn_boolean_t *skipped_p,
svn_revnum_t *dst_min_unpacked_rev,
svn_fs_t *src_fs,
svn_fs_t *dst_fs,
svn_revnum_t rev,
int max_files_per_dir,
apr_pool_t *scratch_pool)
{
const char *src_subdir;
const char *dst_subdir;
const char *packed_shard;
const char *src_subdir_packed_shard;
svn_revnum_t revprop_rev;
apr_pool_t *iterpool;
fs_fs_data_t *src_ffd = src_fs->fsap_data;
src_subdir = svn_dirent_join(src_fs->path, PATH_REVS_DIR, scratch_pool);
dst_subdir = svn_dirent_join(dst_fs->path, PATH_REVS_DIR, scratch_pool);
packed_shard = apr_psprintf(scratch_pool, "%ld" PATH_EXT_PACKED_SHARD,
rev / max_files_per_dir);
src_subdir_packed_shard = svn_dirent_join(src_subdir, packed_shard,
scratch_pool);
SVN_ERR(hotcopy_io_copy_dir_recursively(skipped_p, src_subdir_packed_shard,
dst_subdir, packed_shard,
TRUE ,
NULL , NULL,
scratch_pool));
src_subdir = svn_dirent_join(src_fs->path, PATH_REVPROPS_DIR, scratch_pool);
dst_subdir = svn_dirent_join(dst_fs->path, PATH_REVPROPS_DIR, scratch_pool);
if ( src_ffd->format < SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT
|| src_ffd->min_unpacked_rev < rev + max_files_per_dir)
{
iterpool = svn_pool_create(scratch_pool);
for (revprop_rev = rev;
revprop_rev < rev + max_files_per_dir;
revprop_rev++)
{
svn_pool_clear(iterpool);
SVN_ERR(hotcopy_copy_shard_file(skipped_p, src_subdir, dst_subdir,
revprop_rev, max_files_per_dir,
iterpool));
}
svn_pool_destroy(iterpool);
}
else
{
if (rev == 0)
SVN_ERR(hotcopy_copy_shard_file(skipped_p, src_subdir, dst_subdir,
0, max_files_per_dir,
scratch_pool));
packed_shard = apr_psprintf(scratch_pool, "%ld" PATH_EXT_PACKED_SHARD,
rev / max_files_per_dir);
src_subdir_packed_shard = svn_dirent_join(src_subdir, packed_shard,
scratch_pool);
SVN_ERR(hotcopy_io_copy_dir_recursively(skipped_p,
src_subdir_packed_shard,
dst_subdir, packed_shard,
TRUE ,
NULL , NULL,
scratch_pool));
}
if (*dst_min_unpacked_rev < rev + max_files_per_dir)
{
*dst_min_unpacked_rev = rev + max_files_per_dir;
SVN_ERR(svn_fs_fs__write_min_unpacked_rev(dst_fs,
*dst_min_unpacked_rev,
scratch_pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *
hotcopy_remove_file(const char *path,
apr_pool_t *pool)
{
SVN_ERR(svn_io_set_file_read_write(path, TRUE, pool));
SVN_ERR(svn_io_remove_file2(path, TRUE, pool));
return SVN_NO_ERROR;
}
static svn_error_t *
hotcopy_remove_files(svn_fs_t *dst_fs,
const char *dst_subdir,
svn_revnum_t start_rev,
svn_revnum_t end_rev,
int max_files_per_dir,
apr_pool_t *scratch_pool)
{
const char *shard;
const char *dst_subdir_shard;
svn_revnum_t rev;
apr_pool_t *iterpool;
shard = apr_psprintf(scratch_pool, "%ld", start_rev / max_files_per_dir);
dst_subdir_shard = svn_dirent_join(dst_subdir, shard, scratch_pool);
iterpool = svn_pool_create(scratch_pool);
for (rev = start_rev; rev < end_rev; rev++)
{
svn_pool_clear(iterpool);
if (rev != start_rev && rev % max_files_per_dir == 0)
{
shard = apr_psprintf(iterpool, "%ld", rev / max_files_per_dir);
dst_subdir_shard = svn_dirent_join(dst_subdir, shard, scratch_pool);
}
SVN_ERR(hotcopy_remove_file(svn_dirent_join(dst_subdir_shard,
apr_psprintf(iterpool,
"%ld", rev),
iterpool),
iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
hotcopy_remove_rev_files(svn_fs_t *dst_fs,
svn_revnum_t start_rev,
svn_revnum_t end_rev,
int max_files_per_dir,
apr_pool_t *scratch_pool)
{
SVN_ERR_ASSERT(start_rev <= end_rev);
SVN_ERR(hotcopy_remove_files(dst_fs,
svn_dirent_join(dst_fs->path,
PATH_REVS_DIR,
scratch_pool),
start_rev, end_rev,
max_files_per_dir, scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
hotcopy_remove_revprop_files(svn_fs_t *dst_fs,
svn_revnum_t start_rev,
svn_revnum_t end_rev,
int max_files_per_dir,
apr_pool_t *scratch_pool)
{
SVN_ERR_ASSERT(start_rev <= end_rev);
SVN_ERR(hotcopy_remove_files(dst_fs,
svn_dirent_join(dst_fs->path,
PATH_REVPROPS_DIR,
scratch_pool),
start_rev ? start_rev : 1, end_rev,
max_files_per_dir, scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
hotcopy_incremental_check_preconditions(svn_fs_t *src_fs,
svn_fs_t *dst_fs,
apr_pool_t *pool)
{
fs_fs_data_t *src_ffd = src_fs->fsap_data;
fs_fs_data_t *dst_ffd = dst_fs->fsap_data;
if (src_ffd->format != dst_ffd->format)
return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("The FSFS format (%d) of the hotcopy source does not match the "
"FSFS format (%d) of the hotcopy destination; please upgrade "
"both repositories to the same format"),
src_ffd->format, dst_ffd->format);
if (strcmp(src_fs->uuid, dst_fs->uuid) != 0)
return svn_error_create(SVN_ERR_RA_UUID_MISMATCH, NULL,
_("The UUID of the hotcopy source does "
"not match the UUID of the hotcopy "
"destination"));
if (src_ffd->max_files_per_dir != dst_ffd->max_files_per_dir)
return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("The sharding layout configuration "
"of the hotcopy source does not match "
"the sharding layout configuration of "
"the hotcopy destination"));
return SVN_NO_ERROR;
}
static svn_error_t *
remove_folder(const char *path,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
svn_error_t *err = svn_io_remove_dir2(path, TRUE,
cancel_func, cancel_baton, pool);
if (err && APR_STATUS_IS_ENOTEMPTY(err->apr_err))
{
svn_error_clear(err);
err = SVN_NO_ERROR;
}
return svn_error_trace(err);
}
static svn_error_t *
hotcopy_revisions(svn_fs_t *src_fs,
svn_fs_t *dst_fs,
svn_revnum_t src_youngest,
svn_revnum_t dst_youngest,
svn_boolean_t incremental,
const char *src_revs_dir,
const char *dst_revs_dir,
const char *src_revprops_dir,
const char *dst_revprops_dir,
svn_fs_hotcopy_notify_t notify_func,
void* notify_baton,
svn_cancel_func_t cancel_func,
void* cancel_baton,
apr_pool_t *pool)
{
fs_fs_data_t *src_ffd = src_fs->fsap_data;
fs_fs_data_t *dst_ffd = dst_fs->fsap_data;
int max_files_per_dir = src_ffd->max_files_per_dir;
svn_revnum_t src_min_unpacked_rev;
svn_revnum_t dst_min_unpacked_rev;
svn_revnum_t rev;
apr_pool_t *iterpool;
if (src_ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
{
SVN_ERR(svn_fs_fs__read_min_unpacked_rev(&src_min_unpacked_rev,
src_fs, pool));
SVN_ERR(svn_fs_fs__read_min_unpacked_rev(&dst_min_unpacked_rev,
dst_fs, pool));
if (src_min_unpacked_rev < dst_min_unpacked_rev)
return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("The hotcopy destination already contains "
"more packed revisions (%lu) than the "
"hotcopy source contains (%lu)"),
dst_min_unpacked_rev - 1,
src_min_unpacked_rev - 1);
SVN_ERR(svn_io_dir_file_copy(src_fs->path, dst_fs->path,
PATH_MIN_UNPACKED_REV, pool));
}
else
{
src_min_unpacked_rev = 0;
dst_min_unpacked_rev = 0;
}
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
iterpool = svn_pool_create(pool);
for (rev = 0; rev < src_min_unpacked_rev; rev += max_files_per_dir)
{
svn_boolean_t skipped = TRUE;
svn_revnum_t pack_end_rev;
svn_pool_clear(iterpool);
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
SVN_ERR(hotcopy_copy_packed_shard(&skipped, &dst_min_unpacked_rev,
src_fs, dst_fs,
rev, max_files_per_dir,
iterpool));
pack_end_rev = rev + max_files_per_dir - 1;
if (pack_end_rev > dst_youngest)
{
SVN_ERR(svn_fs_fs__write_current(dst_fs, pack_end_rev, 0, 0,
iterpool));
}
if (notify_func && !skipped)
notify_func(notify_baton, rev, pack_end_rev, iterpool);
if (incremental)
{
SVN_ERR(hotcopy_remove_rev_files(dst_fs, rev,
rev + max_files_per_dir,
max_files_per_dir, iterpool));
if (dst_ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT)
SVN_ERR(hotcopy_remove_revprop_files(dst_fs, rev,
rev + max_files_per_dir,
max_files_per_dir,
iterpool));
}
SVN_ERR(remove_folder(svn_fs_fs__path_rev_shard(dst_fs, rev, iterpool),
cancel_func, cancel_baton, iterpool));
if (rev > 0 && dst_ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT)
SVN_ERR(remove_folder(svn_fs_fs__path_revprops_shard(dst_fs, rev,
iterpool),
cancel_func, cancel_baton, iterpool));
}
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
SVN_ERR_ASSERT(rev == src_min_unpacked_rev);
SVN_ERR_ASSERT(src_min_unpacked_rev == dst_min_unpacked_rev);
for (; rev <= src_youngest; rev++)
{
svn_boolean_t skipped = TRUE;
svn_pool_clear(iterpool);
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
SVN_ERR(hotcopy_copy_shard_file(&skipped,
src_revs_dir, dst_revs_dir, rev,
max_files_per_dir,
iterpool));
SVN_ERR(hotcopy_copy_shard_file(&skipped,
src_revprops_dir, dst_revprops_dir,
rev, max_files_per_dir,
iterpool));
if (rev > dst_youngest)
{
if (max_files_per_dir && (rev % max_files_per_dir == 0))
{
SVN_ERR(svn_fs_fs__write_current(dst_fs, rev, 0, 0,
iterpool));
}
}
if (notify_func && !skipped)
notify_func(notify_baton, rev, rev, iterpool);
}
svn_pool_destroy(iterpool);
SVN_ERR_ASSERT(rev == src_youngest + 1);
return SVN_NO_ERROR;
}
static svn_error_t *
hotcopy_revisions_old(svn_fs_t *src_fs,
svn_fs_t *dst_fs,
svn_revnum_t src_youngest,
const char *src_revs_dir,
const char *dst_revs_dir,
const char *src_revprops_dir,
const char *dst_revprops_dir,
svn_fs_hotcopy_notify_t notify_func,
void* notify_baton,
svn_cancel_func_t cancel_func,
void* cancel_baton,
apr_pool_t *pool)
{
apr_pool_t *iterpool = svn_pool_create(pool);
svn_revnum_t rev;
for (rev = 0; rev <= src_youngest; rev++)
{
svn_boolean_t skipped = TRUE;
svn_pool_clear(iterpool);
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
SVN_ERR(hotcopy_io_dir_file_copy(&skipped, src_revs_dir, dst_revs_dir,
apr_psprintf(iterpool, "%ld", rev),
iterpool));
SVN_ERR(hotcopy_io_dir_file_copy(&skipped, src_revprops_dir,
dst_revprops_dir,
apr_psprintf(iterpool, "%ld", rev),
iterpool));
if (notify_func && !skipped)
notify_func(notify_baton, rev, rev, iterpool);
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
struct hotcopy_body_baton {
svn_fs_t *src_fs;
svn_fs_t *dst_fs;
svn_boolean_t incremental;
svn_fs_hotcopy_notify_t notify_func;
void *notify_baton;
svn_cancel_func_t cancel_func;
void *cancel_baton;
};
static svn_error_t *
hotcopy_body(void *baton, apr_pool_t *pool)
{
struct hotcopy_body_baton *hbb = baton;
svn_fs_t *src_fs = hbb->src_fs;
fs_fs_data_t *src_ffd = src_fs->fsap_data;
svn_fs_t *dst_fs = hbb->dst_fs;
fs_fs_data_t *dst_ffd = dst_fs->fsap_data;
svn_boolean_t incremental = hbb->incremental;
svn_fs_hotcopy_notify_t notify_func = hbb->notify_func;
void* notify_baton = hbb->notify_baton;
svn_cancel_func_t cancel_func = hbb->cancel_func;
void* cancel_baton = hbb->cancel_baton;
svn_revnum_t src_youngest;
apr_uint64_t src_next_node_id;
apr_uint64_t src_next_copy_id;
svn_revnum_t dst_youngest;
const char *src_revprops_dir;
const char *dst_revprops_dir;
const char *src_revs_dir;
const char *dst_revs_dir;
const char *src_subdir;
const char *dst_subdir;
svn_node_kind_t kind;
if (src_ffd->format >= SVN_FS_FS__MIN_CONFIG_FILE)
{
svn_error_t *err;
err = svn_io_dir_file_copy(src_fs->path, dst_fs->path, PATH_CONFIG,
pool);
if (err)
{
if (APR_STATUS_IS_ENOENT(err->apr_err))
{
const char *src_abspath;
const char *dst_abspath;
const char *config_relpath;
svn_error_t *err2;
config_relpath = svn_dirent_join(src_fs->path, PATH_CONFIG, pool);
err2 = svn_dirent_get_absolute(&src_abspath, src_fs->path, pool);
if (err2)
return svn_error_trace(svn_error_compose_create(err, err2));
err2 = svn_dirent_get_absolute(&dst_abspath, dst_fs->path, pool);
if (err2)
return svn_error_trace(svn_error_compose_create(err, err2));
src_abspath = svn_dirent_dirname(src_abspath, pool);
dst_abspath = svn_dirent_dirname(dst_abspath, pool);
return svn_error_quick_wrapf(err,
_("Failed to create hotcopy at '%s'. "
"The file '%s' is missing from the source "
"repository. Please create this file, for "
"instance by running 'svnadmin upgrade %s'"),
dst_abspath, config_relpath, src_abspath);
}
else
return svn_error_trace(err);
}
}
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
SVN_ERR(svn_fs_fs__read_current(&src_youngest, &src_next_node_id,
&src_next_copy_id, src_fs, pool));
if (incremental)
{
SVN_ERR(svn_fs_fs__youngest_rev(&dst_youngest, dst_fs, pool));
if (src_youngest < dst_youngest)
return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("The hotcopy destination already contains more revisions "
"(%lu) than the hotcopy source contains (%lu); are source "
"and destination swapped?"),
dst_youngest, src_youngest);
}
else
dst_youngest = 0;
src_revs_dir = svn_dirent_join(src_fs->path, PATH_REVS_DIR, pool);
dst_revs_dir = svn_dirent_join(dst_fs->path, PATH_REVS_DIR, pool);
src_revprops_dir = svn_dirent_join(src_fs->path, PATH_REVPROPS_DIR, pool);
dst_revprops_dir = svn_dirent_join(dst_fs->path, PATH_REVPROPS_DIR, pool);
SVN_ERR(svn_io_make_dir_recursively(dst_revs_dir, pool));
SVN_ERR(svn_io_make_dir_recursively(dst_revprops_dir, pool));
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
if (src_ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
{
SVN_ERR(hotcopy_revisions(src_fs, dst_fs, src_youngest, dst_youngest,
incremental, src_revs_dir, dst_revs_dir,
src_revprops_dir, dst_revprops_dir,
notify_func, notify_baton,
cancel_func, cancel_baton, pool));
SVN_ERR(svn_fs_fs__write_current(dst_fs, src_youngest, 0, 0, pool));
}
else
{
SVN_ERR(hotcopy_revisions_old(src_fs, dst_fs, src_youngest,
src_revs_dir, dst_revs_dir,
src_revprops_dir, dst_revprops_dir,
notify_func, notify_baton,
cancel_func, cancel_baton, pool));
SVN_ERR(svn_fs_fs__write_current(dst_fs, src_youngest, src_next_node_id,
src_next_copy_id, pool));
}
dst_subdir = svn_dirent_join(dst_fs->path, PATH_LOCKS_DIR, pool);
SVN_ERR(svn_io_remove_dir2(dst_subdir, TRUE, cancel_func, cancel_baton,
pool));
src_subdir = svn_dirent_join(src_fs->path, PATH_LOCKS_DIR, pool);
SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
if (kind == svn_node_dir)
SVN_ERR(svn_io_copy_dir_recursively(src_subdir, dst_fs->path,
PATH_LOCKS_DIR, TRUE,
cancel_func, cancel_baton, pool));
src_subdir = svn_dirent_join(src_fs->path, PATH_NODE_ORIGINS_DIR, pool);
SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
if (kind == svn_node_dir)
SVN_ERR(hotcopy_io_copy_dir_recursively(NULL, src_subdir, dst_fs->path,
PATH_NODE_ORIGINS_DIR, TRUE,
cancel_func, cancel_baton, pool));
if (dst_ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT)
{
src_subdir = svn_dirent_join(src_fs->path, REP_CACHE_DB_NAME, pool);
dst_subdir = svn_dirent_join(dst_fs->path, REP_CACHE_DB_NAME, pool);
SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
if (kind == svn_node_file)
{
SVN_ERR(svn_sqlite__hotcopy(src_subdir, dst_subdir, pool));
SVN_ERR(svn_io_set_file_read_write(dst_subdir, FALSE, pool));
SVN_ERR(svn_fs_fs__del_rep_reference(dst_fs, src_youngest, pool));
}
}
if (dst_ffd->format >= SVN_FS_FS__MIN_TXN_CURRENT_FORMAT)
SVN_ERR(svn_io_dir_file_copy(src_fs->path, dst_fs->path,
PATH_TXN_CURRENT, pool));
SVN_ERR(svn_fs_fs__write_format(dst_fs, TRUE, pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__hotcopy(svn_fs_t *src_fs,
svn_fs_t *dst_fs,
const char *src_path,
const char *dst_path,
svn_boolean_t incremental,
svn_fs_hotcopy_notify_t notify_func,
void *notify_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_mutex__t *common_pool_lock,
apr_pool_t *pool,
apr_pool_t *common_pool)
{
struct hotcopy_body_baton hbb;
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
SVN_ERR(svn_fs_fs__open(src_fs, src_path, pool));
if (incremental)
{
const char *dst_format_abspath;
svn_node_kind_t dst_format_kind;
dst_format_abspath = svn_dirent_join(dst_path, PATH_FORMAT, pool);
SVN_ERR(svn_io_check_path(dst_format_abspath, &dst_format_kind, pool));
if (dst_format_kind == svn_node_none)
{
incremental = FALSE;
}
}
if (incremental)
{
SVN_ERR(svn_fs_fs__open(dst_fs, dst_path, pool));
SVN_ERR(hotcopy_incremental_check_preconditions(src_fs, dst_fs, pool));
SVN_ERR(svn_fs_fs__initialize_shared_data(dst_fs, common_pool_lock,
pool, common_pool));
SVN_ERR(svn_fs_fs__initialize_caches(dst_fs, pool));
}
else
{
fs_fs_data_t *src_ffd = src_fs->fsap_data;
SVN_ERR(svn_fs_fs__create_file_tree(dst_fs, dst_path, src_ffd->format,
src_ffd->max_files_per_dir,
src_ffd->use_log_addressing,
pool));
SVN_ERR(svn_fs_fs__set_uuid(dst_fs, src_fs->uuid, NULL, pool));
SVN_ERR(hotcopy_remove_file(svn_fs_fs__path_rev(dst_fs, 0, pool),
pool));
SVN_ERR(hotcopy_remove_file(svn_fs_fs__path_revprops(dst_fs, 0, pool),
pool));
SVN_ERR(svn_fs_fs__initialize_shared_data(dst_fs, common_pool_lock,
pool, common_pool));
SVN_ERR(svn_fs_fs__initialize_caches(dst_fs, pool));
}
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
hbb.src_fs = src_fs;
hbb.dst_fs = dst_fs;
hbb.incremental = incremental;
hbb.notify_func = notify_func;
hbb.notify_baton = notify_baton;
hbb.cancel_func = cancel_func;
hbb.cancel_baton = cancel_baton;
if (incremental)
SVN_ERR(svn_fs_fs__with_all_locks(dst_fs, hotcopy_body, &hbb, pool));
else
SVN_ERR(hotcopy_body(&hbb, pool));
return SVN_NO_ERROR;
}