#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <apr_pools.h>
#include <apr_hash.h>
#include "svn_hash.h"
#include "svn_private_config.h"
#include "svn_pools.h"
#include "svn_error.h"
#include "svn_path.h"
#include "svn_mergeinfo.h"
#include "svn_fs.h"
#include "svn_props.h"
#include "svn_sorts.h"
#include "fs.h"
#include "cached_data.h"
#include "dag.h"
#include "lock.h"
#include "tree.h"
#include "fs_fs.h"
#include "id.h"
#include "pack.h"
#include "temp_serializer.h"
#include "transaction.h"
#include "util.h"
#include "private/svn_mergeinfo_private.h"
#include "private/svn_subr_private.h"
#include "private/svn_fs_util.h"
#include "private/svn_fspath.h"
#include "../libsvn_fs/fs-loader.h"
typedef dag_node_t fs_rev_root_data_t;
typedef struct fs_txn_root_data_t
{
svn_fs_fs__id_part_t txn_id;
svn_cache__t *txn_node_cache;
} fs_txn_root_data_t;
static svn_error_t * get_dag(dag_node_t **dag_node_p,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool);
static svn_fs_root_t *make_revision_root(svn_fs_t *fs, svn_revnum_t rev,
dag_node_t *root_dir,
apr_pool_t *pool);
static svn_error_t *make_txn_root(svn_fs_root_t **root_p,
svn_fs_t *fs,
const svn_fs_fs__id_part_t *txn,
svn_revnum_t base_rev,
apr_uint32_t flags,
apr_pool_t *pool);
static svn_error_t *fs_closest_copy(svn_fs_root_t **root_p,
const char **path_p,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool);
typedef struct cache_entry_t
{
apr_uint32_t hash_value;
svn_revnum_t revision;
char *path;
apr_size_t path_len;
dag_node_t *node;
} cache_entry_t;
enum { BUCKET_COUNT = 256 };
struct fs_fs_dag_cache_t
{
cache_entry_t buckets[BUCKET_COUNT];
apr_pool_t *pool;
apr_size_t insertions;
apr_size_t last_hit;
apr_size_t last_non_empty;
};
fs_fs_dag_cache_t*
svn_fs_fs__create_dag_cache(apr_pool_t *pool)
{
fs_fs_dag_cache_t *result = apr_pcalloc(pool, sizeof(*result));
result->pool = svn_pool_create(pool);
return result;
}
static void
auto_clear_dag_cache(fs_fs_dag_cache_t* cache)
{
if (cache->insertions > BUCKET_COUNT)
{
svn_pool_clear(cache->pool);
memset(cache->buckets, 0, sizeof(cache->buckets));
cache->insertions = 0;
}
}
static apr_uint32_t
hash_func(svn_revnum_t revision,
const char *path,
apr_size_t path_len)
{
apr_size_t i;
apr_uint32_t hash_value = (apr_uint32_t)revision;
#if SVN_UNALIGNED_ACCESS_IS_OK
const apr_uint32_t factor = 0xd1f3da69;
#endif
i = 0;
#if SVN_UNALIGNED_ACCESS_IS_OK
for (; i + 8 <= path_len; i += 8)
hash_value = hash_value * factor * factor
+ ( *(const apr_uint32_t*)(path + i) * factor
+ *(const apr_uint32_t*)(path + i + 4));
#endif
for (; i < path_len; ++i)
hash_value = hash_value * 32 + (hash_value + (unsigned char)path[i]);
return hash_value;
}
static dag_node_t *
cache_lookup( fs_fs_dag_cache_t *cache
, svn_revnum_t revision
, const char *path)
{
apr_size_t bucket_index;
apr_size_t path_len = strlen(path);
apr_uint32_t hash_value;
cache_entry_t *result = &cache->buckets[cache->last_hit];
if ( (result->revision == revision)
&& (result->path_len == path_len)
&& !memcmp(result->path, path, path_len))
{
if (result->node)
cache->last_non_empty = cache->last_hit;
return result->node;
}
hash_value = hash_func(revision, path, path_len);
bucket_index = hash_value + (hash_value >> 16);
bucket_index = (bucket_index + (bucket_index >> 8)) % BUCKET_COUNT;
result = &cache->buckets[bucket_index];
cache->last_hit = bucket_index;
if ( (result->hash_value != hash_value)
|| (result->revision != revision)
|| (result->path_len != path_len)
|| memcmp(result->path, path, path_len))
{
return NULL;
}
else if (result->node)
{
cache->last_non_empty = bucket_index;
}
return result->node;
}
static void
cache_insert(fs_fs_dag_cache_t *cache,
svn_revnum_t revision,
const char *path,
dag_node_t *node)
{
apr_size_t bucket_index;
apr_size_t path_len = strlen(path);
apr_uint32_t hash_value;
cache_entry_t *entry;
auto_clear_dag_cache(cache);
hash_value = hash_func(revision, path, path_len);
bucket_index = hash_value + (hash_value >> 16);
bucket_index = (bucket_index + (bucket_index >> 8)) % BUCKET_COUNT;
entry = &cache->buckets[bucket_index];
cache->last_hit = bucket_index;
entry->hash_value = hash_value;
entry->revision = revision;
if (entry->path_len < path_len)
entry->path = apr_palloc(cache->pool, path_len + 1);
entry->path_len = path_len;
memcpy(entry->path, path, path_len + 1);
entry->node = svn_fs_fs__dag_dup(node, cache->pool);
cache->insertions++;
}
static dag_node_t *
cache_lookup_last_path(fs_fs_dag_cache_t *cache,
const char *path,
apr_size_t path_len)
{
cache_entry_t *result = &cache->buckets[cache->last_non_empty];
assert(strlen(path) == path_len);
if ( result->node
&& (result->path_len == path_len)
&& !memcmp(result->path, path, path_len))
{
return result->node;
}
return NULL;
}
static void
locate_cache(svn_cache__t **cache,
const char **key,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
if (root->is_txn_root)
{
fs_txn_root_data_t *frd = root->fsap_data;
if (cache)
*cache = frd->txn_node_cache;
if (key && path)
*key = path;
}
else
{
fs_fs_data_t *ffd = root->fs->fsap_data;
if (cache)
*cache = ffd->rev_node_cache;
if (key && path)
*key = svn_fs_fs__combine_number_and_string(root->rev, path, pool);
}
}
static svn_error_t *
dag_node_cache_get(dag_node_t **node_p,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
svn_boolean_t found;
dag_node_t *node = NULL;
svn_cache__t *cache;
const char *key;
SVN_ERR_ASSERT(*path == '/');
if (!root->is_txn_root)
{
fs_fs_data_t *ffd = root->fs->fsap_data;
node = cache_lookup(ffd->dag_node_cache, root->rev, path);
if (node == NULL)
{
locate_cache(&cache, &key, root, path, pool);
SVN_ERR(svn_cache__get((void **)&node, &found, cache, key, pool));
if (found && node)
{
svn_fs_fs__dag_set_fs(node, root->fs);
cache_insert(ffd->dag_node_cache, root->rev, path, node);
}
}
else
{
node = svn_fs_fs__dag_dup(node, pool);
}
}
else
{
locate_cache(&cache, &key, root, path, pool);
SVN_ERR(svn_cache__get((void **) &node, &found, cache, key, pool));
if (found && node)
{
svn_fs_fs__dag_set_fs(node, root->fs);
}
}
*node_p = node;
return SVN_NO_ERROR;
}
static svn_error_t *
dag_node_cache_set(svn_fs_root_t *root,
const char *path,
dag_node_t *node,
apr_pool_t *pool)
{
svn_cache__t *cache;
const char *key;
SVN_ERR_ASSERT(*path == '/');
locate_cache(&cache, &key, root, path, pool);
return svn_cache__set(cache, key, node, pool);
}
struct fdic_baton {
const char *path;
apr_array_header_t *list;
apr_pool_t *pool;
};
static svn_error_t *
find_descendants_in_cache(void *baton,
const void *key,
apr_ssize_t klen,
void *val,
apr_pool_t *pool)
{
struct fdic_baton *b = baton;
const char *item_path = key;
if (svn_fspath__skip_ancestor(b->path, item_path))
APR_ARRAY_PUSH(b->list, const char *) = apr_pstrdup(b->pool, item_path);
return SVN_NO_ERROR;
}
static svn_error_t *
dag_node_cache_invalidate(svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
struct fdic_baton b;
svn_cache__t *cache;
apr_pool_t *iterpool;
int i;
b.path = path;
b.pool = svn_pool_create(pool);
b.list = apr_array_make(b.pool, 1, sizeof(const char *));
SVN_ERR_ASSERT(root->is_txn_root);
locate_cache(&cache, NULL, root, NULL, b.pool);
SVN_ERR(svn_cache__iter(NULL, cache, find_descendants_in_cache,
&b, b.pool));
iterpool = svn_pool_create(b.pool);
for (i = 0; i < b.list->nelts; i++)
{
const char *descendant = APR_ARRAY_IDX(b.list, i, const char *);
svn_pool_clear(iterpool);
SVN_ERR(svn_cache__set(cache, descendant, NULL, iterpool));
}
svn_pool_destroy(iterpool);
svn_pool_destroy(b.pool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__txn_root(svn_fs_root_t **root_p,
svn_fs_txn_t *txn,
apr_pool_t *pool)
{
apr_uint32_t flags = 0;
apr_hash_t *txnprops;
SVN_ERR(svn_fs_fs__txn_proplist(&txnprops, txn, pool));
if (txnprops)
{
if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD))
flags |= SVN_FS_TXN_CHECK_OOD;
if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS))
flags |= SVN_FS_TXN_CHECK_LOCKS;
}
return make_txn_root(root_p, txn->fs, svn_fs_fs__txn_get_id(txn),
txn->base_rev, flags, pool);
}
svn_error_t *
svn_fs_fs__revision_root(svn_fs_root_t **root_p,
svn_fs_t *fs,
svn_revnum_t rev,
apr_pool_t *pool)
{
dag_node_t *root_dir;
SVN_ERR(svn_fs__check_fs(fs, TRUE));
SVN_ERR(svn_fs_fs__dag_revision_root(&root_dir, fs, rev, pool));
*root_p = make_revision_root(fs, rev, root_dir, pool);
return SVN_NO_ERROR;
}
static const svn_fs_fs__id_part_t *
root_txn_id(svn_fs_root_t *root)
{
fs_txn_root_data_t *frd = root->fsap_data;
assert(root->is_txn_root);
return &frd->txn_id;
}
static svn_error_t *
root_node(dag_node_t **node_p,
svn_fs_root_t *root,
apr_pool_t *pool)
{
if (root->is_txn_root)
{
return svn_fs_fs__dag_txn_root(node_p, root->fs, root_txn_id(root),
pool);
}
else
{
dag_node_t *root_dir = root->fsap_data;
*node_p = svn_fs_fs__dag_dup(root_dir, pool);
return SVN_NO_ERROR;
}
}
static svn_error_t *
mutable_root_node(dag_node_t **node_p,
svn_fs_root_t *root,
const char *error_path,
apr_pool_t *pool)
{
if (root->is_txn_root)
{
return svn_fs_fs__dag_clone_root(node_p, root->fs, root_txn_id(root),
pool);
}
else
return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path);
}
typedef enum copy_id_inherit_t
{
copy_id_inherit_unknown = 0,
copy_id_inherit_self,
copy_id_inherit_parent,
copy_id_inherit_new
} copy_id_inherit_t;
typedef struct parent_path_t
{
dag_node_t *node;
char *entry;
struct parent_path_t *parent;
copy_id_inherit_t copy_inherit;
const char *copy_src_path;
} parent_path_t;
static const char *
parent_path_path(parent_path_t *parent_path,
apr_pool_t *pool)
{
const char *path_so_far = "/";
if (parent_path->parent)
path_so_far = parent_path_path(parent_path->parent, pool);
return parent_path->entry
? svn_fspath__join(path_so_far, parent_path->entry, pool)
: path_so_far;
}
static const char *
parent_path_relpath(parent_path_t *child,
parent_path_t *ancestor,
apr_pool_t *pool)
{
const char *path_so_far = "";
parent_path_t *this_node = child;
while (this_node != ancestor)
{
assert(this_node != NULL);
path_so_far = svn_relpath_join(this_node->entry, path_so_far, pool);
this_node = this_node->parent;
}
return path_so_far;
}
static svn_error_t *
get_copy_inheritance(copy_id_inherit_t *inherit_p,
const char **copy_src_path,
svn_fs_t *fs,
parent_path_t *child,
apr_pool_t *pool)
{
const svn_fs_id_t *child_id, *parent_id, *copyroot_id;
const svn_fs_fs__id_part_t *child_copy_id, *parent_copy_id;
const char *id_path = NULL;
svn_fs_root_t *copyroot_root;
dag_node_t *copyroot_node;
svn_revnum_t copyroot_rev;
const char *copyroot_path;
SVN_ERR_ASSERT(child && child->parent);
child_id = svn_fs_fs__dag_get_id(child->node);
parent_id = svn_fs_fs__dag_get_id(child->parent->node);
child_copy_id = svn_fs_fs__id_copy_id(child_id);
parent_copy_id = svn_fs_fs__id_copy_id(parent_id);
if (svn_fs_fs__id_is_txn(child_id))
{
*inherit_p = copy_id_inherit_self;
*copy_src_path = NULL;
return SVN_NO_ERROR;
}
*inherit_p = copy_id_inherit_parent;
*copy_src_path = NULL;
if (svn_fs_fs__id_part_is_root(child_copy_id))
return SVN_NO_ERROR;
if (svn_fs_fs__id_part_eq(child_copy_id, parent_copy_id))
return SVN_NO_ERROR;
SVN_ERR(svn_fs_fs__dag_get_copyroot(©root_rev, ©root_path,
child->node));
SVN_ERR(svn_fs_fs__revision_root(©root_root, fs, copyroot_rev, pool));
SVN_ERR(get_dag(©root_node, copyroot_root, copyroot_path, pool));
copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
if (svn_fs_fs__id_compare(copyroot_id, child_id) == svn_fs_node_unrelated)
return SVN_NO_ERROR;
id_path = svn_fs_fs__dag_get_created_path(child->node);
if (strcmp(id_path, parent_path_path(child, pool)) == 0)
{
*inherit_p = copy_id_inherit_self;
return SVN_NO_ERROR;
}
*inherit_p = copy_id_inherit_new;
*copy_src_path = id_path;
return SVN_NO_ERROR;
}
static parent_path_t *
make_parent_path(dag_node_t *node,
char *entry,
parent_path_t *parent,
apr_pool_t *pool)
{
parent_path_t *parent_path = apr_pcalloc(pool, sizeof(*parent_path));
parent_path->node = node;
parent_path->entry = entry;
parent_path->parent = parent;
parent_path->copy_inherit = copy_id_inherit_unknown;
parent_path->copy_src_path = NULL;
return parent_path;
}
typedef enum open_path_flags_t {
open_path_last_optional = 1,
open_path_uncached = 2,
open_path_node_only = 4,
open_path_allow_null = 8
} open_path_flags_t;
static svn_error_t *
try_match_last_node(dag_node_t **node_p,
svn_fs_root_t *root,
const char *path,
apr_size_t path_len,
apr_pool_t *scratch_pool)
{
fs_fs_data_t *ffd = root->fs->fsap_data;
dag_node_t *node
= cache_lookup_last_path(ffd->dag_node_cache, path, path_len);
if (node && !svn_fs_fs__dag_check_mutable(node))
{
const char *created_path
= svn_fs_fs__dag_get_created_path(node);
svn_revnum_t revision;
SVN_ERR(svn_fs_fs__dag_get_revision(&revision, node, scratch_pool));
if (revision == root->rev && strcmp(created_path, path) == 0)
{
SVN_ERR(dag_node_cache_set(root, path, node, scratch_pool));
*node_p = node;
return SVN_NO_ERROR;
}
}
*node_p = NULL;
return SVN_NO_ERROR;
}
static svn_error_t *
err_not_directory(svn_fs_root_t *root,
const char *path,
apr_pool_t *scratch_pool)
{
const char *msg;
msg = root->is_txn_root
? apr_psprintf(scratch_pool,
_("Failure opening '%s' in transaction '%s'"),
path, root->txn)
: apr_psprintf(scratch_pool,
_("Failure opening '%s' in revision %ld"),
path, root->rev);
return svn_error_quick_wrap(SVN_FS__ERR_NOT_DIRECTORY(root->fs, path), msg);
}
static svn_error_t *
open_path(parent_path_t **parent_path_p,
svn_fs_root_t *root,
const char *path,
int flags,
svn_boolean_t is_txn_path,
apr_pool_t *pool)
{
svn_fs_t *fs = root->fs;
dag_node_t *here = NULL;
parent_path_t *parent_path;
const char *rest = NULL;
apr_pool_t *iterpool = svn_pool_create(pool);
svn_stringbuf_t *path_so_far = svn_stringbuf_create(path, pool);
apr_size_t path_len = path_so_far->len;
assert(svn_fs__is_canonical_abspath(path));
path_so_far->len = 0;
if (flags & open_path_node_only)
{
const char *directory;
if (!root->is_txn_root)
{
dag_node_t *node;
SVN_ERR(try_match_last_node(&node, root, path, path_len, iterpool));
if (node)
{
svn_pool_destroy(iterpool);
parent_path = make_parent_path(node, 0, 0, pool);
parent_path->copy_inherit = copy_id_inherit_self;
*parent_path_p = parent_path;
return SVN_NO_ERROR;
}
}
directory = svn_dirent_dirname(path, pool);
if (directory[1] != 0)
{
SVN_ERR(dag_node_cache_get(&here, root, directory, pool));
if (here && svn_fs_fs__dag_node_kind(here) == svn_node_dir)
{
apr_size_t dirname_len = strlen(directory);
path_so_far->len = dirname_len;
rest = path + dirname_len + 1;
}
else if (here)
{
if (flags & open_path_allow_null)
{
*parent_path_p = NULL;
return SVN_NO_ERROR;
}
else
return svn_error_trace(err_not_directory(root, directory, pool));
}
}
}
if (!here)
{
SVN_ERR(root_node(&here, root, pool));
rest = path + 1;
}
path_so_far->data[path_so_far->len] = '\0';
parent_path = make_parent_path(here, 0, 0, pool);
parent_path->copy_inherit = copy_id_inherit_self;
for (;;)
{
const char *next;
char *entry;
dag_node_t *child;
svn_pool_clear(iterpool);
entry = svn_fs__next_entry_name(&next, rest, pool);
path_so_far->data[path_so_far->len] = '/';
path_so_far->len += strlen(entry) + 1;
path_so_far->data[path_so_far->len] = '\0';
if (*entry == '\0')
{
child = here;
}
else
{
copy_id_inherit_t inherit;
const char *copy_path = NULL;
dag_node_t *cached_node = NULL;
if (next || !(flags & open_path_uncached))
SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far->data,
pool));
if (cached_node)
child = cached_node;
else
SVN_ERR(svn_fs_fs__dag_open(&child, here, entry, pool, iterpool));
if (child == NULL)
{
if ((flags & open_path_last_optional)
&& (! next || *next == '\0'))
{
parent_path = make_parent_path(NULL, entry, parent_path,
pool);
break;
}
else if (flags & open_path_allow_null)
{
parent_path = NULL;
break;
}
else
{
return SVN_FS__NOT_FOUND(root, path);
}
}
if (flags & open_path_node_only)
{
parent_path->node = child;
}
else
{
parent_path = make_parent_path(child, entry, parent_path, pool);
if (is_txn_path)
{
SVN_ERR(get_copy_inheritance(&inherit, ©_path, fs,
parent_path, iterpool));
parent_path->copy_inherit = inherit;
parent_path->copy_src_path = apr_pstrdup(pool, copy_path);
}
}
if (! cached_node)
SVN_ERR(dag_node_cache_set(root, path_so_far->data, child,
iterpool));
}
if (! next)
break;
if (svn_fs_fs__dag_node_kind(child) != svn_node_dir)
{
if (flags & open_path_allow_null)
{
parent_path = NULL;
break;
}
return svn_error_trace(
err_not_directory(root, path_so_far->data, iterpool));
}
rest = next;
here = child;
}
svn_pool_destroy(iterpool);
*parent_path_p = parent_path;
return SVN_NO_ERROR;
}
static svn_error_t *
make_path_mutable(svn_fs_root_t *root,
parent_path_t *parent_path,
const char *error_path,
apr_pool_t *pool)
{
dag_node_t *clone;
const svn_fs_fs__id_part_t *txn_id = root_txn_id(root);
if (svn_fs_fs__dag_check_mutable(parent_path->node))
return SVN_NO_ERROR;
if (parent_path->parent)
{
const svn_fs_id_t *parent_id, *child_id, *copyroot_id;
svn_fs_fs__id_part_t copy_id = { SVN_INVALID_REVNUM, 0 };
svn_fs_fs__id_part_t *copy_id_ptr = ©_id;
copy_id_inherit_t inherit = parent_path->copy_inherit;
const char *clone_path, *copyroot_path;
svn_revnum_t copyroot_rev;
svn_boolean_t is_parent_copyroot = FALSE;
svn_fs_root_t *copyroot_root;
dag_node_t *copyroot_node;
SVN_ERR(make_path_mutable(root, parent_path->parent,
error_path, pool));
switch (inherit)
{
case copy_id_inherit_parent:
parent_id = svn_fs_fs__dag_get_id(parent_path->parent->node);
copy_id = *svn_fs_fs__id_copy_id(parent_id);
break;
case copy_id_inherit_new:
SVN_ERR(svn_fs_fs__reserve_copy_id(©_id, root->fs, txn_id,
pool));
break;
case copy_id_inherit_self:
copy_id_ptr = NULL;
break;
case copy_id_inherit_unknown:
default:
SVN_ERR_MALFUNCTION();
}
SVN_ERR(svn_fs_fs__dag_get_copyroot(©root_rev, ©root_path,
parent_path->node));
SVN_ERR(svn_fs_fs__revision_root(©root_root, root->fs,
copyroot_rev, pool));
SVN_ERR(get_dag(©root_node, copyroot_root, copyroot_path, pool));
child_id = svn_fs_fs__dag_get_id(parent_path->node);
copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
if (!svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(child_id),
svn_fs_fs__id_node_id(copyroot_id)))
is_parent_copyroot = TRUE;
clone_path = parent_path_path(parent_path->parent, pool);
SVN_ERR(svn_fs_fs__dag_clone_child(&clone,
parent_path->parent->node,
clone_path,
parent_path->entry,
copy_id_ptr, txn_id,
is_parent_copyroot,
pool));
SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool),
clone, pool));
}
else
{
SVN_ERR(mutable_root_node(&clone, root, error_path, pool));
}
parent_path->node = clone;
return SVN_NO_ERROR;
}
static svn_error_t *
get_dag(dag_node_t **dag_node_p,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
parent_path_t *parent_path;
dag_node_t *node = NULL;
if (*path == '/')
SVN_ERR(dag_node_cache_get(&node, root, path, pool));
if (! node)
{
if (*path != '/' || !svn_fs__is_canonical_abspath(path))
{
path = svn_fs__canonicalize_abspath(path, pool);
SVN_ERR(dag_node_cache_get(&node, root, path, pool));
}
if (! node)
{
SVN_ERR(open_path(&parent_path, root, path,
open_path_uncached | open_path_node_only,
FALSE, pool));
node = parent_path->node;
}
}
*dag_node_p = node;
return SVN_NO_ERROR;
}
static svn_error_t *
add_change(svn_fs_t *fs,
const svn_fs_fs__id_part_t *txn_id,
const char *path,
const svn_fs_id_t *noderev_id,
svn_fs_path_change_kind_t change_kind,
svn_boolean_t text_mod,
svn_boolean_t prop_mod,
svn_boolean_t mergeinfo_mod,
svn_node_kind_t node_kind,
svn_revnum_t copyfrom_rev,
const char *copyfrom_path,
apr_pool_t *pool)
{
return svn_fs_fs__add_change(fs, txn_id,
svn_fs__canonicalize_abspath(path, pool),
noderev_id, change_kind,
text_mod, prop_mod, mergeinfo_mod,
node_kind, copyfrom_rev, copyfrom_path,
pool);
}
svn_error_t *
svn_fs_fs__node_id(const svn_fs_id_t **id_p,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
if ((! root->is_txn_root)
&& (path[0] == '\0' || ((path[0] == '/') && (path[1] == '\0'))))
{
dag_node_t *root_dir = root->fsap_data;
*id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(root_dir), pool);
}
else
{
dag_node_t *node;
SVN_ERR(get_dag(&node, root, path, pool));
*id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(node), pool);
}
return SVN_NO_ERROR;
}
static svn_error_t *
fs_node_relation(svn_fs_node_relation_t *relation,
svn_fs_root_t *root_a, const char *path_a,
svn_fs_root_t *root_b, const char *path_b,
apr_pool_t *pool)
{
dag_node_t *node;
const svn_fs_id_t *id_a, *id_b;
svn_fs_fs__id_part_t node_id_a, node_id_b;
svn_boolean_t a_is_root_dir
= (path_a[0] == '\0') || ((path_a[0] == '/') && (path_a[1] == '\0'));
svn_boolean_t b_is_root_dir
= (path_b[0] == '\0') || ((path_b[0] == '/') && (path_b[1] == '\0'));
svn_boolean_t different_txn
= root_a->is_txn_root && root_b->is_txn_root
&& strcmp(root_a->txn, root_b->txn);
if (root_a->fs != root_b->fs)
{
*relation = svn_fs_node_unrelated;
return SVN_NO_ERROR;
}
if (a_is_root_dir && b_is_root_dir)
{
*relation = ( (root_a->rev == root_b->rev)
&& (root_a->is_txn_root == root_b->is_txn_root)
&& !different_txn)
? svn_fs_node_unchanged
: svn_fs_node_common_ancestor;
return SVN_NO_ERROR;
}
SVN_ERR(get_dag(&node, root_a, path_a, pool));
id_a = svn_fs_fs__dag_get_id(node);
node_id_a = *svn_fs_fs__id_node_id(id_a);
SVN_ERR(get_dag(&node, root_b, path_b, pool));
id_b = svn_fs_fs__dag_get_id(node);
node_id_b = *svn_fs_fs__id_node_id(id_b);
if (!svn_fs_fs__id_part_eq(&node_id_a, &node_id_b))
{
*relation = svn_fs_node_unrelated;
return SVN_NO_ERROR;
}
if (different_txn && node_id_a.revision == SVN_INVALID_REVNUM)
{
*relation = svn_fs_node_unrelated;
return SVN_NO_ERROR;
}
if (svn_fs_fs__id_eq(id_a, id_b))
*relation = svn_fs_node_unchanged;
else
*relation = svn_fs_node_common_ancestor;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__node_created_rev(svn_revnum_t *revision,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
dag_node_t *node;
SVN_ERR(get_dag(&node, root, path, pool));
return svn_fs_fs__dag_get_revision(revision, node, pool);
}
static svn_error_t *
fs_node_created_path(const char **created_path,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
dag_node_t *node;
SVN_ERR(get_dag(&node, root, path, pool));
*created_path = svn_fs_fs__dag_get_created_path(node);
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__check_path(svn_node_kind_t *kind_p,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
dag_node_t *node;
svn_error_t *err;
err = get_dag(&node, root, path, pool);
if (err &&
((err->apr_err == SVN_ERR_FS_NOT_FOUND)
|| (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY)))
{
svn_error_clear(err);
*kind_p = svn_node_none;
return SVN_NO_ERROR;
}
else if (err)
return svn_error_trace(err);
*kind_p = svn_fs_fs__dag_node_kind(node);
return SVN_NO_ERROR;
}
static svn_error_t *
fs_node_prop(svn_string_t **value_p,
svn_fs_root_t *root,
const char *path,
const char *propname,
apr_pool_t *pool)
{
dag_node_t *node;
apr_hash_t *proplist;
SVN_ERR(get_dag(&node, root, path, pool));
SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, node, pool));
*value_p = NULL;
if (proplist)
*value_p = svn_hash_gets(proplist, propname);
return SVN_NO_ERROR;
}
static svn_error_t *
fs_node_proplist(apr_hash_t **table_p,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
apr_hash_t *table;
dag_node_t *node;
SVN_ERR(get_dag(&node, root, path, pool));
SVN_ERR(svn_fs_fs__dag_get_proplist(&table, node, pool));
*table_p = table ? table : apr_hash_make(pool);
return SVN_NO_ERROR;
}
static svn_error_t *
fs_node_has_props(svn_boolean_t *has_props,
svn_fs_root_t *root,
const char *path,
apr_pool_t *scratch_pool)
{
dag_node_t *node;
SVN_ERR(get_dag(&node, root, path, scratch_pool));
return svn_error_trace(svn_fs_fs__dag_has_props(has_props, node,
scratch_pool));
}
static svn_error_t *
increment_mergeinfo_up_tree(parent_path_t *pp,
apr_int64_t increment,
apr_pool_t *pool)
{
for (; pp; pp = pp->parent)
SVN_ERR(svn_fs_fs__dag_increment_mergeinfo_count(pp->node,
increment,
pool));
return SVN_NO_ERROR;
}
static svn_error_t *
fs_change_node_prop(svn_fs_root_t *root,
const char *path,
const char *name,
const svn_string_t *value,
apr_pool_t *pool)
{
parent_path_t *parent_path;
apr_hash_t *proplist;
const svn_fs_fs__id_part_t *txn_id;
svn_boolean_t mergeinfo_mod = FALSE;
if (! root->is_txn_root)
return SVN_FS__NOT_TXN(root);
txn_id = root_txn_id(root);
path = svn_fs__canonicalize_abspath(path, pool);
SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, pool));
if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, FALSE, FALSE,
pool));
SVN_ERR(make_path_mutable(root, parent_path, path, pool));
SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, parent_path->node, pool));
if ((! proplist) && (! value))
return SVN_NO_ERROR;
if (! proplist)
proplist = apr_hash_make(pool);
if (svn_fs_fs__fs_supports_mergeinfo(root->fs)
&& strcmp (name, SVN_PROP_MERGEINFO) == 0)
{
apr_int64_t increment = 0;
svn_boolean_t had_mergeinfo;
SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&had_mergeinfo, parent_path->node));
if (value && !had_mergeinfo)
increment = 1;
else if (!value && had_mergeinfo)
increment = -1;
if (increment != 0)
{
SVN_ERR(increment_mergeinfo_up_tree(parent_path, increment, pool));
SVN_ERR(svn_fs_fs__dag_set_has_mergeinfo(parent_path->node,
(value != NULL), pool));
}
mergeinfo_mod = TRUE;
}
svn_hash_sets(proplist, name, value);
SVN_ERR(svn_fs_fs__dag_set_proplist(parent_path->node, proplist,
pool));
return add_change(root->fs, txn_id, path,
svn_fs_fs__dag_get_id(parent_path->node),
svn_fs_path_change_modify, FALSE, TRUE, mergeinfo_mod,
svn_fs_fs__dag_node_kind(parent_path->node),
SVN_INVALID_REVNUM, NULL, pool);
}
static svn_error_t *
fs_props_changed(svn_boolean_t *changed_p,
svn_fs_root_t *root1,
const char *path1,
svn_fs_root_t *root2,
const char *path2,
svn_boolean_t strict,
apr_pool_t *pool)
{
dag_node_t *node1, *node2;
if (root1->fs != root2->fs)
return svn_error_create
(SVN_ERR_FS_GENERAL, NULL,
_("Cannot compare property value between two different filesystems"));
SVN_ERR(get_dag(&node1, root1, path1, pool));
SVN_ERR(get_dag(&node2, root2, path2, pool));
return svn_fs_fs__dag_things_different(changed_p, NULL,
node1, node2, strict, pool);
}
static svn_error_t *
get_root(dag_node_t **node, svn_fs_root_t *root, apr_pool_t *pool)
{
return get_dag(node, root, "/", pool);
}
static svn_error_t *
conflict_err(svn_stringbuf_t *conflict_path,
const char *path)
{
svn_stringbuf_set(conflict_path, path);
return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL,
_("Conflict at '%s'"), path);
}
static svn_error_t *
compare_dir_structure(svn_boolean_t *changed,
dag_node_t *lhs,
dag_node_t *rhs,
apr_pool_t *pool)
{
apr_array_header_t *lhs_entries;
apr_array_header_t *rhs_entries;
int i;
SVN_ERR(svn_fs_fs__dag_dir_entries(&lhs_entries, lhs, pool));
SVN_ERR(svn_fs_fs__dag_dir_entries(&rhs_entries, rhs, pool));
if (lhs_entries->nelts != rhs_entries->nelts)
{
*changed = TRUE;
return SVN_NO_ERROR;
}
for (i = 0; i < lhs_entries->nelts; ++i)
{
svn_fs_dirent_t *lhs_entry
= APR_ARRAY_IDX(lhs_entries, i, svn_fs_dirent_t *);
svn_fs_dirent_t *rhs_entry
= APR_ARRAY_IDX(rhs_entries, i, svn_fs_dirent_t *);
if (strcmp(lhs_entry->name, rhs_entry->name)
|| !svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(lhs_entry->id),
svn_fs_fs__id_node_id(rhs_entry->id))
|| !svn_fs_fs__id_part_eq(svn_fs_fs__id_copy_id(lhs_entry->id),
svn_fs_fs__id_copy_id(rhs_entry->id)))
{
*changed = TRUE;
return SVN_NO_ERROR;
}
}
*changed = FALSE;
return SVN_NO_ERROR;
}
static svn_error_t *
merge(svn_stringbuf_t *conflict_p,
const char *target_path,
dag_node_t *target,
dag_node_t *source,
dag_node_t *ancestor,
const svn_fs_fs__id_part_t *txn_id,
apr_int64_t *mergeinfo_increment_out,
apr_pool_t *pool)
{
const svn_fs_id_t *source_id, *target_id, *ancestor_id;
apr_array_header_t *s_entries, *t_entries, *a_entries;
int i, s_idx = -1, t_idx = -1;
svn_fs_t *fs;
apr_pool_t *iterpool;
apr_int64_t mergeinfo_increment = 0;
svn_boolean_t fs_supports_mergeinfo;
fs = svn_fs_fs__dag_get_fs(ancestor);
if ((fs != svn_fs_fs__dag_get_fs(source))
|| (fs != svn_fs_fs__dag_get_fs(target)))
{
return svn_error_create
(SVN_ERR_FS_CORRUPT, NULL,
_("Bad merge; ancestor, source, and target not all in same fs"));
}
SVN_ERR(svn_fs__check_fs(fs, TRUE));
source_id = svn_fs_fs__dag_get_id(source);
target_id = svn_fs_fs__dag_get_id(target);
ancestor_id = svn_fs_fs__dag_get_id(ancestor);
if (svn_fs_fs__id_eq(ancestor_id, target_id))
{
svn_string_t *id_str = svn_fs_fs__id_unparse(target_id, pool);
return svn_error_createf
(SVN_ERR_FS_GENERAL, NULL,
_("Bad merge; target '%s' has id '%s', same as ancestor"),
target_path, id_str->data);
}
svn_stringbuf_setempty(conflict_p);
if (svn_fs_fs__id_eq(ancestor_id, source_id)
|| (svn_fs_fs__id_eq(source_id, target_id)))
return SVN_NO_ERROR;
if ((svn_fs_fs__dag_node_kind(source) != svn_node_dir)
|| (svn_fs_fs__dag_node_kind(target) != svn_node_dir)
|| (svn_fs_fs__dag_node_kind(ancestor) != svn_node_dir))
{
return conflict_err(conflict_p, target_path);
}
{
node_revision_t *tgt_nr, *anc_nr, *src_nr;
svn_boolean_t same;
apr_pool_t *scratch_pool;
scratch_pool = svn_pool_create(pool);
SVN_ERR(svn_fs_fs__get_node_revision(&tgt_nr, fs, target_id, pool,
scratch_pool));
svn_pool_clear(scratch_pool);
SVN_ERR(svn_fs_fs__get_node_revision(&anc_nr, fs, ancestor_id, pool,
scratch_pool));
svn_pool_clear(scratch_pool);
SVN_ERR(svn_fs_fs__get_node_revision(&src_nr, fs, source_id, pool,
scratch_pool));
svn_pool_destroy(scratch_pool);
SVN_ERR(svn_fs_fs__prop_rep_equal(&same, fs, src_nr, anc_nr, pool));
if (! same)
return conflict_err(conflict_p, target_path);
SVN_ERR(svn_fs_fs__prop_rep_equal(&same, fs, tgt_nr, anc_nr, pool));
if (! same)
{
svn_boolean_t changed;
SVN_ERR(compare_dir_structure(&changed, source, ancestor, pool));
if (changed)
return conflict_err(conflict_p, target_path);
}
}
SVN_ERR(svn_fs_fs__dag_dir_entries(&s_entries, source, pool));
SVN_ERR(svn_fs_fs__dag_dir_entries(&t_entries, target, pool));
SVN_ERR(svn_fs_fs__dag_dir_entries(&a_entries, ancestor, pool));
fs_supports_mergeinfo = svn_fs_fs__fs_supports_mergeinfo(fs);
iterpool = svn_pool_create(pool);
for (i = 0; i < a_entries->nelts; ++i)
{
svn_fs_dirent_t *s_entry, *t_entry, *a_entry;
svn_pool_clear(iterpool);
a_entry = APR_ARRAY_IDX(a_entries, i, svn_fs_dirent_t *);
s_entry = svn_fs_fs__find_dir_entry(s_entries, a_entry->name, &s_idx);
t_entry = svn_fs_fs__find_dir_entry(t_entries, a_entry->name, &t_idx);
if (s_entry && svn_fs_fs__id_eq(a_entry->id, s_entry->id))
continue;
else if (t_entry && svn_fs_fs__id_eq(a_entry->id, t_entry->id))
{
dag_node_t *t_ent_node;
SVN_ERR(svn_fs_fs__dag_get_node(&t_ent_node, fs,
t_entry->id, iterpool));
if (fs_supports_mergeinfo)
{
apr_int64_t mergeinfo_start;
SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start,
t_ent_node));
mergeinfo_increment -= mergeinfo_start;
}
if (s_entry)
{
dag_node_t *s_ent_node;
SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
s_entry->id, iterpool));
if (fs_supports_mergeinfo)
{
apr_int64_t mergeinfo_end;
SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end,
s_ent_node));
mergeinfo_increment += mergeinfo_end;
}
SVN_ERR(svn_fs_fs__dag_set_entry(target, a_entry->name,
s_entry->id,
s_entry->kind,
txn_id,
pool));
}
else
{
SVN_ERR(svn_fs_fs__dag_delete(target, a_entry->name, txn_id,
iterpool));
}
}
else
{
dag_node_t *s_ent_node, *t_ent_node, *a_ent_node;
const char *new_tpath;
apr_int64_t sub_mergeinfo_increment;
if (s_entry == NULL || t_entry == NULL)
return conflict_err(conflict_p,
svn_fspath__join(target_path,
a_entry->name,
iterpool));
if (s_entry->kind == svn_node_file
|| t_entry->kind == svn_node_file
|| a_entry->kind == svn_node_file)
return conflict_err(conflict_p,
svn_fspath__join(target_path,
a_entry->name,
iterpool));
if (!svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(s_entry->id),
svn_fs_fs__id_node_id(a_entry->id))
|| !svn_fs_fs__id_part_eq(svn_fs_fs__id_copy_id(s_entry->id),
svn_fs_fs__id_copy_id(a_entry->id))
|| !svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(t_entry->id),
svn_fs_fs__id_node_id(a_entry->id))
|| !svn_fs_fs__id_part_eq(svn_fs_fs__id_copy_id(t_entry->id),
svn_fs_fs__id_copy_id(a_entry->id)))
return conflict_err(conflict_p,
svn_fspath__join(target_path,
a_entry->name,
iterpool));
SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
s_entry->id, iterpool));
SVN_ERR(svn_fs_fs__dag_get_node(&t_ent_node, fs,
t_entry->id, iterpool));
SVN_ERR(svn_fs_fs__dag_get_node(&a_ent_node, fs,
a_entry->id, iterpool));
new_tpath = svn_fspath__join(target_path, t_entry->name, iterpool);
SVN_ERR(merge(conflict_p, new_tpath,
t_ent_node, s_ent_node, a_ent_node,
txn_id,
&sub_mergeinfo_increment,
iterpool));
if (fs_supports_mergeinfo)
mergeinfo_increment += sub_mergeinfo_increment;
}
}
for (i = 0; i < s_entries->nelts; ++i)
{
svn_fs_dirent_t *a_entry, *s_entry, *t_entry;
dag_node_t *s_ent_node;
svn_pool_clear(iterpool);
s_entry = APR_ARRAY_IDX(s_entries, i, svn_fs_dirent_t *);
a_entry = svn_fs_fs__find_dir_entry(a_entries, s_entry->name, &s_idx);
t_entry = svn_fs_fs__find_dir_entry(t_entries, s_entry->name, &t_idx);
if (a_entry)
continue;
if (t_entry)
return conflict_err(conflict_p,
svn_fspath__join(target_path,
t_entry->name,
iterpool));
SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
s_entry->id, iterpool));
if (fs_supports_mergeinfo)
{
apr_int64_t mergeinfo_s;
SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_s,
s_ent_node));
mergeinfo_increment += mergeinfo_s;
}
SVN_ERR(svn_fs_fs__dag_set_entry
(target, s_entry->name, s_entry->id, s_entry->kind,
txn_id, iterpool));
}
svn_pool_destroy(iterpool);
SVN_ERR(svn_fs_fs__dag_update_ancestry(target, source, pool));
if (fs_supports_mergeinfo)
SVN_ERR(svn_fs_fs__dag_increment_mergeinfo_count(target,
mergeinfo_increment,
pool));
if (mergeinfo_increment_out)
*mergeinfo_increment_out = mergeinfo_increment;
return SVN_NO_ERROR;
}
static svn_error_t *
merge_changes(dag_node_t *ancestor_node,
dag_node_t *source_node,
svn_fs_txn_t *txn,
svn_stringbuf_t *conflict,
apr_pool_t *pool)
{
dag_node_t *txn_root_node;
svn_fs_t *fs = txn->fs;
const svn_fs_fs__id_part_t *txn_id = svn_fs_fs__txn_get_id(txn);
SVN_ERR(svn_fs_fs__dag_txn_root(&txn_root_node, fs, txn_id, pool));
if (ancestor_node == NULL)
{
SVN_ERR(svn_fs_fs__dag_txn_base_root(&ancestor_node, fs,
txn_id, pool));
}
if (svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(ancestor_node),
svn_fs_fs__dag_get_id(txn_root_node)))
{
SVN_ERR_MALFUNCTION();
}
else
SVN_ERR(merge(conflict, "/", txn_root_node,
source_node, ancestor_node, txn_id, NULL, pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__commit_txn(const char **conflict_p,
svn_revnum_t *new_rev,
svn_fs_txn_t *txn,
apr_pool_t *pool)
{
svn_error_t *err = SVN_NO_ERROR;
svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool);
svn_fs_t *fs = txn->fs;
fs_fs_data_t *ffd = fs->fsap_data;
apr_pool_t *iterpool = svn_pool_create(pool);
*new_rev = SVN_INVALID_REVNUM;
if (conflict_p)
*conflict_p = NULL;
while (1729)
{
svn_revnum_t youngish_rev;
svn_fs_root_t *youngish_root;
dag_node_t *youngish_root_node;
svn_pool_clear(iterpool);
SVN_ERR(svn_fs_fs__youngest_rev(&youngish_rev, fs, iterpool));
SVN_ERR(svn_fs_fs__revision_root(&youngish_root, fs, youngish_rev,
iterpool));
SVN_ERR(get_root(&youngish_root_node, youngish_root, iterpool));
err = merge_changes(NULL, youngish_root_node, txn, conflict, iterpool);
if (err)
{
if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
*conflict_p = conflict->data;
goto cleanup;
}
txn->base_rev = youngish_rev;
err = svn_fs_fs__commit(new_rev, fs, txn, iterpool);
if (err && (err->apr_err == SVN_ERR_FS_TXN_OUT_OF_DATE))
{
svn_revnum_t youngest_rev;
SVN_ERR(svn_fs_fs__youngest_rev(&youngest_rev, fs, iterpool));
if (youngest_rev == youngish_rev)
goto cleanup;
else
svn_error_clear(err);
}
else if (err)
{
goto cleanup;
}
else
{
err = SVN_NO_ERROR;
goto cleanup;
}
}
cleanup:
svn_fs_fs__reset_txn_caches(fs);
svn_pool_destroy(iterpool);
SVN_ERR(err);
if (ffd->pack_after_commit)
{
SVN_ERR(svn_fs_fs__pack(fs, 0, NULL, NULL, NULL, NULL, pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *
fs_merge(const char **conflict_p,
svn_fs_root_t *source_root,
const char *source_path,
svn_fs_root_t *target_root,
const char *target_path,
svn_fs_root_t *ancestor_root,
const char *ancestor_path,
apr_pool_t *pool)
{
dag_node_t *source, *ancestor;
svn_fs_txn_t *txn;
svn_error_t *err;
svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool);
if (! target_root->is_txn_root)
return SVN_FS__NOT_TXN(target_root);
if ((source_root->fs != ancestor_root->fs)
|| (target_root->fs != ancestor_root->fs))
{
return svn_error_create
(SVN_ERR_FS_CORRUPT, NULL,
_("Bad merge; ancestor, source, and target not all in same fs"));
}
SVN_ERR(get_root(&ancestor, ancestor_root, pool));
SVN_ERR(get_root(&source, source_root, pool));
SVN_ERR(svn_fs_fs__open_txn(&txn, ancestor_root->fs, target_root->txn,
pool));
err = merge_changes(ancestor, source, txn, conflict, pool);
if (err)
{
if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
*conflict_p = conflict->data;
return svn_error_trace(err);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__deltify(svn_fs_t *fs,
svn_revnum_t revision,
apr_pool_t *pool)
{
return SVN_NO_ERROR;
}
static svn_error_t *
fs_dir_entries(apr_hash_t **table_p,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
dag_node_t *node;
apr_hash_t *hash = svn_hash__make(pool);
apr_array_header_t *table;
int i;
SVN_ERR(get_dag(&node, root, path, pool));
SVN_ERR(svn_fs_fs__dag_dir_entries(&table, node, pool));
for (i = 0; i < table->nelts; ++i)
{
svn_fs_dirent_t *entry = APR_ARRAY_IDX(table, i, svn_fs_dirent_t *);
svn_hash_sets(hash, entry->name, entry);
}
*table_p = hash;
return SVN_NO_ERROR;
}
static svn_error_t *
fs_dir_optimal_order(apr_array_header_t **ordered_p,
svn_fs_root_t *root,
apr_hash_t *entries,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
*ordered_p = svn_fs_fs__order_dir_entries(root->fs, entries, result_pool,
scratch_pool);
return SVN_NO_ERROR;
}
static svn_error_t *
check_newline(const char *path, apr_pool_t *pool)
{
char *c = strchr(path, '\n');
if (c)
return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
_("Invalid control character '0x%02x' in path '%s'"),
(unsigned char)*c, svn_path_illegal_path_escape(path, pool));
return SVN_NO_ERROR;
}
static svn_error_t *
fs_make_dir(svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
parent_path_t *parent_path;
dag_node_t *sub_dir;
const svn_fs_fs__id_part_t *txn_id = root_txn_id(root);
SVN_ERR(check_newline(path, pool));
path = svn_fs__canonicalize_abspath(path, pool);
SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
TRUE, pool));
if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, TRUE, FALSE,
pool));
if (parent_path->node)
return SVN_FS__ALREADY_EXISTS(root, path);
SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
SVN_ERR(svn_fs_fs__dag_make_dir(&sub_dir,
parent_path->parent->node,
parent_path_path(parent_path->parent,
pool),
parent_path->entry,
txn_id,
pool));
SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool),
sub_dir, pool));
return add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(sub_dir),
svn_fs_path_change_add, FALSE, FALSE, FALSE,
svn_node_dir, SVN_INVALID_REVNUM, NULL, pool);
}
static svn_error_t *
fs_delete_node(svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
parent_path_t *parent_path;
const svn_fs_fs__id_part_t *txn_id;
apr_int64_t mergeinfo_count = 0;
svn_node_kind_t kind;
if (! root->is_txn_root)
return SVN_FS__NOT_TXN(root);
txn_id = root_txn_id(root);
path = svn_fs__canonicalize_abspath(path, pool);
SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, pool));
kind = svn_fs_fs__dag_node_kind(parent_path->node);
if (! parent_path->parent)
return svn_error_create(SVN_ERR_FS_ROOT_DIR, NULL,
_("The root directory cannot be deleted"));
if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, TRUE, FALSE,
pool));
SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
if (svn_fs_fs__fs_supports_mergeinfo(root->fs))
SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_count,
parent_path->node));
SVN_ERR(svn_fs_fs__dag_delete(parent_path->parent->node,
parent_path->entry,
txn_id, pool));
SVN_ERR(dag_node_cache_invalidate(root, parent_path_path(parent_path, pool),
pool));
if (mergeinfo_count > 0)
SVN_ERR(increment_mergeinfo_up_tree(parent_path->parent,
-mergeinfo_count,
pool));
return add_change(root->fs, txn_id, path,
svn_fs_fs__dag_get_id(parent_path->node),
svn_fs_path_change_delete, FALSE, FALSE, FALSE, kind,
SVN_INVALID_REVNUM, NULL, pool);
}
static svn_error_t *
fs_same_p(svn_boolean_t *same_p,
svn_fs_t *fs1,
svn_fs_t *fs2,
apr_pool_t *pool)
{
*same_p = ! strcmp(fs1->uuid, fs2->uuid);
return SVN_NO_ERROR;
}
static svn_error_t *
copy_helper(svn_fs_root_t *from_root,
const char *from_path,
svn_fs_root_t *to_root,
const char *to_path,
svn_boolean_t preserve_history,
apr_pool_t *pool)
{
dag_node_t *from_node;
parent_path_t *to_parent_path;
const svn_fs_fs__id_part_t *txn_id = root_txn_id(to_root);
svn_boolean_t same_p;
SVN_ERR(fs_same_p(&same_p, from_root->fs, to_root->fs, pool));
if (! same_p)
return svn_error_createf
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Cannot copy between two different filesystems ('%s' and '%s')"),
from_root->fs->path, to_root->fs->path);
if (from_root->is_txn_root)
return svn_error_create
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Copy from mutable tree not currently supported"));
if (! to_root->is_txn_root)
return svn_error_create
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Copy immutable tree not supported"));
SVN_ERR(get_dag(&from_node, from_root, from_path, pool));
SVN_ERR(open_path(&to_parent_path, to_root, to_path,
open_path_last_optional, TRUE, pool));
if (to_root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
SVN_ERR(svn_fs_fs__allow_locked_operation(to_path, to_root->fs,
TRUE, FALSE, pool));
if (to_parent_path->node &&
svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(from_node),
svn_fs_fs__dag_get_id(to_parent_path->node)))
return SVN_NO_ERROR;
if (! from_root->is_txn_root)
{
svn_fs_path_change_kind_t kind;
dag_node_t *new_node;
const char *from_canonpath;
apr_int64_t mergeinfo_start;
apr_int64_t mergeinfo_end;
if (to_parent_path->node)
{
kind = svn_fs_path_change_replace;
if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs))
SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start,
to_parent_path->node));
}
else
{
kind = svn_fs_path_change_add;
mergeinfo_start = 0;
}
if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs))
SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end,
from_node));
SVN_ERR(make_path_mutable(to_root, to_parent_path->parent,
to_path, pool));
from_canonpath = svn_fs__canonicalize_abspath(from_path, pool);
SVN_ERR(svn_fs_fs__dag_copy(to_parent_path->parent->node,
to_parent_path->entry,
from_node,
preserve_history,
from_root->rev,
from_canonpath,
txn_id, pool));
if (kind != svn_fs_path_change_add)
SVN_ERR(dag_node_cache_invalidate(to_root,
parent_path_path(to_parent_path,
pool), pool));
if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs)
&& mergeinfo_start != mergeinfo_end)
SVN_ERR(increment_mergeinfo_up_tree(to_parent_path->parent,
mergeinfo_end - mergeinfo_start,
pool));
SVN_ERR(get_dag(&new_node, to_root, to_path, pool));
SVN_ERR(add_change(to_root->fs, txn_id, to_path,
svn_fs_fs__dag_get_id(new_node), kind, FALSE,
FALSE, FALSE, svn_fs_fs__dag_node_kind(from_node),
from_root->rev, from_canonpath, pool));
}
else
{
SVN_ERR_MALFUNCTION();
}
return SVN_NO_ERROR;
}
static svn_error_t *
fs_copy(svn_fs_root_t *from_root,
const char *from_path,
svn_fs_root_t *to_root,
const char *to_path,
apr_pool_t *pool)
{
SVN_ERR(check_newline(to_path, pool));
return svn_error_trace(copy_helper(from_root,
svn_fs__canonicalize_abspath(from_path,
pool),
to_root,
svn_fs__canonicalize_abspath(to_path,
pool),
TRUE, pool));
}
static svn_error_t *
fs_revision_link(svn_fs_root_t *from_root,
svn_fs_root_t *to_root,
const char *path,
apr_pool_t *pool)
{
if (! to_root->is_txn_root)
return SVN_FS__NOT_TXN(to_root);
path = svn_fs__canonicalize_abspath(path, pool);
return svn_error_trace(copy_helper(from_root, path, to_root, path,
FALSE, pool));
}
static svn_error_t *
fs_copied_from(svn_revnum_t *rev_p,
const char **path_p,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
dag_node_t *node;
SVN_ERR(get_dag(&node, root, path, pool));
SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(rev_p, node));
SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(path_p, node));
return SVN_NO_ERROR;
}
static svn_error_t *
fs_make_file(svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
parent_path_t *parent_path;
dag_node_t *child;
const svn_fs_fs__id_part_t *txn_id = root_txn_id(root);
SVN_ERR(check_newline(path, pool));
path = svn_fs__canonicalize_abspath(path, pool);
SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
TRUE, pool));
if (parent_path->node)
return SVN_FS__ALREADY_EXISTS(root, path);
if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, FALSE, FALSE,
pool));
SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
SVN_ERR(svn_fs_fs__dag_make_file(&child,
parent_path->parent->node,
parent_path_path(parent_path->parent,
pool),
parent_path->entry,
txn_id,
pool));
SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool), child,
pool));
return add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(child),
svn_fs_path_change_add, TRUE, FALSE, FALSE,
svn_node_file, SVN_INVALID_REVNUM, NULL, pool);
}
static svn_error_t *
fs_file_length(svn_filesize_t *length_p,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
dag_node_t *file;
SVN_ERR(get_dag(&file, root, path, pool));
return svn_fs_fs__dag_file_length(length_p, file, pool);
}
static svn_error_t *
fs_file_checksum(svn_checksum_t **checksum,
svn_checksum_kind_t kind,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
dag_node_t *file;
SVN_ERR(get_dag(&file, root, path, pool));
return svn_fs_fs__dag_file_checksum(checksum, file, kind, pool);
}
static svn_error_t *
fs_file_contents(svn_stream_t **contents,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
dag_node_t *node;
svn_stream_t *file_stream;
SVN_ERR(get_dag(&node, root, path, pool));
SVN_ERR(svn_fs_fs__dag_get_contents(&file_stream, node, pool));
*contents = file_stream;
return SVN_NO_ERROR;
}
static svn_error_t *
fs_try_process_file_contents(svn_boolean_t *success,
svn_fs_root_t *root,
const char *path,
svn_fs_process_contents_func_t processor,
void* baton,
apr_pool_t *pool)
{
dag_node_t *node;
SVN_ERR(get_dag(&node, root, path, pool));
return svn_fs_fs__dag_try_process_file_contents(success, node,
processor, baton, pool);
}
typedef struct txdelta_baton_t
{
svn_txdelta_window_handler_t interpreter;
void *interpreter_baton;
svn_fs_root_t *root;
const char *path;
dag_node_t *node;
svn_stream_t *source_stream;
svn_stream_t *target_stream;
svn_checksum_t *base_checksum;
svn_checksum_t *result_checksum;
apr_pool_t *pool;
} txdelta_baton_t;
static svn_error_t *
window_consumer(svn_txdelta_window_t *window, void *baton)
{
txdelta_baton_t *tb = (txdelta_baton_t *) baton;
SVN_ERR(tb->interpreter(window, tb->interpreter_baton));
if (! window)
SVN_ERR(svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum,
tb->pool));
return SVN_NO_ERROR;
}
static svn_error_t *
apply_textdelta(void *baton, apr_pool_t *pool)
{
txdelta_baton_t *tb = (txdelta_baton_t *) baton;
parent_path_t *parent_path;
const svn_fs_fs__id_part_t *txn_id = root_txn_id(tb->root);
SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, TRUE, pool));
if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
SVN_ERR(svn_fs_fs__allow_locked_operation(tb->path, tb->root->fs,
FALSE, FALSE, pool));
SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, pool));
tb->node = parent_path->node;
if (tb->base_checksum)
{
svn_checksum_t *checksum;
SVN_ERR(svn_fs_fs__dag_file_checksum(&checksum, tb->node,
tb->base_checksum->kind, pool));
if (!svn_checksum_match(tb->base_checksum, checksum))
return svn_checksum_mismatch_err(tb->base_checksum, checksum, pool,
_("Base checksum mismatch on '%s'"),
tb->path);
}
SVN_ERR(svn_fs_fs__dag_get_contents(&(tb->source_stream),
tb->node, tb->pool));
SVN_ERR(svn_fs_fs__dag_get_edit_stream(&(tb->target_stream), tb->node,
tb->pool));
svn_txdelta_apply(tb->source_stream,
tb->target_stream,
NULL,
tb->path,
tb->pool,
&(tb->interpreter),
&(tb->interpreter_baton));
return add_change(tb->root->fs, txn_id, tb->path,
svn_fs_fs__dag_get_id(tb->node),
svn_fs_path_change_modify, TRUE, FALSE, FALSE,
svn_node_file, SVN_INVALID_REVNUM, NULL, pool);
}
static svn_error_t *
fs_apply_textdelta(svn_txdelta_window_handler_t *contents_p,
void **contents_baton_p,
svn_fs_root_t *root,
const char *path,
svn_checksum_t *base_checksum,
svn_checksum_t *result_checksum,
apr_pool_t *pool)
{
txdelta_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
tb->root = root;
tb->path = svn_fs__canonicalize_abspath(path, pool);
tb->pool = pool;
tb->base_checksum = svn_checksum_dup(base_checksum, pool);
tb->result_checksum = svn_checksum_dup(result_checksum, pool);
SVN_ERR(apply_textdelta(tb, pool));
*contents_p = window_consumer;
*contents_baton_p = tb;
return SVN_NO_ERROR;
}
struct text_baton_t
{
svn_fs_root_t *root;
const char *path;
dag_node_t *node;
svn_stream_t *stream;
svn_stream_t *file_stream;
svn_checksum_t *result_checksum;
apr_pool_t *pool;
};
static svn_error_t *
text_stream_writer(void *baton,
const char *data,
apr_size_t *len)
{
struct text_baton_t *tb = baton;
return svn_stream_write(tb->file_stream, data, len);
}
static svn_error_t *
text_stream_closer(void *baton)
{
struct text_baton_t *tb = baton;
SVN_ERR(svn_stream_close(tb->file_stream));
return svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum,
tb->pool);
}
static svn_error_t *
apply_text(void *baton, apr_pool_t *pool)
{
struct text_baton_t *tb = baton;
parent_path_t *parent_path;
const svn_fs_fs__id_part_t *txn_id = root_txn_id(tb->root);
SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, TRUE, pool));
if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
SVN_ERR(svn_fs_fs__allow_locked_operation(tb->path, tb->root->fs,
FALSE, FALSE, pool));
SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, pool));
tb->node = parent_path->node;
SVN_ERR(svn_fs_fs__dag_get_edit_stream(&(tb->file_stream), tb->node,
tb->pool));
tb->stream = svn_stream_create(tb, tb->pool);
svn_stream_set_write(tb->stream, text_stream_writer);
svn_stream_set_close(tb->stream, text_stream_closer);
return add_change(tb->root->fs, txn_id, tb->path,
svn_fs_fs__dag_get_id(tb->node),
svn_fs_path_change_modify, TRUE, FALSE, FALSE,
svn_node_file, SVN_INVALID_REVNUM, NULL, pool);
}
static svn_error_t *
fs_apply_text(svn_stream_t **contents_p,
svn_fs_root_t *root,
const char *path,
svn_checksum_t *result_checksum,
apr_pool_t *pool)
{
struct text_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
tb->root = root;
tb->path = svn_fs__canonicalize_abspath(path, pool);
tb->pool = pool;
tb->result_checksum = svn_checksum_dup(result_checksum, pool);
SVN_ERR(apply_text(tb, pool));
*contents_p = tb->stream;
return SVN_NO_ERROR;
}
static svn_error_t *
fs_contents_changed(svn_boolean_t *changed_p,
svn_fs_root_t *root1,
const char *path1,
svn_fs_root_t *root2,
const char *path2,
svn_boolean_t strict,
apr_pool_t *pool)
{
dag_node_t *node1, *node2;
if (root1->fs != root2->fs)
return svn_error_create
(SVN_ERR_FS_GENERAL, NULL,
_("Cannot compare file contents between two different filesystems"));
SVN_ERR(get_dag(&node1, root1, path1, pool));
if (svn_fs_fs__dag_node_kind(node1) != svn_node_file)
return svn_error_createf
(SVN_ERR_FS_NOT_FILE, NULL, _("'%s' is not a file"), path1);
SVN_ERR(get_dag(&node2, root2, path2, pool));
if (svn_fs_fs__dag_node_kind(node2) != svn_node_file)
return svn_error_createf
(SVN_ERR_FS_NOT_FILE, NULL, _("'%s' is not a file"), path2);
return svn_fs_fs__dag_things_different(NULL, changed_p,
node1, node2, strict, pool);
}
static svn_error_t *
fs_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
svn_fs_root_t *source_root,
const char *source_path,
svn_fs_root_t *target_root,
const char *target_path,
apr_pool_t *pool)
{
dag_node_t *source_node, *target_node;
if (source_root && source_path)
SVN_ERR(get_dag(&source_node, source_root, source_path, pool));
else
source_node = NULL;
SVN_ERR(get_dag(&target_node, target_root, target_path, pool));
return svn_fs_fs__dag_get_file_delta_stream(stream_p, source_node,
target_node, pool);
}
static svn_error_t *
fs_paths_changed(apr_hash_t **changed_paths_p,
svn_fs_root_t *root,
apr_pool_t *pool)
{
if (root->is_txn_root)
return svn_fs_fs__txn_changes_fetch(changed_paths_p, root->fs,
root_txn_id(root), pool);
else
return svn_fs_fs__paths_changed(changed_paths_p, root->fs, root->rev,
pool);
}
static void
convert_path_change(svn_fs_path_change3_t *output,
const char *path,
size_t path_len,
svn_fs_path_change2_t *entry)
{
output->path.data = path;
output->path.len = path_len;
output->change_kind = entry->change_kind;
output->node_kind = entry->node_kind;
output->text_mod = entry->text_mod;
output->prop_mod = entry->prop_mod;
output->mergeinfo_mod = entry->mergeinfo_mod;
output->copyfrom_known = entry->copyfrom_known;
output->copyfrom_rev = entry->copyfrom_rev;
output->copyfrom_path = entry->copyfrom_path;
}
typedef struct fs_txn_changes_iterator_data_t
{
apr_hash_index_t *hi;
svn_fs_path_change3_t change;
} fs_txn_changes_iterator_data_t;
static svn_error_t *
fs_txn_changes_iterator_get(svn_fs_path_change3_t **change,
svn_fs_path_change_iterator_t *iterator)
{
fs_txn_changes_iterator_data_t *data = iterator->fsap_data;
if (data->hi)
{
const void *key;
apr_ssize_t length;
void *value;
apr_hash_this(data->hi, &key, &length, &value);
convert_path_change(&data->change, key, length, value);
*change = &data->change;
data->hi = apr_hash_next(data->hi);
}
else
{
*change = NULL;
}
return SVN_NO_ERROR;
}
static changes_iterator_vtable_t txn_changes_iterator_vtable =
{
fs_txn_changes_iterator_get
};
typedef struct fs_revision_changes_iterator_data_t
{
svn_fs_fs__changes_context_t *context;
apr_array_header_t *changes;
int idx;
svn_fs_path_change3_t change;
apr_pool_t *scratch_pool;
} fs_revision_changes_iterator_data_t;
static svn_error_t *
fs_revision_changes_iterator_get(svn_fs_path_change3_t **change,
svn_fs_path_change_iterator_t *iterator)
{
fs_revision_changes_iterator_data_t *data = iterator->fsap_data;
if ((data->idx >= data->changes->nelts) && !data->context->eol)
{
apr_pool_t *changes_pool = data->changes->pool;
svn_pool_clear(changes_pool);
SVN_ERR(svn_fs_fs__get_changes(&data->changes, data->context,
changes_pool, data->scratch_pool));
data->idx = 0;
svn_pool_clear(data->scratch_pool);
}
if (data->idx < data->changes->nelts)
{
change_t *entry = APR_ARRAY_IDX(data->changes, data->idx, change_t *);
convert_path_change(&data->change, entry->path.data, entry->path.len,
&entry->info);
*change = &data->change;
++data->idx;
}
else
{
*change = NULL;
}
return SVN_NO_ERROR;
}
static changes_iterator_vtable_t rev_changes_iterator_vtable =
{
fs_revision_changes_iterator_get
};
static svn_error_t *
fs_report_changes(svn_fs_path_change_iterator_t **iterator,
svn_fs_root_t *root,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_fs_path_change_iterator_t *result = apr_pcalloc(result_pool,
sizeof(*result));
if (root->is_txn_root)
{
fs_txn_changes_iterator_data_t *data = apr_pcalloc(result_pool,
sizeof(*data));
apr_hash_t *changed_paths;
SVN_ERR(svn_fs_fs__txn_changes_fetch(&changed_paths, root->fs,
root_txn_id(root), result_pool));
data->hi = apr_hash_first(result_pool, changed_paths);
result->fsap_data = data;
result->vtable = &txn_changes_iterator_vtable;
}
else
{
apr_pool_t *changes_pool = svn_pool_create(result_pool);
fs_revision_changes_iterator_data_t *data = apr_pcalloc(result_pool,
sizeof(*data));
data->scratch_pool = svn_pool_create(result_pool);
SVN_ERR(svn_fs_fs__create_changes_context(&data->context,
root->fs, root->rev,
result_pool));
SVN_ERR(svn_fs_fs__get_changes(&data->changes, data->context,
changes_pool, scratch_pool));
result->fsap_data = data;
result->vtable = &rev_changes_iterator_vtable;
}
*iterator = result;
return SVN_NO_ERROR;
}
typedef struct fs_history_data_t
{
svn_fs_t *fs;
const char *path;
svn_revnum_t revision;
const char *path_hint;
svn_revnum_t rev_hint;
svn_boolean_t is_interesting;
svn_revnum_t next_copy;
const svn_fs_id_t *current_id;
} fs_history_data_t;
static svn_fs_history_t *
assemble_history(svn_fs_t *fs,
const char *path,
svn_revnum_t revision,
svn_boolean_t is_interesting,
const char *path_hint,
svn_revnum_t rev_hint,
svn_revnum_t next_copy,
const svn_fs_id_t *current_id,
apr_pool_t *pool);
static svn_error_t *
fs_node_history(svn_fs_history_t **history_p,
svn_fs_root_t *root,
const char *path,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_node_kind_t kind;
if (root->is_txn_root)
return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
SVN_ERR(svn_fs_fs__check_path(&kind, root, path, scratch_pool));
if (kind == svn_node_none)
return SVN_FS__NOT_FOUND(root, path);
*history_p = assemble_history(root->fs, path, root->rev, FALSE, NULL,
SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
NULL, result_pool);
return SVN_NO_ERROR;
}
static svn_error_t *
find_youngest_copyroot(svn_revnum_t *rev_p,
const char **path_p,
svn_fs_t *fs,
parent_path_t *parent_path,
apr_pool_t *pool)
{
svn_revnum_t rev_mine;
svn_revnum_t rev_parent = SVN_INVALID_REVNUM;
const char *path_mine;
const char *path_parent = NULL;
if (parent_path->parent)
SVN_ERR(find_youngest_copyroot(&rev_parent, &path_parent, fs,
parent_path->parent, pool));
SVN_ERR(svn_fs_fs__dag_get_copyroot(&rev_mine, &path_mine,
parent_path->node));
if (rev_mine >= rev_parent)
{
*rev_p = rev_mine;
*path_p = path_mine;
}
else
{
*rev_p = rev_parent;
*path_p = path_parent;
}
return SVN_NO_ERROR;
}
static svn_error_t *fs_closest_copy(svn_fs_root_t **root_p,
const char **path_p,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
svn_fs_t *fs = root->fs;
parent_path_t *parent_path, *copy_dst_parent_path;
svn_revnum_t copy_dst_rev, created_rev;
const char *copy_dst_path;
svn_fs_root_t *copy_dst_root;
dag_node_t *copy_dst_node;
*root_p = NULL;
*path_p = NULL;
path = svn_fs__canonicalize_abspath(path, pool);
SVN_ERR(open_path(&parent_path, root, path, 0, FALSE, pool));
SVN_ERR(find_youngest_copyroot(©_dst_rev, ©_dst_path,
fs, parent_path, pool));
if (copy_dst_rev == 0)
return SVN_NO_ERROR;
SVN_ERR(svn_fs_fs__revision_root(©_dst_root, fs, copy_dst_rev, pool));
SVN_ERR(open_path(©_dst_parent_path, copy_dst_root, path,
open_path_node_only | open_path_allow_null, FALSE, pool));
if (copy_dst_parent_path == NULL)
return SVN_NO_ERROR;
copy_dst_node = copy_dst_parent_path->node;
if (! svn_fs_fs__id_check_related(svn_fs_fs__dag_get_id(copy_dst_node),
svn_fs_fs__dag_get_id(parent_path->node)))
return SVN_NO_ERROR;
SVN_ERR(svn_fs_fs__dag_get_revision(&created_rev, copy_dst_node, pool));
if (created_rev == copy_dst_rev)
{
const svn_fs_id_t *pred;
SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred, copy_dst_node));
if (! pred)
return SVN_NO_ERROR;
}
*root_p = copy_dst_root;
*path_p = copy_dst_path;
return SVN_NO_ERROR;
}
static svn_error_t *
prev_location(const char **prev_path,
svn_revnum_t *prev_rev,
svn_fs_t *fs,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
const char *copy_path, *copy_src_path, *remainder_path;
svn_fs_root_t *copy_root;
svn_revnum_t copy_src_rev;
SVN_ERR(fs_closest_copy(©_root, ©_path, root, path, pool));
if (! copy_root)
{
*prev_rev = SVN_INVALID_REVNUM;
*prev_path = NULL;
return SVN_NO_ERROR;
}
SVN_ERR(fs_copied_from(©_src_rev, ©_src_path,
copy_root, copy_path, pool));
remainder_path = svn_fspath__skip_ancestor(copy_path, path);
*prev_path = svn_fspath__join(copy_src_path, remainder_path, pool);
*prev_rev = copy_src_rev;
return SVN_NO_ERROR;
}
static svn_error_t *
fs_node_origin_rev(svn_revnum_t *revision,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
svn_fs_t *fs = root->fs;
const svn_fs_id_t *given_noderev_id, *cached_origin_id;
const svn_fs_fs__id_part_t *node_id;
path = svn_fs__canonicalize_abspath(path, pool);
SVN_ERR(svn_fs_fs__node_id(&given_noderev_id, root, path, pool));
node_id = svn_fs_fs__id_node_id(given_noderev_id);
if (node_id->revision != 0 || node_id->number == 0)
{
*revision = node_id->revision;
return SVN_NO_ERROR;
}
SVN_ERR(svn_fs_fs__get_node_origin(&cached_origin_id,
fs,
node_id,
pool));
if (cached_origin_id != NULL)
{
*revision = svn_fs_fs__id_rev(cached_origin_id);
return SVN_NO_ERROR;
}
{
svn_fs_root_t *curroot = root;
apr_pool_t *subpool = svn_pool_create(pool);
apr_pool_t *predidpool = svn_pool_create(pool);
svn_stringbuf_t *lastpath = svn_stringbuf_create(path, pool);
svn_revnum_t lastrev = SVN_INVALID_REVNUM;
dag_node_t *node;
const svn_fs_id_t *pred_id;
while (1)
{
svn_revnum_t currev;
const char *curpath = lastpath->data;
svn_pool_clear(subpool);
if (SVN_IS_VALID_REVNUM(lastrev))
SVN_ERR(svn_fs_fs__revision_root(&curroot, fs, lastrev, subpool));
SVN_ERR(prev_location(&curpath, &currev, fs, curroot, curpath,
subpool));
if (! curpath)
break;
svn_stringbuf_set(lastpath, curpath);
lastrev = currev;
}
SVN_ERR(svn_fs_fs__node_id(&pred_id, curroot, lastpath->data, predidpool));
do
{
svn_pool_clear(subpool);
SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, subpool));
svn_pool_clear(predidpool);
SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
pred_id = pred_id ? svn_fs_fs__id_copy(pred_id, predidpool) : NULL;
}
while (pred_id);
SVN_ERR(svn_fs_fs__dag_get_revision(revision, node, pool));
if (node_id->revision != SVN_INVALID_REVNUM)
SVN_ERR(svn_fs_fs__set_node_origin(fs, node_id,
svn_fs_fs__dag_get_id(node), pool));
svn_pool_destroy(subpool);
svn_pool_destroy(predidpool);
return SVN_NO_ERROR;
}
}
static svn_error_t *
history_prev(svn_fs_history_t **prev_history,
svn_fs_history_t *history,
svn_boolean_t cross_copies,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
fs_history_data_t *fhd = history->fsap_data;
const char *commit_path, *src_path, *path = fhd->path;
svn_revnum_t commit_rev, src_rev, dst_rev;
svn_revnum_t revision = fhd->revision;
svn_fs_t *fs = fhd->fs;
parent_path_t *parent_path;
dag_node_t *node;
svn_fs_root_t *root;
svn_boolean_t reported = fhd->is_interesting;
svn_revnum_t copyroot_rev;
const char *copyroot_path;
const svn_fs_id_t *pred_id = NULL;
*prev_history = NULL;
if ( SVN_IS_VALID_REVNUM(fhd->next_copy)
&& revision > fhd->next_copy
&& fhd->current_id)
{
node_revision_t *noderev;
assert(reported);
SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, fhd->current_id,
scratch_pool, scratch_pool));
if (! noderev->predecessor_id)
return SVN_NO_ERROR;
commit_rev = svn_fs_fs__id_rev(noderev->predecessor_id);
if (commit_rev > fhd->next_copy)
{
*prev_history = assemble_history(fs, noderev->created_path,
commit_rev, TRUE, NULL,
SVN_INVALID_REVNUM,
fhd->next_copy,
noderev->predecessor_id,
result_pool);
return SVN_NO_ERROR;
}
}
if (fhd->path_hint && SVN_IS_VALID_REVNUM(fhd->rev_hint))
{
reported = FALSE;
if (! cross_copies)
return SVN_NO_ERROR;
path = fhd->path_hint;
revision = fhd->rev_hint;
}
SVN_ERR(svn_fs_fs__revision_root(&root, fs, revision, scratch_pool));
SVN_ERR(open_path(&parent_path, root, path, 0, FALSE, scratch_pool));
node = parent_path->node;
commit_path = svn_fs_fs__dag_get_created_path(node);
SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, scratch_pool));
if (revision == commit_rev)
{
if (! reported)
{
*prev_history = assemble_history(fs, commit_path,
commit_rev, TRUE, NULL,
SVN_INVALID_REVNUM,
SVN_INVALID_REVNUM, NULL,
result_pool);
return SVN_NO_ERROR;
}
else
{
SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
if (! pred_id)
return SVN_NO_ERROR;
SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, scratch_pool));
commit_path = svn_fs_fs__dag_get_created_path(node);
SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, scratch_pool));
}
}
SVN_ERR(find_youngest_copyroot(©root_rev, ©root_path, fs,
parent_path, scratch_pool));
src_path = NULL;
src_rev = SVN_INVALID_REVNUM;
dst_rev = SVN_INVALID_REVNUM;
if (copyroot_rev > commit_rev)
{
const char *remainder_path;
const char *copy_dst, *copy_src;
svn_fs_root_t *copyroot_root;
SVN_ERR(svn_fs_fs__revision_root(©root_root, fs, copyroot_rev,
scratch_pool));
SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, scratch_pool));
copy_dst = svn_fs_fs__dag_get_created_path(node);
remainder_path = svn_fspath__skip_ancestor(copy_dst, path);
if (remainder_path)
{
SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(&src_rev, node));
SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(©_src, node));
dst_rev = copyroot_rev;
src_path = svn_fspath__join(copy_src, remainder_path, scratch_pool);
}
}
if (src_path && SVN_IS_VALID_REVNUM(src_rev))
{
svn_boolean_t retry = FALSE;
if ((dst_rev == revision) && reported)
retry = TRUE;
*prev_history = assemble_history(fs, path, dst_rev, ! retry,
src_path, src_rev,
SVN_INVALID_REVNUM, NULL,
result_pool);
}
else
{
*prev_history = assemble_history(fs, commit_path, commit_rev, TRUE,
NULL, SVN_INVALID_REVNUM,
copyroot_rev, pred_id, result_pool);
}
return SVN_NO_ERROR;
}
static svn_error_t *
fs_history_prev(svn_fs_history_t **prev_history_p,
svn_fs_history_t *history,
svn_boolean_t cross_copies,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_fs_history_t *prev_history = NULL;
fs_history_data_t *fhd = history->fsap_data;
svn_fs_t *fs = fhd->fs;
if (strcmp(fhd->path, "/") == 0)
{
if (! fhd->is_interesting)
prev_history = assemble_history(fs, "/", fhd->revision,
1, NULL, SVN_INVALID_REVNUM,
SVN_INVALID_REVNUM, NULL,
result_pool);
else if (fhd->revision > 0)
prev_history = assemble_history(fs, "/", fhd->revision - 1,
1, NULL, SVN_INVALID_REVNUM,
SVN_INVALID_REVNUM, NULL,
result_pool);
}
else
{
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
prev_history = history;
while (1)
{
svn_pool_clear(iterpool);
SVN_ERR(history_prev(&prev_history, prev_history, cross_copies,
result_pool, iterpool));
if (! prev_history)
break;
fhd = prev_history->fsap_data;
if (fhd->is_interesting)
break;
}
svn_pool_destroy(iterpool);
}
*prev_history_p = prev_history;
return SVN_NO_ERROR;
}
static svn_error_t *
fs_history_location(const char **path,
svn_revnum_t *revision,
svn_fs_history_t *history,
apr_pool_t *pool)
{
fs_history_data_t *fhd = history->fsap_data;
*path = apr_pstrdup(pool, fhd->path);
*revision = fhd->revision;
return SVN_NO_ERROR;
}
static history_vtable_t history_vtable = {
fs_history_prev,
fs_history_location
};
static svn_fs_history_t *
assemble_history(svn_fs_t *fs,
const char *path,
svn_revnum_t revision,
svn_boolean_t is_interesting,
const char *path_hint,
svn_revnum_t rev_hint,
svn_revnum_t next_copy,
const svn_fs_id_t *current_id,
apr_pool_t *pool)
{
svn_fs_history_t *history = apr_pcalloc(pool, sizeof(*history));
fs_history_data_t *fhd = apr_pcalloc(pool, sizeof(*fhd));
fhd->path = svn_fs__canonicalize_abspath(path, pool);
fhd->revision = revision;
fhd->is_interesting = is_interesting;
fhd->path_hint = path_hint ? svn_fs__canonicalize_abspath(path_hint, pool)
: NULL;
fhd->rev_hint = rev_hint;
fhd->next_copy = next_copy;
fhd->current_id = current_id ? svn_fs_fs__id_copy(current_id, pool) : NULL;
fhd->fs = fs;
history->vtable = &history_vtable;
history->fsap_data = fhd;
return history;
}
static svn_error_t *
crawl_directory_dag_for_mergeinfo(svn_fs_root_t *root,
const char *this_path,
dag_node_t *dir_dag,
svn_fs_mergeinfo_receiver_t receiver,
void *baton,
apr_pool_t *scratch_pool)
{
apr_array_header_t *entries;
int i;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, dir_dag, scratch_pool));
for (i = 0; i < entries->nelts; ++i)
{
svn_fs_dirent_t *dirent = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *);
const char *kid_path;
dag_node_t *kid_dag;
svn_boolean_t has_mergeinfo, go_down;
svn_pool_clear(iterpool);
kid_path = svn_fspath__join(this_path, dirent->name, iterpool);
SVN_ERR(get_dag(&kid_dag, root, kid_path, iterpool));
SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, kid_dag));
SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down, kid_dag));
if (has_mergeinfo)
{
apr_hash_t *proplist;
svn_mergeinfo_t kid_mergeinfo;
svn_string_t *mergeinfo_string;
svn_error_t *err;
SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, kid_dag, iterpool));
mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
if (!mergeinfo_string)
{
svn_string_t *idstr = svn_fs_fs__id_unparse(dirent->id, iterpool);
return svn_error_createf
(SVN_ERR_FS_CORRUPT, NULL,
_("Node-revision #'%s' claims to have mergeinfo but doesn't"),
idstr->data);
}
err = svn_mergeinfo_parse(&kid_mergeinfo,
mergeinfo_string->data,
iterpool);
if (err)
{
if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
svn_error_clear(err);
else
return svn_error_trace(err);
}
else
{
SVN_ERR(receiver(kid_path, kid_mergeinfo, baton, iterpool));
}
}
if (go_down)
SVN_ERR(crawl_directory_dag_for_mergeinfo(root,
kid_path,
kid_dag,
receiver,
baton,
iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static const char *
mergeinfo_cache_key(const char *path,
svn_fs_root_t *rev_root,
svn_mergeinfo_inheritance_t inherit,
svn_boolean_t adjust_inherited_mergeinfo,
apr_pool_t *pool)
{
apr_int64_t number = rev_root->rev;
number = number * 4
+ (inherit == svn_mergeinfo_nearest_ancestor ? 2 : 0)
+ (adjust_inherited_mergeinfo ? 1 : 0);
return svn_fs_fs__combine_number_and_string(number, path, pool);
}
static svn_error_t *
get_mergeinfo_for_path_internal(svn_mergeinfo_t *mergeinfo,
svn_fs_root_t *rev_root,
const char *path,
svn_mergeinfo_inheritance_t inherit,
svn_boolean_t adjust_inherited_mergeinfo,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
parent_path_t *parent_path, *nearest_ancestor;
apr_hash_t *proplist;
svn_string_t *mergeinfo_string;
path = svn_fs__canonicalize_abspath(path, scratch_pool);
SVN_ERR(open_path(&parent_path, rev_root, path, 0, FALSE, scratch_pool));
if (inherit == svn_mergeinfo_nearest_ancestor && ! parent_path->parent)
return SVN_NO_ERROR;
if (inherit == svn_mergeinfo_nearest_ancestor)
nearest_ancestor = parent_path->parent;
else
nearest_ancestor = parent_path;
while (TRUE)
{
svn_boolean_t has_mergeinfo;
SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo,
nearest_ancestor->node));
if (has_mergeinfo)
break;
if (inherit == svn_mergeinfo_explicit)
{
return SVN_NO_ERROR;
}
nearest_ancestor = nearest_ancestor->parent;
if (!nearest_ancestor)
{
return SVN_NO_ERROR;
}
}
SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, nearest_ancestor->node,
scratch_pool));
mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
if (!mergeinfo_string)
return svn_error_createf
(SVN_ERR_FS_CORRUPT, NULL,
_("Node-revision '%s@%ld' claims to have mergeinfo but doesn't"),
parent_path_path(nearest_ancestor, scratch_pool), rev_root->rev);
{
svn_error_t *err = svn_mergeinfo_parse(mergeinfo,
mergeinfo_string->data,
result_pool);
if (err)
{
if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
{
svn_error_clear(err);
err = NULL;
*mergeinfo = NULL;
}
return svn_error_trace(err);
}
}
if (adjust_inherited_mergeinfo && (nearest_ancestor != parent_path))
{
svn_mergeinfo_t tmp_mergeinfo;
SVN_ERR(svn_mergeinfo_inheritable2(&tmp_mergeinfo, *mergeinfo,
NULL, SVN_INVALID_REVNUM,
SVN_INVALID_REVNUM, TRUE,
scratch_pool, scratch_pool));
SVN_ERR(svn_fs__append_to_merged_froms(mergeinfo, tmp_mergeinfo,
parent_path_relpath(
parent_path, nearest_ancestor,
scratch_pool),
result_pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *
get_mergeinfo_for_path(svn_mergeinfo_t *mergeinfo,
svn_fs_root_t *rev_root,
const char *path,
svn_mergeinfo_inheritance_t inherit,
svn_boolean_t adjust_inherited_mergeinfo,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
fs_fs_data_t *ffd = rev_root->fs->fsap_data;
const char *cache_key;
svn_boolean_t found = FALSE;
svn_stringbuf_t *mergeinfo_exists;
*mergeinfo = NULL;
cache_key = mergeinfo_cache_key(path, rev_root, inherit,
adjust_inherited_mergeinfo, scratch_pool);
if (ffd->mergeinfo_existence_cache)
{
SVN_ERR(svn_cache__get((void **)&mergeinfo_exists, &found,
ffd->mergeinfo_existence_cache,
cache_key, result_pool));
if (found && mergeinfo_exists->data[0] == '1')
SVN_ERR(svn_cache__get((void **)mergeinfo, &found,
ffd->mergeinfo_cache,
cache_key, result_pool));
}
if (! found)
{
SVN_ERR(get_mergeinfo_for_path_internal(mergeinfo, rev_root, path,
inherit,
adjust_inherited_mergeinfo,
result_pool, scratch_pool));
if (ffd->mergeinfo_existence_cache)
{
mergeinfo_exists = svn_stringbuf_create(*mergeinfo ? "1" : "0",
scratch_pool);
SVN_ERR(svn_cache__set(ffd->mergeinfo_existence_cache,
cache_key, mergeinfo_exists, scratch_pool));
if (*mergeinfo)
SVN_ERR(svn_cache__set(ffd->mergeinfo_cache,
cache_key, *mergeinfo, scratch_pool));
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
add_descendant_mergeinfo(svn_fs_root_t *root,
const char *path,
svn_fs_mergeinfo_receiver_t receiver,
void *baton,
apr_pool_t *scratch_pool)
{
dag_node_t *this_dag;
svn_boolean_t go_down;
SVN_ERR(get_dag(&this_dag, root, path, scratch_pool));
SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down,
this_dag));
if (go_down)
SVN_ERR(crawl_directory_dag_for_mergeinfo(root,
path,
this_dag,
receiver,
baton,
scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
get_mergeinfos_for_paths(svn_fs_root_t *root,
const apr_array_header_t *paths,
svn_mergeinfo_inheritance_t inherit,
svn_boolean_t include_descendants,
svn_boolean_t adjust_inherited_mergeinfo,
svn_fs_mergeinfo_receiver_t receiver,
void *baton,
apr_pool_t *scratch_pool)
{
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
int i;
for (i = 0; i < paths->nelts; i++)
{
svn_error_t *err;
svn_mergeinfo_t path_mergeinfo;
const char *path = APR_ARRAY_IDX(paths, i, const char *);
svn_pool_clear(iterpool);
err = get_mergeinfo_for_path(&path_mergeinfo, root, path,
inherit, adjust_inherited_mergeinfo,
iterpool, iterpool);
if (err)
{
if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
{
svn_error_clear(err);
err = NULL;
path_mergeinfo = NULL;
}
else
{
return svn_error_trace(err);
}
}
if (path_mergeinfo)
SVN_ERR(receiver(path, path_mergeinfo, baton, iterpool));
if (include_descendants)
SVN_ERR(add_descendant_mergeinfo(root, path, receiver, baton,
iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
fs_get_mergeinfo(svn_fs_root_t *root,
const apr_array_header_t *paths,
svn_mergeinfo_inheritance_t inherit,
svn_boolean_t include_descendants,
svn_boolean_t adjust_inherited_mergeinfo,
svn_fs_mergeinfo_receiver_t receiver,
void *baton,
apr_pool_t *scratch_pool)
{
fs_fs_data_t *ffd = root->fs->fsap_data;
if (root->is_txn_root)
return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
if (! svn_fs_fs__fs_supports_mergeinfo(root->fs))
return svn_error_createf
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Querying mergeinfo requires version %d of the FSFS filesystem "
"schema; filesystem '%s' uses only version %d"),
SVN_FS_FS__MIN_MERGEINFO_FORMAT, root->fs->path, ffd->format);
return get_mergeinfos_for_paths(root, paths, inherit,
include_descendants,
adjust_inherited_mergeinfo,
receiver, baton,
scratch_pool);
}
static root_vtable_t root_vtable = {
fs_paths_changed,
fs_report_changes,
svn_fs_fs__check_path,
fs_node_history,
svn_fs_fs__node_id,
fs_node_relation,
svn_fs_fs__node_created_rev,
fs_node_origin_rev,
fs_node_created_path,
fs_delete_node,
fs_copy,
fs_revision_link,
fs_copied_from,
fs_closest_copy,
fs_node_prop,
fs_node_proplist,
fs_node_has_props,
fs_change_node_prop,
fs_props_changed,
fs_dir_entries,
fs_dir_optimal_order,
fs_make_dir,
fs_file_length,
fs_file_checksum,
fs_file_contents,
fs_try_process_file_contents,
fs_make_file,
fs_apply_textdelta,
fs_apply_text,
fs_contents_changed,
fs_get_file_delta_stream,
fs_merge,
fs_get_mergeinfo,
};
static svn_fs_root_t *
make_root(svn_fs_t *fs,
apr_pool_t *pool)
{
svn_fs_root_t *root = apr_pcalloc(pool, sizeof(*root));
root->fs = fs;
root->pool = pool;
root->vtable = &root_vtable;
return root;
}
static svn_fs_root_t *
make_revision_root(svn_fs_t *fs,
svn_revnum_t rev,
dag_node_t *root_dir,
apr_pool_t *pool)
{
svn_fs_root_t *root = make_root(fs, pool);
root->is_txn_root = FALSE;
root->rev = rev;
root->fsap_data = root_dir;
return root;
}
static svn_error_t *
make_txn_root(svn_fs_root_t **root_p,
svn_fs_t *fs,
const svn_fs_fs__id_part_t *txn,
svn_revnum_t base_rev,
apr_uint32_t flags,
apr_pool_t *pool)
{
svn_fs_root_t *root = make_root(fs, pool);
fs_txn_root_data_t *frd = apr_pcalloc(root->pool, sizeof(*frd));
frd->txn_id = *txn;
root->is_txn_root = TRUE;
root->txn = svn_fs_fs__id_txn_unparse(txn, root->pool);
root->txn_flags = flags;
root->rev = base_rev;
SVN_ERR(svn_cache__create_inprocess(&(frd->txn_node_cache),
svn_fs_fs__dag_serialize,
svn_fs_fs__dag_deserialize,
APR_HASH_KEY_STRING,
32, 20, FALSE,
apr_pstrcat(pool, root->txn, ":TXN",
SVN_VA_NULL),
root->pool));
SVN_ERR(svn_fs_fs__initialize_txn_caches(fs, root->txn, root->pool));
root->fsap_data = frd;
*root_p = root;
return SVN_NO_ERROR;
}
static const char *
stringify_node(dag_node_t *node,
apr_pool_t *pool)
{
return svn_fs_fs__id_unparse(svn_fs_fs__dag_get_id(node), pool)->data;
}
static svn_error_t *
verify_node(dag_node_t *node,
svn_revnum_t rev,
apr_array_header_t *parent_nodes,
apr_pool_t *pool)
{
svn_boolean_t has_mergeinfo;
apr_int64_t mergeinfo_count;
const svn_fs_id_t *pred_id;
svn_fs_t *fs = svn_fs_fs__dag_get_fs(node);
int pred_count;
svn_node_kind_t kind;
apr_pool_t *iterpool = svn_pool_create(pool);
int i;
for (i = 0; i < parent_nodes->nelts; ++i)
{
dag_node_t *parent = APR_ARRAY_IDX(parent_nodes, i, dag_node_t *);
if (svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(parent),
svn_fs_fs__dag_get_id(node)))
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
"Node is its own direct or indirect parent '%s'",
stringify_node(node, iterpool));
}
SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, node));
SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_count, node));
SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
SVN_ERR(svn_fs_fs__dag_get_predecessor_count(&pred_count, node));
kind = svn_fs_fs__dag_node_kind(node);
if (mergeinfo_count < 0)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
"Negative mergeinfo-count %" APR_INT64_T_FMT
" on node '%s'",
mergeinfo_count, stringify_node(node, iterpool));
if (pred_id)
{
dag_node_t *pred;
int pred_pred_count;
SVN_ERR(svn_fs_fs__dag_get_node(&pred, fs, pred_id, iterpool));
SVN_ERR(svn_fs_fs__dag_get_predecessor_count(&pred_pred_count, pred));
if (pred_pred_count+1 != pred_count)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
"Predecessor count mismatch: "
"%s has %d, but %s has %d",
stringify_node(node, iterpool), pred_count,
stringify_node(pred, iterpool),
pred_pred_count);
}
if (kind == svn_node_none)
{
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
"Node '%s' has kind 'none'",
stringify_node(node, iterpool));
}
if (kind == svn_node_file)
{
if (has_mergeinfo != mergeinfo_count)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
"File node '%s' has inconsistent mergeinfo: "
"has_mergeinfo=%d, "
"mergeinfo_count=%" APR_INT64_T_FMT,
stringify_node(node, iterpool),
has_mergeinfo, mergeinfo_count);
}
if (kind == svn_node_dir)
{
apr_array_header_t *entries;
apr_int64_t children_mergeinfo = 0;
APR_ARRAY_PUSH(parent_nodes, dag_node_t*) = node;
SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool));
for (i = 0; i < entries->nelts; ++i)
{
svn_fs_dirent_t *dirent
= APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *);
dag_node_t *child;
apr_int64_t child_mergeinfo;
svn_pool_clear(iterpool);
if (svn_fs_fs__id_rev(dirent->id) == rev)
{
SVN_ERR(svn_fs_fs__dag_get_node(&child, fs, dirent->id,
iterpool));
SVN_ERR(verify_node(child, rev, parent_nodes, iterpool));
SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&child_mergeinfo,
child));
}
else
{
node_revision_t *noderev;
SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, dirent->id,
iterpool, iterpool));
child_mergeinfo = noderev->mergeinfo_count;
}
children_mergeinfo += child_mergeinfo;
}
if (children_mergeinfo+has_mergeinfo != mergeinfo_count)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
"Mergeinfo-count discrepancy on '%s': "
"expected %" APR_INT64_T_FMT "+%d, "
"counted %" APR_INT64_T_FMT,
stringify_node(node, iterpool),
mergeinfo_count, has_mergeinfo,
children_mergeinfo);
apr_array_pop(parent_nodes);
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__verify_root(svn_fs_root_t *root,
apr_pool_t *pool)
{
svn_fs_t *fs = root->fs;
dag_node_t *root_dir;
apr_array_header_t *parent_nodes;
if (root->is_txn_root)
{
fs_txn_root_data_t *frd = root->fsap_data;
SVN_ERR(svn_fs_fs__dag_txn_root(&root_dir, fs, &frd->txn_id, pool));
}
else
{
root_dir = root->fsap_data;
}
parent_nodes = apr_array_make(pool, 16, sizeof(dag_node_t *));
SVN_ERR(verify_node(root_dir, root->rev, parent_nodes, pool));
{
const svn_fs_id_t *pred_id;
SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, root_dir));
if (! root->is_txn_root && !!pred_id != !!root->rev)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
"r%ld's root node's predecessor is "
"unexpectedly '%s'",
root->rev,
(pred_id
? svn_fs_fs__id_unparse(pred_id, pool)->data
: "(null)"));
if (root->is_txn_root && !pred_id)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
"Transaction '%s''s root node's predecessor is "
"unexpectedly NULL",
root->txn);
if (pred_id)
{
svn_revnum_t pred_rev = svn_fs_fs__id_rev(pred_id);
if (! root->is_txn_root && pred_rev+1 != root->rev)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
"r%ld's root node's predecessor is r%ld"
" but should be r%ld",
root->rev, pred_rev, root->rev - 1);
if (root->is_txn_root && pred_rev != root->rev)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
"Transaction '%s''s root node's predecessor"
" is r%ld"
" but should be r%ld",
root->txn, pred_rev, root->rev);
}
}
return SVN_NO_ERROR;
}