summaryrefslogtreecommitdiff
path: root/subversion/libsvn_repos/dump.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_repos/dump.c')
-rw-r--r--subversion/libsvn_repos/dump.c386
1 files changed, 287 insertions, 99 deletions
diff --git a/subversion/libsvn_repos/dump.c b/subversion/libsvn_repos/dump.c
index ca23809..a64b180 100644
--- a/subversion/libsvn_repos/dump.c
+++ b/subversion/libsvn_repos/dump.c
@@ -34,6 +34,7 @@
#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"
@@ -71,7 +72,8 @@ store_delta(apr_file_t **tempfile, svn_filesize_t *len,
/* Compute the delta and send it to the temporary file. */
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_txdelta_to_svndiff3(&wh, &whb, temp_stream, 0,
+ SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool);
SVN_ERR(svn_txdelta_send_txstream(delta_stream, wh, whb, pool));
/* Get the length of the temporary file and rewind it. */
@@ -105,6 +107,9 @@ struct edit_baton
svn_fs_root_t *fs_root;
svn_revnum_t current_rev;
+ /* The fs, so we can grab historic information if needed. */
+ svn_fs_t *fs;
+
/* True if dumped nodes should output deltas instead of full text. */
svn_boolean_t use_deltas;
@@ -114,13 +119,13 @@ struct edit_baton
/* The first revision dumped in this dumpstream. */
svn_revnum_t oldest_dumped_rev;
- /* Set to true if any references to revisions older than
+ /* If not NULL, set to true if any references to revisions older than
OLDEST_DUMPED_REV were found in the dumpstream. */
- svn_boolean_t found_old_reference;
+ svn_boolean_t *found_old_reference;
- /* Set to true if any mergeinfo was dumped which contains revisions
- older than OLDEST_DUMPED_REV. */
- svn_boolean_t found_old_mergeinfo;
+ /* If not NULL, set to true if any mergeinfo was dumped which contains
+ revisions older than OLDEST_DUMPED_REV. */
+ svn_boolean_t *found_old_mergeinfo;
/* reusable buffer for writing file contents */
char buffer[SVN__STREAM_CHUNK_SIZE];
@@ -212,6 +217,49 @@ make_dir_baton(const char *path,
}
+/* If the mergeinfo in MERGEINFO_STR refers to any revisions older than
+ * OLDEST_DUMPED_REV, issue a warning and set *FOUND_OLD_MERGEINFO to TRUE,
+ * otherwise leave *FOUND_OLD_MERGEINFO unchanged.
+ */
+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;
+}
+
+
/* This helper is the main "meat" of the editor -- it does all the
work of writing a node record.
@@ -279,11 +327,11 @@ dump_node(struct edit_baton *eb,
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"));
+ SVN_ERR(svn_stream_puts(eb->stream,
+ 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"));
+ SVN_ERR(svn_stream_puts(eb->stream,
+ SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n"));
/* Remove leading slashes from copyfrom paths. */
if (cmp_path)
@@ -298,9 +346,8 @@ dump_node(struct edit_baton *eb,
if (action == svn_node_action_change)
{
- SVN_ERR(svn_stream_printf(eb->stream, pool,
- SVN_REPOS_DUMPFILE_NODE_ACTION
- ": change\n"));
+ SVN_ERR(svn_stream_puts(eb->stream,
+ SVN_REPOS_DUMPFILE_NODE_ACTION ": change\n"));
/* either the text or props changed, or possibly both. */
SVN_ERR(svn_fs_revision_root(&compare_root,
@@ -320,9 +367,9 @@ dump_node(struct edit_baton *eb,
if (! is_copy)
{
/* a simple delete+add, implied by a single 'replace' action. */
- SVN_ERR(svn_stream_printf(eb->stream, pool,
- SVN_REPOS_DUMPFILE_NODE_ACTION
- ": replace\n"));
+ SVN_ERR(svn_stream_puts(eb->stream,
+ SVN_REPOS_DUMPFILE_NODE_ACTION
+ ": replace\n"));
/* definitely need to dump all content for a replace. */
if (kind == svn_node_file)
@@ -335,9 +382,9 @@ dump_node(struct edit_baton *eb,
/* the path & kind headers have already been printed; just
add a delete action, and end the current record.*/
- SVN_ERR(svn_stream_printf(eb->stream, pool,
- SVN_REPOS_DUMPFILE_NODE_ACTION
- ": delete\n\n"));
+ SVN_ERR(svn_stream_puts(eb->stream,
+ SVN_REPOS_DUMPFILE_NODE_ACTION
+ ": delete\n\n"));
/* recurse: print an additional add-with-history record. */
SVN_ERR(dump_node(eb, path, kind, svn_node_action_add,
@@ -351,9 +398,8 @@ dump_node(struct edit_baton *eb,
}
else if (action == svn_node_action_delete)
{
- SVN_ERR(svn_stream_printf(eb->stream, pool,
- SVN_REPOS_DUMPFILE_NODE_ACTION
- ": delete\n"));
+ SVN_ERR(svn_stream_puts(eb->stream,
+ SVN_REPOS_DUMPFILE_NODE_ACTION ": delete\n"));
/* we can leave this routine quietly now, don't need to dump
any content. */
@@ -362,8 +408,8 @@ dump_node(struct edit_baton *eb,
}
else if (action == svn_node_action_add)
{
- SVN_ERR(svn_stream_printf(eb->stream, pool,
- SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n"));
+ SVN_ERR(svn_stream_puts(eb->stream,
+ SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n"));
if (! is_copy)
{
@@ -389,7 +435,8 @@ dump_node(struct edit_baton *eb,
" into an empty repository"
" will fail."),
cmp_rev, eb->oldest_dumped_rev);
- eb->found_old_reference = TRUE;
+ if (eb->found_old_reference)
+ *eb->found_old_reference = TRUE;
eb->notify_func(eb->notify_baton, notify, pool);
}
@@ -467,36 +514,17 @@ dump_node(struct edit_baton *eb,
dumped. */
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);
+ svn_string_t *mergeinfo_str = svn_hash_gets(prophash,
+ SVN_PROP_MERGEINFO);
if (mergeinfo_str)
{
- svn_mergeinfo_t mergeinfo, old_mergeinfo;
-
- SVN_ERR(svn_mergeinfo_parse(&mergeinfo, mergeinfo_str->data,
- pool));
- SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
- &old_mergeinfo, mergeinfo,
- eb->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."),
- eb->oldest_dumped_rev);
-
- eb->found_old_mergeinfo = TRUE;
- eb->notify_func(eb->notify_baton, notify, pool);
- }
+ /* An error in verifying the mergeinfo must not prevent dumping
+ the data. Ignore any such error. */
+ svn_error_clear(verify_mergeinfo_revisions(
+ eb->found_old_mergeinfo,
+ mergeinfo_str->data, eb->oldest_dumped_rev,
+ eb->notify_func, eb->notify_baton,
+ pool));
}
}
@@ -506,9 +534,8 @@ dump_node(struct edit_baton *eb,
saying that our property contents are a delta. */
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"));
+ SVN_ERR(svn_stream_puts(eb->stream,
+ SVN_REPOS_DUMPFILE_PROP_DELTA ": true\n"));
}
else
oldhash = apr_hash_make(pool);
@@ -539,9 +566,8 @@ dump_node(struct edit_baton *eb,
saying our text contents are a delta. */
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"));
+ SVN_ERR(svn_stream_puts(eb->stream,
+ SVN_REPOS_DUMPFILE_TEXT_DELTA ": true\n"));
if (compare_root)
{
@@ -652,7 +678,7 @@ delete_entry(const char *path,
const char *mypath = apr_pstrdup(pb->pool, path);
/* remember this path needs to be deleted. */
- apr_hash_set(pb->deleted_entries, mypath, APR_HASH_KEY_STRING, pb);
+ svn_hash_sets(pb->deleted_entries, mypath, pb);
return SVN_NO_ERROR;
}
@@ -674,7 +700,7 @@ add_directory(const char *path,
= make_dir_baton(path, copyfrom_path, copyfrom_rev, eb, pb, TRUE, pool);
/* This might be a replacement -- is the path already deleted? */
- val = apr_hash_get(pb->deleted_entries, path, APR_HASH_KEY_STRING);
+ val = svn_hash_gets(pb->deleted_entries, path);
/* Detect an add-with-history. */
is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
@@ -690,7 +716,7 @@ add_directory(const char *path,
if (val)
/* Delete the path, it's now been dumped. */
- apr_hash_set(pb->deleted_entries, path, APR_HASH_KEY_STRING, NULL);
+ svn_hash_sets(pb->deleted_entries, path, NULL);
new_db->written_out = TRUE;
@@ -733,17 +759,20 @@ close_directory(void *dir_baton,
{
struct dir_baton *db = dir_baton;
struct edit_baton *eb = db->edit_baton;
- apr_hash_index_t *hi;
apr_pool_t *subpool = svn_pool_create(pool);
-
- for (hi = apr_hash_first(pool, db->deleted_entries);
- hi;
- hi = apr_hash_next(hi))
+ int i;
+ apr_array_header_t *sorted_entries;
+
+ /* Sort entries lexically instead of as paths. Even though the entries
+ * are full paths they're all in the same directory (see comment in struct
+ * dir_baton definition). So we really want to sort by basename, in which
+ * case the lexical sort function is more efficient. */
+ sorted_entries = svn_sort__hash(db->deleted_entries,
+ svn_sort_compare_items_lexically, pool);
+ for (i = 0; i < sorted_entries->nelts; i++)
{
- const void *key;
- const char *path;
- apr_hash_this(hi, &key, NULL, NULL);
- path = key;
+ const char *path = APR_ARRAY_IDX(sorted_entries, i,
+ svn_sort__item_t).key;
svn_pool_clear(subpool);
@@ -774,7 +803,7 @@ add_file(const char *path,
svn_boolean_t is_copy = FALSE;
/* This might be a replacement -- is the path already deleted? */
- val = apr_hash_get(pb->deleted_entries, path, APR_HASH_KEY_STRING);
+ val = svn_hash_gets(pb->deleted_entries, path);
/* Detect add-with-history. */
is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
@@ -790,7 +819,7 @@ add_file(const char *path,
if (val)
/* delete the path, it's now been dumped. */
- apr_hash_set(pb->deleted_entries, path, APR_HASH_KEY_STRING, NULL);
+ svn_hash_sets(pb->deleted_entries, path, NULL);
*file_baton = NULL; /* muhahahaha */
return SVN_NO_ERROR;
@@ -849,6 +878,94 @@ change_dir_prop(void *parent_baton,
return SVN_NO_ERROR;
}
+static svn_error_t *
+fetch_props_func(apr_hash_t **props,
+ void *baton,
+ const char *path,
+ svn_revnum_t base_revision,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct edit_baton *eb = baton;
+ svn_error_t *err;
+ svn_fs_root_t *fs_root;
+
+ if (!SVN_IS_VALID_REVNUM(base_revision))
+ base_revision = eb->current_rev - 1;
+
+ SVN_ERR(svn_fs_revision_root(&fs_root, eb->fs, base_revision, scratch_pool));
+
+ err = svn_fs_node_proplist(props, fs_root, path, result_pool);
+ if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
+ {
+ svn_error_clear(err);
+ *props = apr_hash_make(result_pool);
+ return SVN_NO_ERROR;
+ }
+ else if (err)
+ return svn_error_trace(err);
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+fetch_kind_func(svn_node_kind_t *kind,
+ void *baton,
+ const char *path,
+ svn_revnum_t base_revision,
+ apr_pool_t *scratch_pool)
+{
+ struct edit_baton *eb = baton;
+ svn_fs_root_t *fs_root;
+
+ if (!SVN_IS_VALID_REVNUM(base_revision))
+ base_revision = eb->current_rev - 1;
+
+ SVN_ERR(svn_fs_revision_root(&fs_root, eb->fs, base_revision, scratch_pool));
+
+ SVN_ERR(svn_fs_check_path(kind, fs_root, path, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+fetch_base_func(const char **filename,
+ void *baton,
+ const char *path,
+ svn_revnum_t base_revision,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct edit_baton *eb = baton;
+ svn_stream_t *contents;
+ svn_stream_t *file_stream;
+ const char *tmp_filename;
+ svn_error_t *err;
+ svn_fs_root_t *fs_root;
+
+ if (!SVN_IS_VALID_REVNUM(base_revision))
+ base_revision = eb->current_rev - 1;
+
+ SVN_ERR(svn_fs_revision_root(&fs_root, eb->fs, base_revision, scratch_pool));
+
+ err = svn_fs_file_contents(&contents, fs_root, path, scratch_pool);
+ if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
+ {
+ svn_error_clear(err);
+ *filename = NULL;
+ return SVN_NO_ERROR;
+ }
+ else if (err)
+ return svn_error_trace(err);
+ SVN_ERR(svn_stream_open_unique(&file_stream, &tmp_filename, NULL,
+ svn_io_file_del_on_pool_cleanup,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_stream_copy3(contents, file_stream, NULL, NULL, scratch_pool));
+
+ *filename = apr_pstrdup(result_pool, tmp_filename);
+
+ return SVN_NO_ERROR;
+}
static svn_error_t *
@@ -858,6 +975,10 @@ get_dump_editor(const svn_delta_editor_t **editor,
svn_revnum_t to_rev,
const char *root_path,
svn_stream_t *stream,
+ svn_boolean_t *found_old_reference,
+ svn_boolean_t *found_old_mergeinfo,
+ svn_error_t *(*custom_close_directory)(void *dir_baton,
+ apr_pool_t *scratch_pool),
svn_repos_notify_func_t notify_func,
void *notify_baton,
svn_revnum_t oldest_dumped_rev,
@@ -870,6 +991,8 @@ get_dump_editor(const svn_delta_editor_t **editor,
root baton. */
struct edit_baton *eb = apr_pcalloc(pool, sizeof(*eb));
svn_delta_editor_t *dump_editor = svn_delta_default_editor(pool);
+ svn_delta_shim_callbacks_t *shim_callbacks =
+ svn_delta_shim_callbacks_default(pool);
/* Set up the edit baton. */
eb->stream = stream;
@@ -879,16 +1002,22 @@ get_dump_editor(const svn_delta_editor_t **editor,
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->fs = fs;
eb->current_rev = to_rev;
eb->use_deltas = use_deltas;
eb->verify = verify;
+ eb->found_old_reference = found_old_reference;
+ eb->found_old_mergeinfo = found_old_mergeinfo;
/* Set up the editor. */
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;
+ if (custom_close_directory)
+ dump_editor->close_directory = custom_close_directory;
+ else
+ 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;
@@ -896,6 +1025,14 @@ get_dump_editor(const svn_delta_editor_t **editor,
*edit_baton = eb;
*editor = dump_editor;
+ shim_callbacks->fetch_kind_func = fetch_kind_func;
+ shim_callbacks->fetch_props_func = fetch_props_func;
+ shim_callbacks->fetch_base_func = fetch_base_func;
+ shim_callbacks->fetch_baton = eb;
+
+ SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
+ NULL, NULL, shim_callbacks, pool, pool));
+
return SVN_NO_ERROR;
}
@@ -928,15 +1065,13 @@ write_revision_record(svn_stream_t *stream,
/* Run revision date properties through the time conversion to
canonicalize them. */
/* ### Remove this when it is no longer needed for sure. */
- datevalue = apr_hash_get(props, SVN_PROP_REVISION_DATE,
- APR_HASH_KEY_STRING);
+ datevalue = svn_hash_gets(props, SVN_PROP_REVISION_DATE);
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);
+ svn_hash_sets(props, SVN_PROP_REVISION_DATE, datevalue);
}
encoded_prophash = svn_stringbuf_create_ensure(0, pool);
@@ -1093,7 +1228,9 @@ svn_repos_dump_fs3(svn_repos_t *repos,
non-incremental dump. */
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,
+ "", stream, &found_old_reference,
+ &found_old_mergeinfo, NULL,
+ notify_func, notify_baton,
start_rev, use_deltas_for_rev, FALSE, subpool));
/* Drive the editor in one way or another. */
@@ -1122,6 +1259,10 @@ svn_repos_dump_fs3(svn_repos_t *repos,
SVN_ERR(svn_repos_replay2(to_root, "", SVN_INVALID_REVNUM, FALSE,
dump_editor, dump_edit_baton,
NULL, NULL, subpool));
+
+ /* While our editor close_edit implementation is a no-op, we still
+ do this for completeness. */
+ SVN_ERR(dump_editor->close_edit(dump_edit_baton, subpool));
}
loop_end:
@@ -1130,14 +1271,6 @@ svn_repos_dump_fs3(svn_repos_t *repos,
notify->revision = to_rev;
notify_func(notify_baton, notify, subpool);
}
-
- if (dump_edit_baton) /* We never get an edit baton for r0. */
- {
- 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)
@@ -1207,13 +1340,15 @@ verify_directory_entry(void *baton, const void *key, apr_ssize_t klen,
void *val, apr_pool_t *pool)
{
struct dir_baton *db = baton;
+ svn_fs_dirent_t *dirent = (svn_fs_dirent_t *)val;
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) {
+ /* since we can't access the directory entries directly by their ID,
+ we need to navigate from the FS_ROOT to them (relatively expensive
+ because we may start at a never rev than the last change to node). */
+ switch (dirent->kind) {
case svn_node_dir:
/* Getting this directory's contents is enough to ensure that our
link to it is correct. */
@@ -1226,7 +1361,8 @@ verify_directory_entry(void *baton, const void *key, apr_ssize_t klen,
break;
default:
return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
- _("Unexpected node kind %d for '%s'"), kind, path);
+ _("Unexpected node kind %d for '%s'"),
+ dirent->kind, path);
}
return SVN_NO_ERROR;
@@ -1245,6 +1381,32 @@ verify_close_directory(void *dir_baton,
return close_directory(dir_baton, pool);
}
+/* Baton type used for forwarding notifications from FS API to REPOS API. */
+struct verify_fs2_notify_func_baton_t
+{
+ /* notification function to call (must not be NULL) */
+ svn_repos_notify_func_t notify_func;
+
+ /* baton to use for it */
+ void *notify_baton;
+
+ /* type of notification to send (we will simply plug in the revision) */
+ svn_repos_notify_t *notify;
+};
+
+/* Forward the notification to BATON. */
+static void
+verify_fs2_notify_func(svn_revnum_t revision,
+ void *baton,
+ apr_pool_t *pool)
+{
+ struct verify_fs2_notify_func_baton_t *notify_baton = baton;
+
+ notify_baton->notify->revision = revision;
+ notify_baton->notify_func(notify_baton->notify_baton,
+ notify_baton->notify, pool);
+}
+
svn_error_t *
svn_repos_verify_fs2(svn_repos_t *repos,
svn_revnum_t start_rev,
@@ -1260,6 +1422,8 @@ svn_repos_verify_fs2(svn_repos_t *repos,
svn_revnum_t rev;
apr_pool_t *iterpool = svn_pool_create(pool);
svn_repos_notify_t *notify;
+ svn_fs_progress_notify_func_t verify_notify = NULL;
+ struct verify_fs2_notify_func_baton_t *verify_notify_baton = NULL;
/* Determine the current youngest revision of the filesystem. */
SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool));
@@ -1282,14 +1446,30 @@ svn_repos_verify_fs2(svn_repos_t *repos,
"(youngest revision is %ld)"),
end_rev, youngest);
- /* Create a notify object that we can reuse within the loop. */
+ /* Create a notify object that we can reuse within the loop and a
+ forwarding structure for notifications from inside svn_fs_verify(). */
if (notify_func)
- notify = svn_repos_notify_create(svn_repos_notify_verify_rev_end,
- pool);
+ {
+ notify = svn_repos_notify_create(svn_repos_notify_verify_rev_end,
+ pool);
+
+ verify_notify = verify_fs2_notify_func;
+ verify_notify_baton = apr_palloc(pool, sizeof(*verify_notify_baton));
+ verify_notify_baton->notify_func = notify_func;
+ verify_notify_baton->notify_baton = notify_baton;
+ verify_notify_baton->notify
+ = svn_repos_notify_create(svn_repos_notify_verify_rev_structure, pool);
+ }
+
+ /* Verify global metadata and backend-specific data first. */
+ SVN_ERR(svn_fs_verify(svn_fs_path(fs, pool), svn_fs_config(fs, pool),
+ start_rev, end_rev,
+ verify_notify, verify_notify_baton,
+ cancel_func, cancel_baton, pool));
for (rev = start_rev; rev <= end_rev; rev++)
{
- svn_delta_editor_t *dump_editor;
+ const svn_delta_editor_t *dump_editor;
void *dump_edit_baton;
const svn_delta_editor_t *cancel_editor;
void *cancel_edit_baton;
@@ -1299,14 +1479,15 @@ svn_repos_verify_fs2(svn_repos_t *repos,
svn_pool_clear(iterpool);
/* Get cancellable dump editor, but with our close_directory handler. */
- SVN_ERR(get_dump_editor((const svn_delta_editor_t **)&dump_editor,
- &dump_edit_baton, fs, rev, "",
- svn_stream_empty(pool),
+ SVN_ERR(get_dump_editor(&dump_editor, &dump_edit_baton,
+ fs, rev, "",
+ svn_stream_empty(iterpool),
+ NULL, NULL,
+ verify_close_directory,
notify_func, notify_baton,
start_rev,
FALSE, TRUE, /* use_deltas, verify */
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,
@@ -1314,9 +1495,15 @@ svn_repos_verify_fs2(svn_repos_t *repos,
iterpool));
SVN_ERR(svn_fs_revision_root(&to_root, fs, rev, iterpool));
+ SVN_ERR(svn_fs_verify_root(to_root, iterpool));
+
SVN_ERR(svn_repos_replay2(to_root, "", SVN_INVALID_REVNUM, FALSE,
cancel_editor, cancel_edit_baton,
NULL, NULL, iterpool));
+ /* While our editor close_edit implementation is a no-op, we still
+ do this for completeness. */
+ SVN_ERR(cancel_editor->close_edit(cancel_edit_baton, iterpool));
+
SVN_ERR(svn_fs_revision_proplist(&props, fs, rev, iterpool));
if (notify_func)
@@ -1333,6 +1520,7 @@ svn_repos_verify_fs2(svn_repos_t *repos,
notify_func(notify_baton, notify, iterpool);
}
+ /* Per-backend verification. */
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;