#include <stdarg.h>
#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_repos_private.h"
#include "private/svn_mergeinfo_private.h"
#include "private/svn_fs_private.h"
#include "private/svn_sorts_private.h"
#include "private/svn_utf_private.h"
#include "private/svn_cache.h"
#define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))
typedef struct path_tracker_entry_t
{
svn_stringbuf_t *path;
svn_stringbuf_t *copyfrom_path;
svn_revnum_t copyfrom_rev;
svn_boolean_t exists;
} path_tracker_entry_t;
typedef struct path_tracker_t
{
apr_array_header_t *stack;
int depth;
svn_revnum_t revision;
apr_pool_t *pool;
} path_tracker_t;
static path_tracker_t *
tracker_create(svn_revnum_t revision,
apr_pool_t *pool)
{
path_tracker_t *result = apr_pcalloc(pool, sizeof(*result));
result->stack = apr_array_make(pool, 16, sizeof(path_tracker_entry_t));
result->revision = revision;
result->pool = pool;
return result;
}
static void
tracker_trim(path_tracker_t *tracker,
const char *path,
svn_boolean_t allow_exact_match)
{
for (; tracker->depth; --tracker->depth)
{
path_tracker_entry_t *parent = &APR_ARRAY_IDX(tracker->stack,
tracker->depth - 1,
path_tracker_entry_t);
const char *rel_path
= svn_dirent_skip_ancestor(parent->path->data, path);
if (rel_path && (allow_exact_match || *rel_path != '\0'))
break;
}
}
static void
tracker_lookup(const char **orig_path,
svn_revnum_t *orig_rev,
path_tracker_t *tracker,
const char *path,
apr_pool_t *pool)
{
tracker_trim(tracker, path, TRUE);
if (tracker->depth == 0)
{
*orig_path = path;
*orig_rev = tracker->revision - 1;
}
else
{
path_tracker_entry_t *parent = &APR_ARRAY_IDX(tracker->stack,
tracker->depth - 1,
path_tracker_entry_t);
if (parent->exists)
{
const char *rel_path
= svn_dirent_skip_ancestor(parent->path->data, path);
if (parent->copyfrom_rev != SVN_INVALID_REVNUM)
{
*orig_path = svn_dirent_join(parent->copyfrom_path->data,
rel_path, pool);
*orig_rev = parent->copyfrom_rev;
}
else if (*rel_path == '\0')
{
*orig_path = path;
*orig_rev = tracker->revision;
}
else
{
*orig_path = NULL;
*orig_rev = SVN_INVALID_REVNUM;
}
}
else
{
*orig_path = NULL;
*orig_rev = SVN_INVALID_REVNUM;
}
}
}
static path_tracker_entry_t *
tracker_add_entry(path_tracker_t *tracker,
const char *path)
{
path_tracker_entry_t *entry;
tracker_trim(tracker, path, FALSE);
if (tracker->depth == tracker->stack->nelts)
{
entry = apr_array_push(tracker->stack);
entry->path = svn_stringbuf_create_empty(tracker->pool);
entry->copyfrom_path = svn_stringbuf_create_empty(tracker->pool);
}
else
{
entry = &APR_ARRAY_IDX(tracker->stack, tracker->depth,
path_tracker_entry_t);
}
svn_stringbuf_set(entry->path, path);
++tracker->depth;
return entry;
}
static void
tracker_path_copy(path_tracker_t *tracker,
const char *path,
const char *copyfrom_path,
svn_revnum_t copyfrom_rev)
{
path_tracker_entry_t *entry = tracker_add_entry(tracker, path);
svn_stringbuf_set(entry->copyfrom_path, copyfrom_path);
entry->copyfrom_rev = copyfrom_rev;
entry->exists = TRUE;
}
static void
tracker_path_add(path_tracker_t *tracker,
const char *path)
{
path_tracker_entry_t *entry = tracker_add_entry(tracker, path);
svn_stringbuf_setempty(entry->copyfrom_path);
entry->copyfrom_rev = SVN_INVALID_REVNUM;
entry->exists = TRUE;
}
static void
tracker_path_replace(path_tracker_t *tracker,
const char *path)
{
tracker_path_add(tracker, path);
}
static void
tracker_path_delete(path_tracker_t *tracker,
const char *path)
{
path_tracker_entry_t *entry = tracker_add_entry(tracker, path);
svn_stringbuf_setempty(entry->copyfrom_path);
entry->copyfrom_rev = SVN_INVALID_REVNUM;
entry->exists = FALSE;
}
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_svndiff3(&wh, &whb, temp_stream, 0,
SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, 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);
}
__attribute__((format(printf, 5, 6)))
static void
notify_warning(apr_pool_t *scratch_pool,
svn_repos_notify_func_t notify_func,
void *notify_baton,
svn_repos_notify_warning_t warning,
const char *warning_fmt,
...)
{
va_list va;
svn_repos_notify_t *notify;
if (notify_func == NULL)
return;
notify = svn_repos_notify_create(svn_repos_notify_warning, scratch_pool);
notify->warning = warning;
va_start(va, warning_fmt);
notify->warning_str = apr_pvsprintf(scratch_pool, warning_fmt, va);
va_end(va);
notify_func(notify_baton, notify, scratch_pool);
}
static svn_error_t *
write_header(svn_stream_t *stream,
apr_hash_t *headers,
const char *key,
apr_pool_t *scratch_pool)
{
const char *val = svn_hash_gets(headers, key);
if (val)
{
SVN_ERR(svn_stream_printf(stream, scratch_pool,
"%s: %s\n", key, val));
}
return SVN_NO_ERROR;
}
static svn_error_t *
write_revision_headers(svn_stream_t *stream,
apr_hash_t *headers,
apr_pool_t *scratch_pool)
{
const char **h;
apr_hash_index_t *hi;
static const char *revision_headers_order[] =
{
SVN_REPOS_DUMPFILE_REVISION_NUMBER,
NULL
};
for (h = revision_headers_order; *h; h++)
{
SVN_ERR(write_header(stream, headers, *h, scratch_pool));
svn_hash_sets(headers, *h, NULL);
}
for (hi = apr_hash_first(scratch_pool, headers); hi; hi = apr_hash_next(hi))
{
const char *key = apr_hash_this_key(hi);
if (strcmp(key, SVN_REPOS_DUMPFILE_CONTENT_LENGTH) != 0)
SVN_ERR(write_header(stream, headers, key, scratch_pool));
}
SVN_ERR(write_header(stream, headers, SVN_REPOS_DUMPFILE_CONTENT_LENGTH,
scratch_pool));
return SVN_NO_ERROR;
}
typedef struct svn_repos__dumpfile_header_entry_t {
const char *key, *val;
} svn_repos__dumpfile_header_entry_t;
svn_repos__dumpfile_headers_t *
svn_repos__dumpfile_headers_create(apr_pool_t *pool)
{
svn_repos__dumpfile_headers_t *headers
= apr_array_make(pool, 5, sizeof(svn_repos__dumpfile_header_entry_t));
return headers;
}
void
svn_repos__dumpfile_header_push(svn_repos__dumpfile_headers_t *headers,
const char *key,
const char *val)
{
svn_repos__dumpfile_header_entry_t *h
= &APR_ARRAY_PUSH(headers, svn_repos__dumpfile_header_entry_t);
h->key = apr_pstrdup(headers->pool, key);
h->val = apr_pstrdup(headers->pool, val);
}
void
svn_repos__dumpfile_header_pushf(svn_repos__dumpfile_headers_t *headers,
const char *key,
const char *val_fmt,
...)
{
va_list ap;
svn_repos__dumpfile_header_entry_t *h
= &APR_ARRAY_PUSH(headers, svn_repos__dumpfile_header_entry_t);
h->key = apr_pstrdup(headers->pool, key);
va_start(ap, val_fmt);
h->val = apr_pvsprintf(headers->pool, val_fmt, ap);
va_end(ap);
}
svn_error_t *
svn_repos__dump_headers(svn_stream_t *stream,
svn_repos__dumpfile_headers_t *headers,
apr_pool_t *scratch_pool)
{
int i;
for (i = 0; i < headers->nelts; i++)
{
svn_repos__dumpfile_header_entry_t *h
= &APR_ARRAY_IDX(headers, i, svn_repos__dumpfile_header_entry_t);
SVN_ERR(svn_stream_printf(stream, scratch_pool,
"%s: %s\n", h->key, h->val));
}
SVN_ERR(svn_stream_puts(stream, "\n"));
return SVN_NO_ERROR;
}
svn_error_t *
svn_repos__dump_revision_record(svn_stream_t *dump_stream,
svn_revnum_t revision,
apr_hash_t *extra_headers,
apr_hash_t *revprops,
svn_boolean_t props_section_always,
apr_pool_t *scratch_pool)
{
svn_stringbuf_t *propstring = NULL;
apr_hash_t *headers;
if (extra_headers)
headers = apr_hash_copy(scratch_pool, extra_headers);
else
headers = apr_hash_make(scratch_pool);
svn_hash_sets(headers, SVN_REPOS_DUMPFILE_REVISION_NUMBER,
apr_psprintf(scratch_pool, "%ld", revision));
if (apr_hash_count(revprops) || props_section_always)
{
svn_stream_t *propstream;
propstring = svn_stringbuf_create_empty(scratch_pool);
propstream = svn_stream_from_stringbuf(propstring, scratch_pool);
SVN_ERR(svn_hash_write2(revprops, propstream, "PROPS-END", scratch_pool));
SVN_ERR(svn_stream_close(propstream));
svn_hash_sets(headers, SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH,
apr_psprintf(scratch_pool,
"%" APR_SIZE_T_FMT, propstring->len));
}
if (propstring)
{
svn_hash_sets(headers, SVN_REPOS_DUMPFILE_CONTENT_LENGTH,
apr_psprintf(scratch_pool,
"%" APR_SIZE_T_FMT, propstring->len));
}
SVN_ERR(write_revision_headers(dump_stream, headers, scratch_pool));
SVN_ERR(svn_stream_puts(dump_stream, "\n"));
if (propstring)
{
SVN_ERR(svn_stream_write(dump_stream, propstring->data, &propstring->len));
}
SVN_ERR(svn_stream_puts(dump_stream, "\n"));
return SVN_NO_ERROR;
}
svn_error_t *
svn_repos__dump_node_record(svn_stream_t *dump_stream,
svn_repos__dumpfile_headers_t *headers,
svn_stringbuf_t *props_str,
svn_boolean_t has_text,
svn_filesize_t text_content_length,
svn_boolean_t content_length_always,
apr_pool_t *scratch_pool)
{
svn_filesize_t content_length = 0;
if (props_str)
{
svn_repos__dumpfile_header_pushf(
headers, SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH,
"%" APR_SIZE_T_FMT, props_str->len);
content_length += props_str->len;
}
if (has_text)
{
svn_repos__dumpfile_header_pushf(
headers, SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH,
"%" SVN_FILESIZE_T_FMT, text_content_length);
content_length += text_content_length;
}
if (content_length_always || props_str || has_text)
{
svn_repos__dumpfile_header_pushf(
headers, SVN_REPOS_DUMPFILE_CONTENT_LENGTH,
"%" SVN_FILESIZE_T_FMT, content_length);
}
SVN_ERR(svn_repos__dump_headers(dump_stream, headers, scratch_pool));
if (props_str)
{
SVN_ERR(svn_stream_write(dump_stream, props_str->data, &props_str->len));
}
return SVN_NO_ERROR;
}
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_fs_t *fs;
svn_boolean_t use_deltas;
svn_boolean_t verify;
svn_boolean_t check_normalization;
svn_revnum_t oldest_dumped_rev;
svn_boolean_t *found_old_reference;
svn_boolean_t *found_old_mergeinfo;
path_tracker_t *path_tracker;
};
struct dir_baton
{
struct edit_baton *edit_baton;
svn_boolean_t written_out;
const char *path;
const char *cmp_path;
svn_revnum_t cmp_rev;
apr_hash_t *deleted_entries;
svn_boolean_t check_name_collision;
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,
struct dir_baton *pb,
apr_pool_t *pool)
{
struct edit_baton *eb = edit_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->path = full_path;
new_db->cmp_path = cmp_path;
new_db->cmp_rev = cmp_rev;
new_db->written_out = FALSE;
new_db->deleted_entries = apr_hash_make(pool);
new_db->check_name_collision = FALSE;
new_db->pool = pool;
return new_db;
}
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);
static svn_error_t *
node_must_exist(struct edit_baton *eb,
const char *path,
svn_revnum_t revision,
svn_node_kind_t expected_kind,
apr_pool_t *scratch_pool)
{
svn_node_kind_t kind = svn_node_none;
if (eb->path_tracker == NULL)
return SVN_NO_ERROR;
if (revision == eb->path_tracker->revision)
tracker_lookup(&path, &revision, eb->path_tracker, path, scratch_pool);
if (path)
SVN_ERR(fetch_kind_func(&kind, eb, path, revision, scratch_pool));
if (kind == svn_node_none)
return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
_("Path '%s' not found in r%ld."),
path, revision);
if (expected_kind != kind && expected_kind != svn_node_unknown)
return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
_("Unexpected node kind %d for '%s' at r%ld. "
"Expected kind was %d."),
kind, path, revision, expected_kind);
return SVN_NO_ERROR;
}
static svn_error_t *
node_must_not_exist(struct edit_baton *eb,
const char *path,
svn_revnum_t revision,
apr_pool_t *scratch_pool)
{
svn_node_kind_t kind = svn_node_none;
if (eb->path_tracker == NULL)
return SVN_NO_ERROR;
if (revision == eb->path_tracker->revision)
tracker_lookup(&path, &revision, eb->path_tracker, path, scratch_pool);
if (path)
SVN_ERR(fetch_kind_func(&kind, eb, path, revision, scratch_pool));
if (kind != svn_node_none)
return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
_("Path '%s' exists in r%ld."),
path, revision);
return SVN_NO_ERROR;
}
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))
{
notify_warning(pool, notify_func, notify_baton,
svn_repos_notify_warning_found_old_mergeinfo,
_("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;
}
return SVN_NO_ERROR;
}
static const char normalized_unique[] = "normalized_unique";
static const char normalized_collision[] = "normalized_collision";
struct extract_mergeinfo_paths_baton
{
apr_hash_t *result;
svn_boolean_t normalize;
svn_membuf_t buffer;
};
static svn_error_t *
extract_mergeinfo_paths(void *baton, const void *key, apr_ssize_t klen,
void *val, apr_pool_t *iterpool)
{
struct extract_mergeinfo_paths_baton *const xb = baton;
if (xb->normalize)
{
const char *normkey;
SVN_ERR(svn_utf__normalize(&normkey, key, klen, &xb->buffer));
svn_hash_sets(xb->result,
apr_pstrdup(xb->buffer.pool, normkey),
normalized_unique);
}
else
apr_hash_set(xb->result,
apr_pmemdup(xb->buffer.pool, key, klen + 1), klen,
normalized_unique);
return SVN_NO_ERROR;
}
struct filter_mergeinfo_paths_baton
{
apr_hash_t *paths;
};
static svn_error_t *
filter_mergeinfo_paths(void *baton, const void *key, apr_ssize_t klen,
void *val, apr_pool_t *iterpool)
{
struct filter_mergeinfo_paths_baton *const fb = baton;
if (apr_hash_get(fb->paths, key, klen))
apr_hash_set(fb->paths, key, klen, NULL);
return SVN_NO_ERROR;
}
struct verify_mergeinfo_normalization_baton
{
const char* path;
apr_hash_t *normalized_paths;
svn_membuf_t buffer;
svn_repos_notify_func_t notify_func;
void *notify_baton;
};
static svn_error_t *
verify_mergeinfo_normalization(void *baton, const void *key, apr_ssize_t klen,
void *val, apr_pool_t *iterpool)
{
struct verify_mergeinfo_normalization_baton *const vb = baton;
const char *const path = key;
const char *normpath;
const char *found;
SVN_ERR(svn_utf__normalize(&normpath, path, klen, &vb->buffer));
found = svn_hash_gets(vb->normalized_paths, normpath);
if (!found)
svn_hash_sets(vb->normalized_paths,
apr_pstrdup(vb->buffer.pool, normpath),
normalized_unique);
else if (found == normalized_collision)
;
else
{
svn_hash_sets(vb->normalized_paths,
apr_pstrdup(vb->buffer.pool, normpath),
normalized_collision);
notify_warning(iterpool, vb->notify_func, vb->notify_baton,
svn_repos_notify_warning_mergeinfo_collision,
_("Duplicate representation of path '%s'"
" in %s property of '%s'"),
normpath, SVN_PROP_MERGEINFO, vb->path);
}
return SVN_NO_ERROR;
}
static svn_error_t *
check_mergeinfo_normalization(const char *path,
const char *new_mergeinfo,
const char *old_mergeinfo,
svn_repos_notify_func_t notify_func,
void *notify_baton,
apr_pool_t *pool)
{
svn_mergeinfo_t mergeinfo;
apr_hash_t *normalized_paths;
apr_hash_t *added_paths;
struct extract_mergeinfo_paths_baton extract_baton;
struct verify_mergeinfo_normalization_baton verify_baton;
SVN_ERR(svn_mergeinfo_parse(&mergeinfo, new_mergeinfo, pool));
extract_baton.result = apr_hash_make(pool);
extract_baton.normalize = FALSE;
svn_membuf__create(&extract_baton.buffer, 0, pool);
SVN_ERR(svn_iter_apr_hash(NULL, mergeinfo,
extract_mergeinfo_paths,
&extract_baton, pool));
added_paths = extract_baton.result;
if (old_mergeinfo)
{
struct filter_mergeinfo_paths_baton filter_baton;
svn_mergeinfo_t oldinfo;
extract_baton.result = apr_hash_make(pool);
extract_baton.normalize = TRUE;
SVN_ERR(svn_mergeinfo_parse(&oldinfo, old_mergeinfo, pool));
SVN_ERR(svn_iter_apr_hash(NULL, oldinfo,
extract_mergeinfo_paths,
&extract_baton, pool));
normalized_paths = extract_baton.result;
filter_baton.paths = added_paths;
SVN_ERR(svn_iter_apr_hash(NULL, oldinfo,
filter_mergeinfo_paths,
&filter_baton, pool));
}
else
normalized_paths = apr_hash_make(pool);
verify_baton.path = path;
verify_baton.normalized_paths = normalized_paths;
verify_baton.buffer = extract_baton.buffer;
verify_baton.notify_func = notify_func;
verify_baton.notify_baton = notify_baton;
SVN_ERR(svn_iter_apr_hash(NULL, added_paths,
verify_mergeinfo_normalization,
&verify_baton, pool));
return SVN_NO_ERROR;
}
static svn_error_t *
dump_node_delete(svn_stream_t *stream,
const char *node_relpath,
apr_pool_t *pool)
{
svn_repos__dumpfile_headers_t *headers
= svn_repos__dumpfile_headers_create(pool);
svn_repos__dumpfile_header_push(
headers, SVN_REPOS_DUMPFILE_NODE_PATH, node_relpath);
svn_repos__dumpfile_header_push(
headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "delete");
SVN_ERR(svn_repos__dump_headers(stream, headers, 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;
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;
svn_repos__dumpfile_headers_t *headers
= svn_repos__dumpfile_headers_create(pool);
svn_filesize_t textlen;
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];
notify_warning(pool, eb->notify_func, eb->notify_baton,
svn_repos_notify_warning_invalid_fspath,
_("E%06d: While validating fspath '%s': %s"),
err->apr_err, path,
svn_err_best_message(err, errbuf, sizeof(errbuf)));
}
if (eb->verify)
return svn_error_trace(err);
else
svn_error_clear(err);
}
}
svn_repos__dumpfile_header_push(
headers, SVN_REPOS_DUMPFILE_NODE_PATH, path);
if (kind == svn_node_file)
svn_repos__dumpfile_header_push(
headers, SVN_REPOS_DUMPFILE_NODE_KIND, "file");
else if (kind == svn_node_dir)
svn_repos__dumpfile_header_push(
headers, SVN_REPOS_DUMPFILE_NODE_KIND, "dir");
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;
}
switch (action)
{
case svn_node_action_change:
if (eb->path_tracker)
SVN_ERR_W(node_must_exist(eb, path, eb->current_rev, kind, pool),
apr_psprintf(pool, _("Change invalid path '%s' in r%ld"),
path, eb->current_rev));
svn_repos__dumpfile_header_push(
headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "change");
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));
break;
case svn_node_action_delete:
if (eb->path_tracker)
{
SVN_ERR_W(node_must_exist(eb, path, eb->current_rev, kind, pool),
apr_psprintf(pool, _("Deleting invalid path '%s' in r%ld"),
path, eb->current_rev));
tracker_path_delete(eb->path_tracker, path);
}
svn_repos__dumpfile_header_push(
headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "delete");
must_dump_text = FALSE;
must_dump_props = FALSE;
break;
case svn_node_action_replace:
if (eb->path_tracker)
SVN_ERR_W(node_must_exist(eb, path, eb->current_rev,
svn_node_unknown, pool),
apr_psprintf(pool,
_("Replacing non-existent path '%s' in r%ld"),
path, eb->current_rev));
if (! is_copy)
{
if (eb->path_tracker)
tracker_path_replace(eb->path_tracker, path);
svn_repos__dumpfile_header_push(
headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "replace");
if (kind == svn_node_file)
must_dump_text = TRUE;
must_dump_props = TRUE;
break;
}
else
{
if (eb->path_tracker)
{
tracker_path_delete(eb->path_tracker, path);
}
SVN_ERR(dump_node_delete(eb->stream, path, pool));
}
case svn_node_action_add:
if (eb->path_tracker)
SVN_ERR_W(node_must_not_exist(eb, path, eb->current_rev, pool),
apr_psprintf(pool,
_("Adding already existing path '%s' in r%ld"),
path, eb->current_rev));
svn_repos__dumpfile_header_push(
headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "add");
if (! is_copy)
{
if (eb->path_tracker)
tracker_path_add(eb->path_tracker, path);
if (kind == svn_node_file)
must_dump_text = TRUE;
must_dump_props = TRUE;
}
else
{
if (eb->path_tracker)
{
SVN_ERR_W(node_must_exist(eb, compare_path, compare_rev,
kind, pool),
apr_psprintf(pool,
_("Copying from invalid path to "
"'%s' in r%ld"),
path, eb->current_rev));
tracker_path_copy(eb->path_tracker, path, compare_path,
compare_rev);
}
if (!eb->verify && cmp_rev < eb->oldest_dumped_rev
&& eb->notify_func)
{
notify_warning(pool, eb->notify_func, eb->notify_baton,
svn_repos_notify_warning_found_old_reference,
_("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);
if (eb->found_old_reference)
*eb->found_old_reference = TRUE;
}
svn_repos__dumpfile_header_pushf(
headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV, "%ld", cmp_rev);
svn_repos__dumpfile_header_push(
headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH, 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_repos__dumpfile_header_push(
headers, SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_MD5, 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_repos__dumpfile_header_push(
headers, SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_SHA1, hex_digest);
}
}
break;
}
if ((! must_dump_text) && (! must_dump_props))
{
SVN_ERR(svn_repos__dump_headers(eb->stream, headers, pool));
len = 1;
return svn_stream_write(eb->stream, "\n", &len);
}
if (must_dump_props)
{
apr_hash_t *prophash, *oldhash = NULL;
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 = svn_hash_gets(prophash,
SVN_PROP_MERGEINFO);
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->verify && eb->check_normalization && eb->notify_func)
{
svn_string_t *mergeinfo_str = svn_hash_gets(prophash,
SVN_PROP_MERGEINFO);
if (mergeinfo_str)
{
svn_string_t *oldinfo_str = NULL;
if (compare_root)
{
SVN_ERR(svn_fs_node_proplist(&oldhash,
compare_root, compare_path,
pool));
oldinfo_str = svn_hash_gets(oldhash, SVN_PROP_MERGEINFO);
}
SVN_ERR(check_mergeinfo_normalization(
path, mergeinfo_str->data,
(oldinfo_str ? oldinfo_str->data : NULL),
eb->notify_func, eb->notify_baton, pool));
}
}
if (eb->use_deltas && compare_root)
{
if (!oldhash)
SVN_ERR(svn_fs_node_proplist(&oldhash, compare_root, compare_path,
pool));
svn_repos__dumpfile_header_push(
headers, SVN_REPOS_DUMPFILE_PROP_DELTA, "true");
}
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));
}
if (must_dump_text && (kind == svn_node_file))
{
svn_checksum_t *checksum;
const char *hex_digest;
if (eb->use_deltas)
{
SVN_ERR(store_delta(&delta_file, &textlen, compare_root,
compare_path, eb->fs_root, path, pool));
svn_repos__dumpfile_header_push(
headers, SVN_REPOS_DUMPFILE_TEXT_DELTA, "true");
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_repos__dumpfile_header_push(
headers, SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5, 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_repos__dumpfile_header_push(
headers, SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_SHA1, hex_digest);
}
}
else
{
SVN_ERR(svn_fs_file_length(&textlen, eb->fs_root, path, pool));
}
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_repos__dumpfile_header_push(
headers, SVN_REPOS_DUMPFILE_TEXT_CONTENT_MD5, 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_repos__dumpfile_header_push(
headers, SVN_REPOS_DUMPFILE_TEXT_CONTENT_SHA1, hex_digest);
}
SVN_ERR(svn_repos__dump_node_record(eb->stream, headers,
must_dump_props ? propstring : NULL,
must_dump_text,
must_dump_text ? textlen : 0,
TRUE ,
pool));
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, 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);
svn_hash_sets(pb->deleted_entries, mypath, 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 *was_deleted;
svn_boolean_t is_copy = FALSE;
struct dir_baton *new_db
= make_dir_baton(path, copyfrom_path, copyfrom_rev, eb, pb, pool);
was_deleted = svn_hash_gets(pb->deleted_entries, path);
is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
SVN_ERR(dump_node(eb, path,
svn_node_dir,
was_deleted ? svn_node_action_replace : svn_node_action_add,
is_copy,
is_copy ? copyfrom_path : NULL,
is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
pool));
if (was_deleted)
svn_hash_sets(pb->deleted_entries, path, NULL);
if (!was_deleted && eb->verify && eb->check_normalization && eb->notify_func)
{
pb->check_name_collision = TRUE;
}
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, 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);
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 *was_deleted;
svn_boolean_t is_copy = FALSE;
was_deleted = svn_hash_gets(pb->deleted_entries, path);
is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
SVN_ERR(dump_node(eb, path,
svn_node_file,
was_deleted ? svn_node_action_replace : svn_node_action_add,
is_copy,
is_copy ? copyfrom_path : NULL,
is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
pool));
if (was_deleted)
svn_hash_sets(pb->deleted_entries, path, NULL);
if (!was_deleted && eb->verify && eb->check_normalization && eb->notify_func)
{
pb->check_name_collision = TRUE;
}
*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 *
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 *
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_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,
svn_boolean_t use_deltas,
svn_boolean_t verify,
svn_boolean_t check_normalization,
apr_pool_t *pool)
{
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);
eb->stream = stream;
eb->notify_func = notify_func;
eb->notify_baton = notify_baton;
eb->oldest_dumped_rev = oldest_dumped_rev;
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->check_normalization = check_normalization;
eb->found_old_reference = found_old_reference;
eb->found_old_mergeinfo = found_old_mergeinfo;
eb->path_tracker = verify ? tracker_create(to_rev, pool) : NULL;
dump_editor->open_root = open_root;
dump_editor->delete_entry = delete_entry;
dump_editor->add_directory = add_directory;
dump_editor->open_directory = open_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;
*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;
}
static svn_error_t *
write_revision_record(svn_stream_t *stream,
svn_fs_t *fs,
svn_revnum_t rev,
apr_pool_t *pool)
{
apr_hash_t *props;
apr_time_t timetemp;
svn_string_t *datevalue;
SVN_ERR(svn_fs_revision_proplist(&props, fs, rev, pool));
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);
svn_hash_sets(props, SVN_PROP_REVISION_DATE, datevalue);
}
SVN_ERR(svn_repos__dump_revision_record(stream, rev, NULL, props,
TRUE ,
pool));
return SVN_NO_ERROR;
}
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 rev;
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);
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 (rev = start_rev; rev <= end_rev; 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));
SVN_ERR(write_revision_record(stream, fs, rev, subpool));
if (rev == 0)
goto loop_end;
use_deltas_for_rev = use_deltas && (incremental || rev != start_rev);
SVN_ERR(get_dump_editor(&dump_editor, &dump_edit_baton, fs, rev,
"", stream, &found_old_reference,
&found_old_mergeinfo, NULL,
notify_func, notify_baton,
start_rev, use_deltas_for_rev, FALSE, FALSE,
subpool));
SVN_ERR(svn_fs_revision_root(&to_root, fs, rev, subpool));
if ((rev == start_rev) && (! incremental))
{
svn_fs_root_t *from_root;
SVN_ERR(svn_fs_revision_root(&from_root, fs, 0, 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));
SVN_ERR(dump_editor->close_edit(dump_edit_baton, subpool));
}
loop_end:
if (notify_func)
{
notify->revision = rev;
notify_func(notify_baton, notify, subpool);
}
}
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_warning(subpool, notify_func, notify_baton,
svn_repos_notify_warning_found_old_reference,
_("The range of revisions dumped "
"contained references to "
"copy sources outside that "
"range."));
}
if (found_old_mergeinfo)
{
notify_warning(subpool, notify_func, notify_baton,
svn_repos_notify_warning_found_old_mergeinfo,
_("The range of revisions dumped "
"contained mergeinfo "
"which reference revisions outside "
"that range."));
}
}
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;
svn_fs_dirent_t *dirent = (svn_fs_dirent_t *)val;
char *path;
svn_boolean_t right_kind;
path = svn_relpath_join(db->path, (const char *)key, pool);
switch (dirent->kind) {
case svn_node_dir:
SVN_ERR(svn_fs_is_dir(&right_kind, db->edit_baton->fs_root, path, pool));
if (!right_kind)
return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
_("Node '%s' is not a directory."),
path);
break;
case svn_node_file:
SVN_ERR(svn_fs_is_file(&right_kind, db->edit_baton->fs_root, path, pool));
if (!right_kind)
return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
_("Node '%s' is not a file."),
path);
break;
default:
return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
_("Unexpected node kind %d for '%s'"),
dirent->kind, path);
}
return SVN_NO_ERROR;
}
struct check_name_collision_baton
{
struct dir_baton *dir_baton;
apr_hash_t *normalized;
svn_membuf_t buffer;
};
static svn_error_t *
check_name_collision(void *baton, const void *key, apr_ssize_t klen,
void *val, apr_pool_t *iterpool)
{
struct check_name_collision_baton *const cb = baton;
const char *name;
const char *found;
SVN_ERR(svn_utf__normalize(&name, key, klen, &cb->buffer));
found = svn_hash_gets(cb->normalized, name);
if (!found)
svn_hash_sets(cb->normalized, apr_pstrdup(cb->buffer.pool, name),
normalized_unique);
else if (found == normalized_collision)
;
else
{
struct dir_baton *const db = cb->dir_baton;
struct edit_baton *const eb = db->edit_baton;
const char* normpath;
svn_hash_sets(cb->normalized, apr_pstrdup(cb->buffer.pool, name),
normalized_collision);
SVN_ERR(svn_utf__normalize(
&normpath, svn_relpath_join(db->path, name, iterpool),
SVN_UTF__UNKNOWN_LENGTH, &cb->buffer));
notify_warning(iterpool, eb->notify_func, eb->notify_baton,
svn_repos_notify_warning_name_collision,
_("Duplicate representation of path '%s'"), normpath);
}
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));
if (db->check_name_collision)
{
struct check_name_collision_baton check_baton;
check_baton.dir_baton = db;
check_baton.normalized = apr_hash_make(pool);
svn_membuf__create(&check_baton.buffer, 0, pool);
SVN_ERR(svn_iter_apr_hash(NULL, dirents, check_name_collision,
&check_baton, pool));
}
return close_directory(dir_baton, pool);
}
static svn_error_t *
verify_one_revision(svn_fs_t *fs,
svn_revnum_t rev,
svn_repos_notify_func_t notify_func,
void *notify_baton,
svn_revnum_t start_rev,
svn_boolean_t check_normalization,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *scratch_pool)
{
const svn_delta_editor_t *dump_editor;
void *dump_edit_baton;
svn_fs_root_t *to_root;
apr_hash_t *props;
const svn_delta_editor_t *cancel_editor;
void *cancel_edit_baton;
SVN_ERR(get_dump_editor(&dump_editor, &dump_edit_baton,
fs, rev, "",
svn_stream_empty(scratch_pool),
NULL, NULL,
verify_close_directory,
notify_func, notify_baton,
start_rev,
FALSE, TRUE,
check_normalization,
scratch_pool));
SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, cancel_baton,
dump_editor, dump_edit_baton,
&cancel_editor,
&cancel_edit_baton,
scratch_pool));
SVN_ERR(svn_fs_revision_root(&to_root, fs, rev, scratch_pool));
SVN_ERR(svn_fs_verify_root(to_root, scratch_pool));
SVN_ERR(svn_repos_replay2(to_root, "", SVN_INVALID_REVNUM, FALSE,
cancel_editor, cancel_edit_baton,
NULL, NULL, scratch_pool));
SVN_ERR(cancel_editor->close_edit(cancel_edit_baton, scratch_pool));
SVN_ERR(svn_fs_revision_proplist(&props, fs, rev, scratch_pool));
return SVN_NO_ERROR;
}
struct verify_fs_notify_func_baton_t
{
svn_repos_notify_func_t notify_func;
void *notify_baton;
svn_repos_notify_t *notify;
};
static void
verify_fs_notify_func(svn_revnum_t revision,
void *baton,
apr_pool_t *pool)
{
struct verify_fs_notify_func_baton_t *notify_baton = baton;
notify_baton->notify->revision = revision;
notify_baton->notify_func(notify_baton->notify_baton,
notify_baton->notify, pool);
}
static svn_error_t *
report_error(svn_revnum_t revision,
svn_error_t *verify_err,
svn_repos_verify_callback_t verify_callback,
void *verify_baton,
apr_pool_t *pool)
{
if (verify_callback)
{
svn_error_t *cb_err;
cb_err = verify_callback(verify_baton, revision, verify_err, pool);
svn_error_clear(verify_err);
SVN_ERR(cb_err);
return SVN_NO_ERROR;
}
else
{
return svn_error_trace(verify_err);
}
}
svn_error_t *
svn_repos_verify_fs3(svn_repos_t *repos,
svn_revnum_t start_rev,
svn_revnum_t end_rev,
svn_boolean_t check_normalization,
svn_boolean_t metadata_only,
svn_repos_notify_func_t notify_func,
void *notify_baton,
svn_repos_verify_callback_t verify_callback,
void *verify_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_fs_progress_notify_func_t verify_notify = NULL;
struct verify_fs_notify_func_baton_t *verify_notify_baton = NULL;
svn_error_t *err;
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);
verify_notify = verify_fs_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);
}
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);
if (err && err->apr_err == SVN_ERR_CANCELLED)
{
return svn_error_trace(err);
}
else if (err)
{
SVN_ERR(report_error(SVN_INVALID_REVNUM, err, verify_callback,
verify_baton, iterpool));
}
if (!metadata_only)
for (rev = start_rev; rev <= end_rev; rev++)
{
svn_pool_clear(iterpool);
err = verify_one_revision(fs, rev, notify_func, notify_baton,
start_rev, check_normalization,
cancel_func, cancel_baton,
iterpool);
if (err && err->apr_err == SVN_ERR_CANCELLED)
{
return svn_error_trace(err);
}
else if (err)
{
SVN_ERR(report_error(rev, err, verify_callback, verify_baton,
iterpool));
}
else 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;
}