#include "svn_private_config.h"
#include "svn_pools.h"
#include "svn_error.h"
#include "svn_fs.h"
#include "svn_hash.h"
#include "svn_iter.h"
#include "svn_repos.h"
#include "svn_string.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_time.h"
#include "svn_checksum.h"
#include "svn_props.h"
#include "svn_sorts.h"
#include "private/svn_mergeinfo_private.h"
#include "private/svn_fs_private.h"
#define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))
static svn_error_t *
store_delta(apr_file_t **tempfile, svn_filesize_t *len,
svn_fs_root_t *oldroot, const char *oldpath,
svn_fs_root_t *newroot, const char *newpath, apr_pool_t *pool)
{
svn_stream_t *temp_stream;
apr_off_t offset = 0;
svn_txdelta_stream_t *delta_stream;
svn_txdelta_window_handler_t wh;
void *whb;
SVN_ERR(svn_io_open_unique_file3(tempfile, NULL, NULL,
svn_io_file_del_on_pool_cleanup,
pool, pool));
temp_stream = svn_stream_from_aprfile2(*tempfile, TRUE, pool);
SVN_ERR(svn_fs_get_file_delta_stream(&delta_stream, oldroot, oldpath,
newroot, newpath, pool));
svn_txdelta_to_svndiff2(&wh, &whb, temp_stream, 0, pool);
SVN_ERR(svn_txdelta_send_txstream(delta_stream, wh, whb, pool));
SVN_ERR(svn_io_file_seek(*tempfile, APR_CUR, &offset, pool));
*len = offset;
offset = 0;
return svn_io_file_seek(*tempfile, APR_SET, &offset, pool);
}
struct edit_baton
{
const char *path;
svn_stream_t *stream;
svn_repos_notify_func_t notify_func;
void *notify_baton;
svn_fs_root_t *fs_root;
svn_revnum_t current_rev;
svn_boolean_t use_deltas;
svn_boolean_t verify;
svn_revnum_t oldest_dumped_rev;
svn_boolean_t found_old_reference;
svn_boolean_t found_old_mergeinfo;
char buffer[SVN__STREAM_CHUNK_SIZE];
apr_size_t bufsize;
};
struct dir_baton
{
struct edit_baton *edit_baton;
struct dir_baton *parent_dir_baton;
svn_boolean_t added;
svn_boolean_t written_out;
const char *path;
const char *cmp_path;
svn_revnum_t cmp_rev;
apr_hash_t *deleted_entries;
apr_pool_t *pool;
};
static struct dir_baton *
make_dir_baton(const char *path,
const char *cmp_path,
svn_revnum_t cmp_rev,
void *edit_baton,
void *parent_dir_baton,
svn_boolean_t added,
apr_pool_t *pool)
{
struct edit_baton *eb = edit_baton;
struct dir_baton *pb = parent_dir_baton;
struct dir_baton *new_db = apr_pcalloc(pool, sizeof(*new_db));
const char *full_path;
SVN_ERR_ASSERT_NO_RETURN(!path || pb);
if (pb)
full_path = svn_relpath_join(eb->path, path, pool);
else
full_path = apr_pstrdup(pool, eb->path);
if (cmp_path)
cmp_path = svn_relpath_canonicalize(cmp_path, pool);
new_db->edit_baton = eb;
new_db->parent_dir_baton = pb;
new_db->path = full_path;
new_db->cmp_path = cmp_path;
new_db->cmp_rev = cmp_rev;
new_db->added = added;
new_db->written_out = FALSE;
new_db->deleted_entries = apr_hash_make(pool);
new_db->pool = pool;
return new_db;
}
static svn_error_t *
verify_mergeinfo_revisions(svn_boolean_t *found_old_mergeinfo,
const char *mergeinfo_str,
svn_revnum_t oldest_dumped_rev,
svn_repos_notify_func_t notify_func,
void *notify_baton,
apr_pool_t *pool)
{
svn_mergeinfo_t mergeinfo, old_mergeinfo;
SVN_ERR(svn_mergeinfo_parse(&mergeinfo, mergeinfo_str, pool));
SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
&old_mergeinfo, mergeinfo,
oldest_dumped_rev - 1, 0,
TRUE, pool, pool));
if (apr_hash_count(old_mergeinfo))
{
svn_repos_notify_t *notify =
svn_repos_notify_create(svn_repos_notify_warning, pool);
notify->warning = svn_repos_notify_warning_found_old_mergeinfo;
notify->warning_str = apr_psprintf(
pool,
_("Mergeinfo referencing revision(s) prior "
"to the oldest dumped revision (r%ld). "
"Loading this dump may result in invalid "
"mergeinfo."),
oldest_dumped_rev);
if (found_old_mergeinfo)
*found_old_mergeinfo = TRUE;
notify_func(notify_baton, notify, pool);
}
return SVN_NO_ERROR;
}
static svn_error_t *
dump_node(struct edit_baton *eb,
const char *path,
svn_node_kind_t kind,
enum svn_node_action action,
svn_boolean_t is_copy,
const char *cmp_path,
svn_revnum_t cmp_rev,
apr_pool_t *pool)
{
svn_stringbuf_t *propstring;
svn_filesize_t content_length = 0;
apr_size_t len;
svn_boolean_t must_dump_text = FALSE, must_dump_props = FALSE;
const char *compare_path = path;
svn_revnum_t compare_rev = eb->current_rev - 1;
svn_fs_root_t *compare_root = NULL;
apr_file_t *delta_file = NULL;
if (eb->verify || eb->notify_func)
{
svn_error_t *err = svn_fs__path_valid(path, pool);
if (err)
{
if (eb->notify_func)
{
char errbuf[512];
svn_repos_notify_t *notify;
notify = svn_repos_notify_create(svn_repos_notify_warning, pool);
notify->warning = svn_repos_notify_warning_invalid_fspath;
notify->warning_str = apr_psprintf(
pool,
_("E%06d: While validating fspath '%s': %s"),
err->apr_err, path,
svn_err_best_message(err, errbuf, sizeof(errbuf)));
eb->notify_func(eb->notify_baton, notify, pool);
}
if (eb->verify)
return svn_error_trace(err);
else
svn_error_clear(err);
}
}
SVN_ERR(svn_stream_printf(eb->stream, pool,
SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n",
path));
if (kind == svn_node_file)
SVN_ERR(svn_stream_printf(eb->stream, pool,
SVN_REPOS_DUMPFILE_NODE_KIND ": file\n"));
else if (kind == svn_node_dir)
SVN_ERR(svn_stream_printf(eb->stream, pool,
SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n"));
if (cmp_path)
cmp_path = svn_relpath_canonicalize(cmp_path, pool);
if (ARE_VALID_COPY_ARGS(cmp_path, cmp_rev))
{
compare_path = cmp_path;
compare_rev = cmp_rev;
}
if (action == svn_node_action_change)
{
SVN_ERR(svn_stream_printf(eb->stream, pool,
SVN_REPOS_DUMPFILE_NODE_ACTION
": change\n"));
SVN_ERR(svn_fs_revision_root(&compare_root,
svn_fs_root_fs(eb->fs_root),
compare_rev, pool));
SVN_ERR(svn_fs_props_changed(&must_dump_props,
compare_root, compare_path,
eb->fs_root, path, pool));
if (kind == svn_node_file)
SVN_ERR(svn_fs_contents_changed(&must_dump_text,
compare_root, compare_path,
eb->fs_root, path, pool));
}
else if (action == svn_node_action_replace)
{
if (! is_copy)
{
SVN_ERR(svn_stream_printf(eb->stream, pool,
SVN_REPOS_DUMPFILE_NODE_ACTION
": replace\n"));
if (kind == svn_node_file)
must_dump_text = TRUE;
must_dump_props = TRUE;
}
else
{
SVN_ERR(svn_stream_printf(eb->stream, pool,
SVN_REPOS_DUMPFILE_NODE_ACTION
": delete\n\n"));
SVN_ERR(dump_node(eb, path, kind, svn_node_action_add,
is_copy, compare_path, compare_rev, pool));
must_dump_text = FALSE;
must_dump_props = FALSE;
}
}
else if (action == svn_node_action_delete)
{
SVN_ERR(svn_stream_printf(eb->stream, pool,
SVN_REPOS_DUMPFILE_NODE_ACTION
": delete\n"));
must_dump_text = FALSE;
must_dump_props = FALSE;
}
else if (action == svn_node_action_add)
{
SVN_ERR(svn_stream_printf(eb->stream, pool,
SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n"));
if (! is_copy)
{
if (kind == svn_node_file)
must_dump_text = TRUE;
must_dump_props = TRUE;
}
else
{
if (!eb->verify && cmp_rev < eb->oldest_dumped_rev
&& eb->notify_func)
{
svn_repos_notify_t *notify =
svn_repos_notify_create(svn_repos_notify_warning, pool);
notify->warning = svn_repos_notify_warning_found_old_reference;
notify->warning_str = apr_psprintf(
pool,
_("Referencing data in revision %ld,"
" which is older than the oldest"
" dumped revision (r%ld). Loading this dump"
" into an empty repository"
" will fail."),
cmp_rev, eb->oldest_dumped_rev);
eb->found_old_reference = TRUE;
eb->notify_func(eb->notify_baton, notify, pool);
}
SVN_ERR(svn_stream_printf(eb->stream, pool,
SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV
": %ld\n"
SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH
": %s\n",
cmp_rev, cmp_path));
SVN_ERR(svn_fs_revision_root(&compare_root,
svn_fs_root_fs(eb->fs_root),
compare_rev, pool));
SVN_ERR(svn_fs_props_changed(&must_dump_props,
compare_root, compare_path,
eb->fs_root, path, pool));
if (kind == svn_node_file)
{
svn_checksum_t *checksum;
const char *hex_digest;
SVN_ERR(svn_fs_contents_changed(&must_dump_text,
compare_root, compare_path,
eb->fs_root, path, pool));
SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5,
compare_root, compare_path,
FALSE, pool));
hex_digest = svn_checksum_to_cstring(checksum, pool);
if (hex_digest)
SVN_ERR(svn_stream_printf(eb->stream, pool,
SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_MD5
": %s\n", hex_digest));
SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_sha1,
compare_root, compare_path,
FALSE, pool));
hex_digest = svn_checksum_to_cstring(checksum, pool);
if (hex_digest)
SVN_ERR(svn_stream_printf(eb->stream, pool,
SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_SHA1
": %s\n", hex_digest));
}
}
}
if ((! must_dump_text) && (! must_dump_props))
{
len = 2;
return svn_stream_write(eb->stream, "\n\n", &len);
}
if (must_dump_props)
{
apr_hash_t *prophash, *oldhash = NULL;
apr_size_t proplen;
svn_stream_t *propstream;
SVN_ERR(svn_fs_node_proplist(&prophash, eb->fs_root, path, pool));
if (!eb->verify && eb->notify_func && eb->oldest_dumped_rev > 1)
{
svn_string_t *mergeinfo_str = apr_hash_get(prophash,
SVN_PROP_MERGEINFO,
APR_HASH_KEY_STRING);
if (mergeinfo_str)
{
svn_error_clear(verify_mergeinfo_revisions(
&eb->found_old_mergeinfo,
mergeinfo_str->data, eb->oldest_dumped_rev,
eb->notify_func, eb->notify_baton,
pool));
}
}
if (eb->use_deltas && compare_root)
{
SVN_ERR(svn_fs_node_proplist(&oldhash, compare_root, compare_path,
pool));
SVN_ERR(svn_stream_printf(eb->stream, pool,
SVN_REPOS_DUMPFILE_PROP_DELTA
": true\n"));
}
else
oldhash = apr_hash_make(pool);
propstring = svn_stringbuf_create_ensure(0, pool);
propstream = svn_stream_from_stringbuf(propstring, pool);
SVN_ERR(svn_hash_write_incremental(prophash, oldhash, propstream,
"PROPS-END", pool));
SVN_ERR(svn_stream_close(propstream));
proplen = propstring->len;
content_length += proplen;
SVN_ERR(svn_stream_printf(eb->stream, pool,
SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
": %" APR_SIZE_T_FMT "\n", proplen));
}
if (must_dump_text && (kind == svn_node_file))
{
svn_checksum_t *checksum;
const char *hex_digest;
svn_filesize_t textlen;
if (eb->use_deltas)
{
SVN_ERR(store_delta(&delta_file, &textlen, compare_root,
compare_path, eb->fs_root, path, pool));
SVN_ERR(svn_stream_printf(eb->stream, pool,
SVN_REPOS_DUMPFILE_TEXT_DELTA
": true\n"));
if (compare_root)
{
SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5,
compare_root, compare_path,
FALSE, pool));
hex_digest = svn_checksum_to_cstring(checksum, pool);
if (hex_digest)
SVN_ERR(svn_stream_printf(eb->stream, pool,
SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5
": %s\n", hex_digest));
SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_sha1,
compare_root, compare_path,
FALSE, pool));
hex_digest = svn_checksum_to_cstring(checksum, pool);
if (hex_digest)
SVN_ERR(svn_stream_printf(eb->stream, pool,
SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_SHA1
": %s\n", hex_digest));
}
}
else
{
SVN_ERR(svn_fs_file_length(&textlen, eb->fs_root, path, pool));
}
content_length += textlen;
SVN_ERR(svn_stream_printf(eb->stream, pool,
SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH
": %" SVN_FILESIZE_T_FMT "\n", textlen));
SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5,
eb->fs_root, path, FALSE, pool));
hex_digest = svn_checksum_to_cstring(checksum, pool);
if (hex_digest)
SVN_ERR(svn_stream_printf(eb->stream, pool,
SVN_REPOS_DUMPFILE_TEXT_CONTENT_MD5
": %s\n", hex_digest));
SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_sha1,
eb->fs_root, path, FALSE, pool));
hex_digest = svn_checksum_to_cstring(checksum, pool);
if (hex_digest)
SVN_ERR(svn_stream_printf(eb->stream, pool,
SVN_REPOS_DUMPFILE_TEXT_CONTENT_SHA1
": %s\n", hex_digest));
}
SVN_ERR(svn_stream_printf(eb->stream, pool,
SVN_REPOS_DUMPFILE_CONTENT_LENGTH
": %" SVN_FILESIZE_T_FMT "\n\n",
content_length));
if (must_dump_props)
{
len = propstring->len;
SVN_ERR(svn_stream_write(eb->stream, propstring->data, &len));
}
if (must_dump_text && (kind == svn_node_file))
{
svn_stream_t *contents;
if (delta_file)
{
contents = svn_stream_from_aprfile2(delta_file, FALSE, pool);
}
else
SVN_ERR(svn_fs_file_contents(&contents, eb->fs_root, path, pool));
SVN_ERR(svn_stream_copy3(contents, svn_stream_disown(eb->stream, pool),
NULL, NULL, pool));
}
len = 2;
return svn_stream_write(eb->stream, "\n\n", &len);
}
static svn_error_t *
open_root(void *edit_baton,
svn_revnum_t base_revision,
apr_pool_t *pool,
void **root_baton)
{
*root_baton = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM,
edit_baton, NULL, FALSE, pool);
return SVN_NO_ERROR;
}
static svn_error_t *
delete_entry(const char *path,
svn_revnum_t revision,
void *parent_baton,
apr_pool_t *pool)
{
struct dir_baton *pb = parent_baton;
const char *mypath = apr_pstrdup(pb->pool, path);
apr_hash_set(pb->deleted_entries, mypath, APR_HASH_KEY_STRING, pb);
return SVN_NO_ERROR;
}
static svn_error_t *
add_directory(const char *path,
void *parent_baton,
const char *copyfrom_path,
svn_revnum_t copyfrom_rev,
apr_pool_t *pool,
void **child_baton)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
void *val;
svn_boolean_t is_copy = FALSE;
struct dir_baton *new_db
= make_dir_baton(path, copyfrom_path, copyfrom_rev, eb, pb, TRUE, pool);
val = apr_hash_get(pb->deleted_entries, path, APR_HASH_KEY_STRING);
is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
SVN_ERR(dump_node(eb, path,
svn_node_dir,
val ? svn_node_action_replace : svn_node_action_add,
is_copy,
is_copy ? copyfrom_path : NULL,
is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
pool));
if (val)
apr_hash_set(pb->deleted_entries, path, APR_HASH_KEY_STRING, NULL);
new_db->written_out = TRUE;
*child_baton = new_db;
return SVN_NO_ERROR;
}
static svn_error_t *
open_directory(const char *path,
void *parent_baton,
svn_revnum_t base_revision,
apr_pool_t *pool,
void **child_baton)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
struct dir_baton *new_db;
const char *cmp_path = NULL;
svn_revnum_t cmp_rev = SVN_INVALID_REVNUM;
if (ARE_VALID_COPY_ARGS(pb->cmp_path, pb->cmp_rev))
{
cmp_path = svn_relpath_join(pb->cmp_path,
svn_relpath_basename(path, pool), pool);
cmp_rev = pb->cmp_rev;
}
new_db = make_dir_baton(path, cmp_path, cmp_rev, eb, pb, FALSE, pool);
*child_baton = new_db;
return SVN_NO_ERROR;
}
static svn_error_t *
close_directory(void *dir_baton,
apr_pool_t *pool)
{
struct dir_baton *db = dir_baton;
struct edit_baton *eb = db->edit_baton;
apr_pool_t *subpool = svn_pool_create(pool);
unsigned int i;
apr_array_header_t *sorted_entries;
sorted_entries = svn_sort__hash(db->deleted_entries,
svn_sort_compare_items_lexically, pool);
for (i = 0; i < sorted_entries->nelts; i++)
{
const char *path = APR_ARRAY_IDX(sorted_entries, i,
svn_sort__item_t).key;
svn_pool_clear(subpool);
SVN_ERR(dump_node(eb, path,
svn_node_unknown, svn_node_action_delete,
FALSE, NULL, SVN_INVALID_REVNUM, subpool));
}
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
static svn_error_t *
add_file(const char *path,
void *parent_baton,
const char *copyfrom_path,
svn_revnum_t copyfrom_rev,
apr_pool_t *pool,
void **file_baton)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
void *val;
svn_boolean_t is_copy = FALSE;
val = apr_hash_get(pb->deleted_entries, path, APR_HASH_KEY_STRING);
is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
SVN_ERR(dump_node(eb, path,
svn_node_file,
val ? svn_node_action_replace : svn_node_action_add,
is_copy,
is_copy ? copyfrom_path : NULL,
is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
pool));
if (val)
apr_hash_set(pb->deleted_entries, path, APR_HASH_KEY_STRING, NULL);
*file_baton = NULL;
return SVN_NO_ERROR;
}
static svn_error_t *
open_file(const char *path,
void *parent_baton,
svn_revnum_t ancestor_revision,
apr_pool_t *pool,
void **file_baton)
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
const char *cmp_path = NULL;
svn_revnum_t cmp_rev = SVN_INVALID_REVNUM;
if (ARE_VALID_COPY_ARGS(pb->cmp_path, pb->cmp_rev))
{
cmp_path = svn_relpath_join(pb->cmp_path,
svn_relpath_basename(path, pool), pool);
cmp_rev = pb->cmp_rev;
}
SVN_ERR(dump_node(eb, path,
svn_node_file, svn_node_action_change,
FALSE, cmp_path, cmp_rev, pool));
*file_baton = NULL;
return SVN_NO_ERROR;
}
static svn_error_t *
change_dir_prop(void *parent_baton,
const char *name,
const svn_string_t *value,
apr_pool_t *pool)
{
struct dir_baton *db = parent_baton;
struct edit_baton *eb = db->edit_baton;
if (! db->written_out)
{
SVN_ERR(dump_node(eb, db->path,
svn_node_dir, svn_node_action_change,
FALSE, db->cmp_path, db->cmp_rev, pool));
db->written_out = TRUE;
}
return SVN_NO_ERROR;
}
static svn_error_t *
get_dump_editor(const svn_delta_editor_t **editor,
void **edit_baton,
svn_fs_t *fs,
svn_revnum_t to_rev,
const char *root_path,
svn_stream_t *stream,
svn_repos_notify_func_t notify_func,
void *notify_baton,
svn_revnum_t oldest_dumped_rev,
svn_boolean_t use_deltas,
svn_boolean_t verify,
apr_pool_t *pool)
{
struct edit_baton *eb = apr_pcalloc(pool, sizeof(*eb));
svn_delta_editor_t *dump_editor = svn_delta_default_editor(pool);
eb->stream = stream;
eb->notify_func = notify_func;
eb->notify_baton = notify_baton;
eb->oldest_dumped_rev = oldest_dumped_rev;
eb->bufsize = sizeof(eb->buffer);
eb->path = apr_pstrdup(pool, root_path);
SVN_ERR(svn_fs_revision_root(&(eb->fs_root), fs, to_rev, pool));
eb->current_rev = to_rev;
eb->use_deltas = use_deltas;
eb->verify = verify;
dump_editor->open_root = open_root;
dump_editor->delete_entry = delete_entry;
dump_editor->add_directory = add_directory;
dump_editor->open_directory = open_directory;
dump_editor->close_directory = close_directory;
dump_editor->change_dir_prop = change_dir_prop;
dump_editor->add_file = add_file;
dump_editor->open_file = open_file;
*edit_baton = eb;
*editor = dump_editor;
return SVN_NO_ERROR;
}
static svn_error_t *
write_revision_record(svn_stream_t *stream,
svn_fs_t *fs,
svn_revnum_t rev,
apr_pool_t *pool)
{
apr_size_t len;
apr_hash_t *props;
svn_stringbuf_t *encoded_prophash;
apr_time_t timetemp;
svn_string_t *datevalue;
svn_stream_t *propstream;
SVN_ERR(svn_fs_revision_proplist(&props, fs, rev, pool));
datevalue = apr_hash_get(props, SVN_PROP_REVISION_DATE,
APR_HASH_KEY_STRING);
if (datevalue)
{
SVN_ERR(svn_time_from_cstring(&timetemp, datevalue->data, pool));
datevalue = svn_string_create(svn_time_to_cstring(timetemp, pool),
pool);
apr_hash_set(props, SVN_PROP_REVISION_DATE, APR_HASH_KEY_STRING,
datevalue);
}
encoded_prophash = svn_stringbuf_create_ensure(0, pool);
propstream = svn_stream_from_stringbuf(encoded_prophash, pool);
SVN_ERR(svn_hash_write2(props, propstream, "PROPS-END", pool));
SVN_ERR(svn_stream_close(propstream));
SVN_ERR(svn_stream_printf(stream, pool,
SVN_REPOS_DUMPFILE_REVISION_NUMBER
": %ld\n", rev));
SVN_ERR(svn_stream_printf(stream, pool,
SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
": %" APR_SIZE_T_FMT "\n",
encoded_prophash->len));
SVN_ERR(svn_stream_printf(stream, pool,
SVN_REPOS_DUMPFILE_CONTENT_LENGTH
": %" APR_SIZE_T_FMT "\n\n",
encoded_prophash->len));
len = encoded_prophash->len;
SVN_ERR(svn_stream_write(stream, encoded_prophash->data, &len));
len = 1;
return svn_stream_write(stream, "\n", &len);
}
svn_error_t *
svn_repos_dump_fs3(svn_repos_t *repos,
svn_stream_t *stream,
svn_revnum_t start_rev,
svn_revnum_t end_rev,
svn_boolean_t incremental,
svn_boolean_t use_deltas,
svn_repos_notify_func_t notify_func,
void *notify_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
const svn_delta_editor_t *dump_editor;
void *dump_edit_baton = NULL;
svn_revnum_t i;
svn_fs_t *fs = svn_repos_fs(repos);
apr_pool_t *subpool = svn_pool_create(pool);
svn_revnum_t youngest;
const char *uuid;
int version;
svn_boolean_t found_old_reference = FALSE;
svn_boolean_t found_old_mergeinfo = FALSE;
svn_repos_notify_t *notify;
SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool));
if (! SVN_IS_VALID_REVNUM(start_rev))
start_rev = 0;
if (! SVN_IS_VALID_REVNUM(end_rev))
end_rev = youngest;
if (! stream)
stream = svn_stream_empty(pool);
if (start_rev > end_rev)
return svn_error_createf(SVN_ERR_REPOS_BAD_ARGS, NULL,
_("Start revision %ld"
" is greater than end revision %ld"),
start_rev, end_rev);
if (end_rev > youngest)
return svn_error_createf(SVN_ERR_REPOS_BAD_ARGS, NULL,
_("End revision %ld is invalid "
"(youngest revision is %ld)"),
end_rev, youngest);
if ((start_rev == 0) && incremental)
incremental = FALSE;
SVN_ERR(svn_fs_get_uuid(fs, &uuid, pool));
version = SVN_REPOS_DUMPFILE_FORMAT_VERSION;
if (!use_deltas)
version--;
SVN_ERR(svn_stream_printf(stream, pool,
SVN_REPOS_DUMPFILE_MAGIC_HEADER ": %d\n\n",
version));
SVN_ERR(svn_stream_printf(stream, pool, SVN_REPOS_DUMPFILE_UUID
": %s\n\n", uuid));
if (notify_func)
notify = svn_repos_notify_create(svn_repos_notify_dump_rev_end,
pool);
for (i = start_rev; i <= end_rev; i++)
{
svn_revnum_t from_rev, to_rev;
svn_fs_root_t *to_root;
svn_boolean_t use_deltas_for_rev;
svn_pool_clear(subpool);
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
if ((i == start_rev) && (! incremental))
{
if (i == 0)
{
SVN_ERR(write_revision_record(stream, fs, 0, subpool));
to_rev = 0;
goto loop_end;
}
from_rev = 0;
to_rev = i;
}
else
{
from_rev = i - 1;
to_rev = i;
}
SVN_ERR(write_revision_record(stream, fs, to_rev, subpool));
use_deltas_for_rev = use_deltas && (incremental || i != start_rev);
SVN_ERR(get_dump_editor(&dump_editor, &dump_edit_baton, fs, to_rev,
"", stream, notify_func, notify_baton,
start_rev, use_deltas_for_rev, FALSE, subpool));
SVN_ERR(svn_fs_revision_root(&to_root, fs, to_rev, subpool));
if ((i == start_rev) && (! incremental))
{
svn_fs_root_t *from_root;
SVN_ERR(svn_fs_revision_root(&from_root, fs, from_rev, subpool));
SVN_ERR(svn_repos_dir_delta2(from_root, "", "",
to_root, "",
dump_editor, dump_edit_baton,
NULL,
NULL,
FALSE,
svn_depth_infinity,
FALSE,
FALSE,
subpool));
}
else
{
SVN_ERR(svn_repos_replay2(to_root, "", SVN_INVALID_REVNUM, FALSE,
dump_editor, dump_edit_baton,
NULL, NULL, subpool));
}
loop_end:
if (notify_func)
{
notify->revision = to_rev;
notify_func(notify_baton, notify, subpool);
}
if (dump_edit_baton)
{
if (((struct edit_baton *)dump_edit_baton)->found_old_reference)
found_old_reference = TRUE;
if (((struct edit_baton *)dump_edit_baton)->found_old_mergeinfo)
found_old_mergeinfo = TRUE;
}
}
if (notify_func)
{
notify = svn_repos_notify_create(svn_repos_notify_dump_end, subpool);
notify_func(notify_baton, notify, subpool);
if (found_old_reference)
{
notify = svn_repos_notify_create(svn_repos_notify_warning, subpool);
notify->warning = svn_repos_notify_warning_found_old_reference;
notify->warning_str = _("The range of revisions dumped "
"contained references to "
"copy sources outside that "
"range.");
notify_func(notify_baton, notify, subpool);
}
if (found_old_mergeinfo)
{
notify = svn_repos_notify_create(svn_repos_notify_warning, subpool);
notify->warning = svn_repos_notify_warning_found_old_mergeinfo;
notify->warning_str = _("The range of revisions dumped "
"contained mergeinfo "
"which reference revisions outside "
"that range.");
notify_func(notify_baton, notify, subpool);
}
}
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
static svn_error_t *
verify_directory_entry(void *baton, const void *key, apr_ssize_t klen,
void *val, apr_pool_t *pool)
{
struct dir_baton *db = baton;
char *path = svn_relpath_join(db->path, (const char *)key, pool);
svn_node_kind_t kind;
apr_hash_t *dirents;
svn_filesize_t len;
SVN_ERR(svn_fs_check_path(&kind, db->edit_baton->fs_root, path, pool));
switch (kind) {
case svn_node_dir:
SVN_ERR(svn_fs_dir_entries(&dirents, db->edit_baton->fs_root, path, pool));
break;
case svn_node_file:
SVN_ERR(svn_fs_file_length(&len, db->edit_baton->fs_root, path, pool));
break;
default:
return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
_("Unexpected node kind %d for '%s'"), kind, path);
}
return SVN_NO_ERROR;
}
static svn_error_t *
verify_close_directory(void *dir_baton,
apr_pool_t *pool)
{
struct dir_baton *db = dir_baton;
apr_hash_t *dirents;
SVN_ERR(svn_fs_dir_entries(&dirents, db->edit_baton->fs_root,
db->path, pool));
SVN_ERR(svn_iter_apr_hash(NULL, dirents, verify_directory_entry,
dir_baton, pool));
return close_directory(dir_baton, pool);
}
svn_error_t *
svn_repos_verify_fs2(svn_repos_t *repos,
svn_revnum_t start_rev,
svn_revnum_t end_rev,
svn_repos_notify_func_t notify_func,
void *notify_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
svn_fs_t *fs = svn_repos_fs(repos);
svn_revnum_t youngest;
svn_revnum_t rev;
apr_pool_t *iterpool = svn_pool_create(pool);
svn_repos_notify_t *notify;
SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool));
if (! SVN_IS_VALID_REVNUM(start_rev))
start_rev = 0;
if (! SVN_IS_VALID_REVNUM(end_rev))
end_rev = youngest;
if (start_rev > end_rev)
return svn_error_createf(SVN_ERR_REPOS_BAD_ARGS, NULL,
_("Start revision %ld"
" is greater than end revision %ld"),
start_rev, end_rev);
if (end_rev > youngest)
return svn_error_createf(SVN_ERR_REPOS_BAD_ARGS, NULL,
_("End revision %ld is invalid "
"(youngest revision is %ld)"),
end_rev, youngest);
if (notify_func)
notify = svn_repos_notify_create(svn_repos_notify_verify_rev_end,
pool);
for (rev = start_rev; rev <= end_rev; rev++)
{
svn_delta_editor_t *dump_editor;
void *dump_edit_baton;
const svn_delta_editor_t *cancel_editor;
void *cancel_edit_baton;
svn_fs_root_t *to_root;
apr_hash_t *props;
svn_pool_clear(iterpool);
SVN_ERR(get_dump_editor((const svn_delta_editor_t **)&dump_editor,
&dump_edit_baton, fs, rev, "",
svn_stream_empty(pool),
notify_func, notify_baton,
start_rev,
FALSE, TRUE,
iterpool));
dump_editor->close_directory = verify_close_directory;
SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, cancel_baton,
dump_editor, dump_edit_baton,
&cancel_editor,
&cancel_edit_baton,
iterpool));
SVN_ERR(svn_fs_revision_root(&to_root, fs, rev, iterpool));
SVN_ERR(svn_repos_replay2(to_root, "", SVN_INVALID_REVNUM, FALSE,
cancel_editor, cancel_edit_baton,
NULL, NULL, iterpool));
SVN_ERR(svn_fs_revision_proplist(&props, fs, rev, iterpool));
if (notify_func)
{
notify->revision = rev;
notify_func(notify_baton, notify, iterpool);
}
}
if (notify_func)
{
notify = svn_repos_notify_create(svn_repos_notify_verify_end, iterpool);
notify_func(notify_baton, notify, iterpool);
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}