#include <string.h>
#include "svn_path.h"
#include "svn_error.h"
#include "svn_fs.h"
#include "svn_props.h"
#include "svn_pools.h"
#include "dag.h"
#include "err.h"
#include "fs.h"
#include "key-gen.h"
#include "fs_fs.h"
#include "id.h"
#include "../libsvn_fs/fs-loader.h"
#include "svn_private_config.h"
struct dag_node_t
{
svn_fs_t *fs;
svn_fs_id_t *id;
const svn_fs_id_t *fresh_root_predecessor_id;
svn_node_kind_t kind;
node_revision_t *node_revision;
const char *created_path;
};
svn_node_kind_t svn_fs_fs__dag_node_kind(dag_node_t *node)
{
return node->kind;
}
const svn_fs_id_t *
svn_fs_fs__dag_get_id(const dag_node_t *node)
{
return node->id;
}
const char *
svn_fs_fs__dag_get_created_path(dag_node_t *node)
{
return node->created_path;
}
svn_fs_t *
svn_fs_fs__dag_get_fs(dag_node_t *node)
{
return node->fs;
}
void
svn_fs_fs__dag_set_fs(dag_node_t *node, svn_fs_t *fs)
{
node->fs = fs;
}
static node_revision_t *
copy_node_revision(node_revision_t *noderev,
apr_pool_t *pool)
{
node_revision_t *nr = apr_pcalloc(pool, sizeof(*nr));
nr->kind = noderev->kind;
if (noderev->predecessor_id)
nr->predecessor_id = svn_fs_fs__id_copy(noderev->predecessor_id, pool);
nr->predecessor_count = noderev->predecessor_count;
if (noderev->copyfrom_path)
nr->copyfrom_path = apr_pstrdup(pool, noderev->copyfrom_path);
nr->copyfrom_rev = noderev->copyfrom_rev;
nr->copyroot_path = apr_pstrdup(pool, noderev->copyroot_path);
nr->copyroot_rev = noderev->copyroot_rev;
nr->predecessor_count = noderev->predecessor_count;
nr->data_rep = svn_fs_fs__rep_copy(noderev->data_rep, pool);
nr->prop_rep = svn_fs_fs__rep_copy(noderev->prop_rep, pool);
nr->mergeinfo_count = noderev->mergeinfo_count;
nr->has_mergeinfo = noderev->has_mergeinfo;
if (noderev->created_path)
nr->created_path = apr_pstrdup(pool, noderev->created_path);
return nr;
}
static svn_error_t *
get_node_revision(node_revision_t **noderev_p,
dag_node_t *node,
apr_pool_t *pool)
{
node_revision_t *noderev;
if (! node->node_revision)
{
SVN_ERR(svn_fs_fs__get_node_revision(&noderev, node->fs,
node->id, pool));
node->node_revision = noderev;
}
*noderev_p = node->node_revision;
return SVN_NO_ERROR;
}
svn_boolean_t svn_fs_fs__dag_check_mutable(const dag_node_t *node)
{
return (svn_fs_fs__id_txn_id(svn_fs_fs__dag_get_id(node)) != NULL);
}
svn_error_t *
svn_fs_fs__dag_get_node(dag_node_t **node,
svn_fs_t *fs,
const svn_fs_id_t *id,
apr_pool_t *pool)
{
dag_node_t *new_node;
node_revision_t *noderev;
new_node = apr_pcalloc(pool, sizeof(*new_node));
new_node->fs = fs;
new_node->id = svn_fs_fs__id_copy(id, pool);
SVN_ERR(get_node_revision(&noderev, new_node, pool));
new_node->kind = noderev->kind;
new_node->created_path = apr_pstrdup(pool, noderev->created_path);
if (noderev->is_fresh_txn_root)
new_node->fresh_root_predecessor_id = noderev->predecessor_id;
else
new_node->fresh_root_predecessor_id = NULL;
*node = new_node;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__dag_get_revision(svn_revnum_t *rev,
dag_node_t *node,
apr_pool_t *pool)
{
const svn_fs_id_t *correct_id = node->fresh_root_predecessor_id
? node->fresh_root_predecessor_id : node->id;
*rev = svn_fs_fs__id_rev(correct_id);
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__dag_get_predecessor_id(const svn_fs_id_t **id_p,
dag_node_t *node,
apr_pool_t *pool)
{
node_revision_t *noderev;
SVN_ERR(get_node_revision(&noderev, node, pool));
*id_p = noderev->predecessor_id;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__dag_get_predecessor_count(int *count,
dag_node_t *node,
apr_pool_t *pool)
{
node_revision_t *noderev;
SVN_ERR(get_node_revision(&noderev, node, pool));
*count = noderev->predecessor_count;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__dag_get_mergeinfo_count(apr_int64_t *count,
dag_node_t *node,
apr_pool_t *pool)
{
node_revision_t *noderev;
SVN_ERR(get_node_revision(&noderev, node, pool));
*count = noderev->mergeinfo_count;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__dag_has_mergeinfo(svn_boolean_t *has_mergeinfo,
dag_node_t *node,
apr_pool_t *pool)
{
node_revision_t *noderev;
SVN_ERR(get_node_revision(&noderev, node, pool));
*has_mergeinfo = noderev->has_mergeinfo;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__dag_has_descendants_with_mergeinfo(svn_boolean_t *do_they,
dag_node_t *node,
apr_pool_t *pool)
{
node_revision_t *noderev;
if (node->kind != svn_node_dir)
{
*do_they = FALSE;
return SVN_NO_ERROR;
}
SVN_ERR(get_node_revision(&noderev, node, pool));
if (noderev->mergeinfo_count > 1)
*do_they = TRUE;
else if (noderev->mergeinfo_count == 1 && !noderev->has_mergeinfo)
*do_they = TRUE;
else
*do_they = FALSE;
return SVN_NO_ERROR;
}
static svn_error_t *
dir_entry_id_from_node(const svn_fs_id_t **id_p,
dag_node_t *parent,
const char *name,
apr_pool_t *pool)
{
apr_hash_t *entries;
svn_fs_dirent_t *dirent;
apr_pool_t *subpool = svn_pool_create(pool);
SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, parent, subpool, pool));
if (entries)
dirent = apr_hash_get(entries, name, APR_HASH_KEY_STRING);
else
dirent = NULL;
*id_p = dirent ? svn_fs_fs__id_copy(dirent->id, pool) : NULL;
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
static svn_error_t *
set_entry(dag_node_t *parent,
const char *name,
const svn_fs_id_t *id,
svn_node_kind_t kind,
const char *txn_id,
apr_pool_t *pool)
{
node_revision_t *parent_noderev;
SVN_ERR(get_node_revision(&parent_noderev, parent, pool));
return svn_fs_fs__set_entry(parent->fs, txn_id, parent_noderev, name, id,
kind, pool);
}
static svn_error_t *
make_entry(dag_node_t **child_p,
dag_node_t *parent,
const char *parent_path,
const char *name,
svn_boolean_t is_dir,
const char *txn_id,
apr_pool_t *pool)
{
const svn_fs_id_t *new_node_id;
node_revision_t new_noderev, *parent_noderev;
if (! svn_path_is_single_path_component(name))
return svn_error_createf
(SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
_("Attempted to create a node with an illegal name '%s'"), name);
if (parent->kind != svn_node_dir)
return svn_error_create
(SVN_ERR_FS_NOT_DIRECTORY, NULL,
_("Attempted to create entry in non-directory parent"));
if (! svn_fs_fs__dag_check_mutable(parent))
return svn_error_createf
(SVN_ERR_FS_NOT_MUTABLE, NULL,
_("Attempted to clone child of non-mutable node"));
memset(&new_noderev, 0, sizeof(new_noderev));
new_noderev.kind = is_dir ? svn_node_dir : svn_node_file;
new_noderev.created_path = svn_path_join(parent_path, name, pool);
SVN_ERR(get_node_revision(&parent_noderev, parent, pool));
new_noderev.copyroot_path = apr_pstrdup(pool,
parent_noderev->copyroot_path);
new_noderev.copyroot_rev = parent_noderev->copyroot_rev;
new_noderev.copyfrom_rev = SVN_INVALID_REVNUM;
new_noderev.copyfrom_path = NULL;
SVN_ERR(svn_fs_fs__create_node
(&new_node_id, svn_fs_fs__dag_get_fs(parent), &new_noderev,
svn_fs_fs__id_copy_id(svn_fs_fs__dag_get_id(parent)),
txn_id, pool));
SVN_ERR(svn_fs_fs__dag_get_node(child_p, svn_fs_fs__dag_get_fs(parent),
new_node_id, pool));
return set_entry(parent, name, svn_fs_fs__dag_get_id(*child_p),
new_noderev.kind, txn_id, pool);
}
svn_error_t *
svn_fs_fs__dag_dir_entries(apr_hash_t **entries,
dag_node_t *node,
apr_pool_t *pool,
apr_pool_t *node_pool)
{
node_revision_t *noderev;
SVN_ERR(get_node_revision(&noderev, node, node_pool));
if (noderev->kind != svn_node_dir)
return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
_("Can't get entries of non-directory"));
return svn_fs_fs__rep_contents_dir(entries, node->fs, noderev, pool);
}
svn_error_t *
svn_fs_fs__dag_set_entry(dag_node_t *node,
const char *entry_name,
const svn_fs_id_t *id,
svn_node_kind_t kind,
const char *txn_id,
apr_pool_t *pool)
{
if (node->kind != svn_node_dir)
return svn_error_create
(SVN_ERR_FS_NOT_DIRECTORY, NULL,
_("Attempted to set entry in non-directory node"));
if (! svn_fs_fs__dag_check_mutable(node))
return svn_error_create
(SVN_ERR_FS_NOT_MUTABLE, NULL,
_("Attempted to set entry in immutable node"));
return set_entry(node, entry_name, id, kind, txn_id, pool);
}
svn_error_t *
svn_fs_fs__dag_get_proplist(apr_hash_t **proplist_p,
dag_node_t *node,
apr_pool_t *pool)
{
node_revision_t *noderev;
apr_hash_t *proplist = NULL;
SVN_ERR(get_node_revision(&noderev, node, pool));
SVN_ERR(svn_fs_fs__get_proplist(&proplist, node->fs,
noderev, pool));
*proplist_p = proplist;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__dag_set_proplist(dag_node_t *node,
apr_hash_t *proplist,
apr_pool_t *pool)
{
node_revision_t *noderev;
if (! svn_fs_fs__dag_check_mutable(node))
{
svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
return svn_error_createf
(SVN_ERR_FS_NOT_MUTABLE, NULL,
"Can't set proplist on *immutable* node-revision %s",
idstr->data);
}
SVN_ERR(get_node_revision(&noderev, node, pool));
return svn_fs_fs__set_proplist(node->fs, noderev, proplist, pool);
}
svn_error_t *
svn_fs_fs__dag_increment_mergeinfo_count(dag_node_t *node,
apr_int64_t increment,
apr_pool_t *pool)
{
node_revision_t *noderev;
if (! svn_fs_fs__dag_check_mutable(node))
{
svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
return svn_error_createf
(SVN_ERR_FS_NOT_MUTABLE, NULL,
"Can't increment mergeinfo count on *immutable* node-revision %s",
idstr->data);
}
if (increment == 0)
return SVN_NO_ERROR;
SVN_ERR(get_node_revision(&noderev, node, pool));
noderev->mergeinfo_count += increment;
if (noderev->mergeinfo_count < 0)
{
svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
return svn_error_createf
(SVN_ERR_FS_CORRUPT, NULL,
apr_psprintf(pool,
_("Can't increment mergeinfo count on node-revision %%s "
"to negative value %%%s"),
APR_INT64_T_FMT),
idstr->data, noderev->mergeinfo_count);
}
if (noderev->mergeinfo_count > 1 && noderev->kind == svn_node_file)
{
svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
return svn_error_createf
(SVN_ERR_FS_CORRUPT, NULL,
apr_psprintf(pool,
_("Can't increment mergeinfo count on *file* "
"node-revision %%s to %%%s (> 1)"),
APR_INT64_T_FMT),
idstr->data, noderev->mergeinfo_count);
}
return svn_fs_fs__put_node_revision(node->fs, noderev->id,
noderev, FALSE, pool);
}
svn_error_t *
svn_fs_fs__dag_set_has_mergeinfo(dag_node_t *node,
svn_boolean_t has_mergeinfo,
apr_pool_t *pool)
{
node_revision_t *noderev;
if (! svn_fs_fs__dag_check_mutable(node))
{
svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
return svn_error_createf
(SVN_ERR_FS_NOT_MUTABLE, NULL,
"Can't set mergeinfo flag on *immutable* node-revision %s",
idstr->data);
}
SVN_ERR(get_node_revision(&noderev, node, pool));
noderev->has_mergeinfo = has_mergeinfo;
return svn_fs_fs__put_node_revision(node->fs, noderev->id,
noderev, FALSE, pool);
}
svn_error_t *
svn_fs_fs__dag_revision_root(dag_node_t **node_p,
svn_fs_t *fs,
svn_revnum_t rev,
apr_pool_t *pool)
{
svn_fs_id_t *root_id;
SVN_ERR(svn_fs_fs__rev_get_root(&root_id, fs, rev, pool));
return svn_fs_fs__dag_get_node(node_p, fs, root_id, pool);
}
svn_error_t *
svn_fs_fs__dag_txn_root(dag_node_t **node_p,
svn_fs_t *fs,
const char *txn_id,
apr_pool_t *pool)
{
const svn_fs_id_t *root_id, *ignored;
SVN_ERR(svn_fs_fs__get_txn_ids(&root_id, &ignored, fs, txn_id, pool));
return svn_fs_fs__dag_get_node(node_p, fs, root_id, pool);
}
svn_error_t *
svn_fs_fs__dag_txn_base_root(dag_node_t **node_p,
svn_fs_t *fs,
const char *txn_id,
apr_pool_t *pool)
{
const svn_fs_id_t *base_root_id, *ignored;
SVN_ERR(svn_fs_fs__get_txn_ids(&ignored, &base_root_id, fs, txn_id, pool));
return svn_fs_fs__dag_get_node(node_p, fs, base_root_id, pool);
}
svn_error_t *
svn_fs_fs__dag_clone_child(dag_node_t **child_p,
dag_node_t *parent,
const char *parent_path,
const char *name,
const char *copy_id,
const char *txn_id,
svn_boolean_t is_parent_copyroot,
apr_pool_t *pool)
{
dag_node_t *cur_entry;
const svn_fs_id_t *new_node_id;
svn_fs_t *fs = svn_fs_fs__dag_get_fs(parent);
if (! svn_fs_fs__dag_check_mutable(parent))
return svn_error_createf
(SVN_ERR_FS_NOT_MUTABLE, NULL,
"Attempted to clone child of non-mutable node");
if (! svn_path_is_single_path_component(name))
return svn_error_createf
(SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
"Attempted to make a child clone with an illegal name '%s'", name);
SVN_ERR(svn_fs_fs__dag_open(&cur_entry, parent, name, pool));
if (svn_fs_fs__dag_check_mutable(cur_entry))
{
new_node_id = cur_entry->id;
}
else
{
node_revision_t *noderev, *parent_noderev;
SVN_ERR(get_node_revision(&noderev, cur_entry, pool));
if (is_parent_copyroot)
{
SVN_ERR(get_node_revision(&parent_noderev, parent, pool));
noderev->copyroot_rev = parent_noderev->copyroot_rev;
noderev->copyroot_path = apr_pstrdup(pool,
parent_noderev->copyroot_path);
}
noderev->copyfrom_path = NULL;
noderev->copyfrom_rev = SVN_INVALID_REVNUM;
noderev->predecessor_id = svn_fs_fs__id_copy(cur_entry->id, pool);
if (noderev->predecessor_count != -1)
noderev->predecessor_count++;
noderev->created_path = svn_path_join(parent_path, name, pool);
SVN_ERR(svn_fs_fs__create_successor(&new_node_id, fs, cur_entry->id,
noderev, copy_id, txn_id, pool));
SVN_ERR(set_entry(parent, name, new_node_id, noderev->kind, txn_id,
pool));
}
return svn_fs_fs__dag_get_node(child_p, fs, new_node_id, pool);
}
svn_error_t *
svn_fs_fs__dag_clone_root(dag_node_t **root_p,
svn_fs_t *fs,
const char *txn_id,
apr_pool_t *pool)
{
const svn_fs_id_t *base_root_id, *root_id;
SVN_ERR(svn_fs_fs__get_txn_ids(&root_id, &base_root_id, fs, txn_id, pool));
SVN_ERR_ASSERT(!svn_fs_fs__id_eq(root_id, base_root_id));
return svn_fs_fs__dag_get_node(root_p, fs, root_id, pool);
}
svn_error_t *
svn_fs_fs__dag_delete(dag_node_t *parent,
const char *name,
const char *txn_id,
apr_pool_t *pool)
{
node_revision_t *parent_noderev;
apr_hash_t *entries;
svn_fs_t *fs = parent->fs;
svn_fs_dirent_t *dirent;
svn_fs_id_t *id;
apr_pool_t *subpool;
if (parent->kind != svn_node_dir)
return svn_error_createf
(SVN_ERR_FS_NOT_DIRECTORY, NULL,
"Attempted to delete entry '%s' from *non*-directory node", name);
if (! svn_fs_fs__dag_check_mutable(parent))
return svn_error_createf
(SVN_ERR_FS_NOT_MUTABLE, NULL,
"Attempted to delete entry '%s' from immutable directory node", name);
if (! svn_path_is_single_path_component(name))
return svn_error_createf
(SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
"Attempted to delete a node with an illegal name '%s'", name);
SVN_ERR(get_node_revision(&parent_noderev, parent, pool));
subpool = svn_pool_create(pool);
SVN_ERR(svn_fs_fs__rep_contents_dir(&entries, fs, parent_noderev, subpool));
dirent = apr_hash_get(entries, name, APR_HASH_KEY_STRING);
if (! dirent)
return svn_error_createf
(SVN_ERR_FS_NO_SUCH_ENTRY, NULL,
"Delete failed--directory has no entry '%s'", name);
id = svn_fs_fs__id_copy(dirent->id, pool);
svn_pool_destroy(subpool);
SVN_ERR(svn_fs_fs__dag_delete_if_mutable(parent->fs, id, pool));
return svn_fs_fs__set_entry(parent->fs, txn_id, parent_noderev, name,
NULL, svn_node_unknown, pool);
}
svn_error_t *
svn_fs_fs__dag_remove_node(svn_fs_t *fs,
const svn_fs_id_t *id,
apr_pool_t *pool)
{
dag_node_t *node;
SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, id, pool));
if (! svn_fs_fs__dag_check_mutable(node))
return svn_error_createf(SVN_ERR_FS_NOT_MUTABLE, NULL,
"Attempted removal of immutable node");
return svn_fs_fs__delete_node_revision(fs, id, pool);
}
svn_error_t *
svn_fs_fs__dag_delete_if_mutable(svn_fs_t *fs,
const svn_fs_id_t *id,
apr_pool_t *pool)
{
dag_node_t *node;
SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, id, pool));
if (! svn_fs_fs__dag_check_mutable(node))
return SVN_NO_ERROR;
if (node->kind == svn_node_dir)
{
apr_hash_t *entries;
apr_hash_index_t *hi;
SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool, pool));
if (entries)
{
for (hi = apr_hash_first(pool, entries);
hi;
hi = apr_hash_next(hi))
{
void *val;
svn_fs_dirent_t *dirent;
apr_hash_this(hi, NULL, NULL, &val);
dirent = val;
SVN_ERR(svn_fs_fs__dag_delete_if_mutable(fs, dirent->id,
pool));
}
}
}
return svn_fs_fs__dag_remove_node(fs, id, pool);
}
svn_error_t *
svn_fs_fs__dag_make_file(dag_node_t **child_p,
dag_node_t *parent,
const char *parent_path,
const char *name,
const char *txn_id,
apr_pool_t *pool)
{
return make_entry(child_p, parent, parent_path, name, FALSE, txn_id, pool);
}
svn_error_t *
svn_fs_fs__dag_make_dir(dag_node_t **child_p,
dag_node_t *parent,
const char *parent_path,
const char *name,
const char *txn_id,
apr_pool_t *pool)
{
return make_entry(child_p, parent, parent_path, name, TRUE, txn_id, pool);
}
svn_error_t *
svn_fs_fs__dag_get_contents(svn_stream_t **contents_p,
dag_node_t *file,
apr_pool_t *pool)
{
node_revision_t *noderev;
svn_stream_t *contents;
if (file->kind != svn_node_file)
return svn_error_createf
(SVN_ERR_FS_NOT_FILE, NULL,
"Attempted to get textual contents of a *non*-file node");
SVN_ERR(get_node_revision(&noderev, file, pool));
SVN_ERR(svn_fs_fs__get_contents(&contents, file->fs,
noderev, pool));
*contents_p = contents;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__dag_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
dag_node_t *source,
dag_node_t *target,
apr_pool_t *pool)
{
node_revision_t *src_noderev;
node_revision_t *tgt_noderev;
if ((source && source->kind != svn_node_file)
|| target->kind != svn_node_file)
return svn_error_createf
(SVN_ERR_FS_NOT_FILE, NULL,
"Attempted to get textual contents of a *non*-file node");
if (source)
SVN_ERR(get_node_revision(&src_noderev, source, pool));
else
src_noderev = NULL;
SVN_ERR(get_node_revision(&tgt_noderev, target, pool));
return svn_fs_fs__get_file_delta_stream(stream_p, target->fs,
src_noderev, tgt_noderev, pool);
}
svn_error_t *
svn_fs_fs__dag_file_length(svn_filesize_t *length,
dag_node_t *file,
apr_pool_t *pool)
{
node_revision_t *noderev;
if (file->kind != svn_node_file)
return svn_error_createf
(SVN_ERR_FS_NOT_FILE, NULL,
"Attempted to get length of a *non*-file node");
SVN_ERR(get_node_revision(&noderev, file, pool));
return svn_fs_fs__file_length(length, noderev, pool);
}
svn_error_t *
svn_fs_fs__dag_file_checksum(svn_checksum_t **checksum,
dag_node_t *file,
svn_checksum_kind_t kind,
apr_pool_t *pool)
{
node_revision_t *noderev;
if (file->kind != svn_node_file)
return svn_error_createf
(SVN_ERR_FS_NOT_FILE, NULL,
"Attempted to get checksum of a *non*-file node");
SVN_ERR(get_node_revision(&noderev, file, pool));
return svn_fs_fs__file_checksum(checksum, noderev, kind, pool);
}
svn_error_t *
svn_fs_fs__dag_get_edit_stream(svn_stream_t **contents,
dag_node_t *file,
apr_pool_t *pool)
{
node_revision_t *noderev;
svn_stream_t *ws;
if (file->kind != svn_node_file)
return svn_error_createf
(SVN_ERR_FS_NOT_FILE, NULL,
"Attempted to set textual contents of a *non*-file node");
if (! svn_fs_fs__dag_check_mutable(file))
return svn_error_createf
(SVN_ERR_FS_NOT_MUTABLE, NULL,
"Attempted to set textual contents of an immutable node");
SVN_ERR(get_node_revision(&noderev, file, pool));
SVN_ERR(svn_fs_fs__set_contents(&ws, file->fs, noderev, pool));
*contents = ws;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__dag_finalize_edits(dag_node_t *file,
const svn_checksum_t *checksum,
apr_pool_t *pool)
{
if (checksum)
{
svn_checksum_t *file_checksum;
SVN_ERR(svn_fs_fs__dag_file_checksum(&file_checksum, file,
checksum->kind, pool));
if (!svn_checksum_match(checksum, file_checksum))
return svn_error_createf(SVN_ERR_CHECKSUM_MISMATCH, NULL,
_("Checksum mismatch, file '%s':\n"
" expected: %s\n"
" actual: %s\n"),
file->created_path,
svn_checksum_to_cstring_display(checksum,
pool),
svn_checksum_to_cstring_display(file_checksum,
pool));
}
return SVN_NO_ERROR;
}
dag_node_t *
svn_fs_fs__dag_dup(dag_node_t *node,
apr_pool_t *pool)
{
dag_node_t *new_node = apr_pcalloc(pool, sizeof(*new_node));
new_node->fs = node->fs;
new_node->id = svn_fs_fs__id_copy(node->id, pool);
new_node->kind = node->kind;
new_node->created_path = apr_pstrdup(pool, node->created_path);
if (node->node_revision && !svn_fs_fs__dag_check_mutable(node))
{
new_node->node_revision = copy_node_revision(node->node_revision, pool);
new_node->node_revision->id =
svn_fs_fs__id_copy(node->node_revision->id, pool);
new_node->node_revision->is_fresh_txn_root =
node->node_revision->is_fresh_txn_root;
}
return new_node;
}
svn_error_t *
svn_fs_fs__dag_dup_for_cache(void **out,
void *in,
apr_pool_t *pool)
{
dag_node_t *in_node = in, *out_node;
out_node = svn_fs_fs__dag_dup(in_node, pool);
out_node->fs = NULL;
*out = out_node;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__dag_serialize(char **data,
apr_size_t *data_len,
void *in,
apr_pool_t *pool)
{
dag_node_t *node = in;
svn_stringbuf_t *buf = svn_stringbuf_create("", pool);
if (svn_fs_fs__dag_check_mutable(node))
{
svn_stringbuf_appendcstr(buf, "M");
svn_stringbuf_appendcstr(buf, (node->kind == svn_node_file ? "F" : "D"));
svn_stringbuf_appendcstr(buf, svn_fs_fs__id_unparse(node->id,
pool)->data);
svn_stringbuf_appendcstr(buf, "\n");
svn_stringbuf_appendcstr(buf, node->created_path);
}
else
{
fs_fs_data_t *ffd = node->fs->fsap_data;
svn_stringbuf_appendcstr(buf, "I");
SVN_ERR(svn_fs_fs__write_noderev(svn_stream_from_stringbuf(buf, pool),
node->node_revision, ffd->format,
TRUE, pool));
}
*data = buf->data;
*data_len = buf->len;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__dag_deserialize(void **out,
const char *data,
apr_size_t data_len,
apr_pool_t *pool)
{
dag_node_t *node = apr_pcalloc(pool, sizeof(*node));
if (data_len == 0)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Empty noderev in cache"));
if (*data == 'M')
{
const char *newline;
int id_len;
data++; data_len--;
if (data_len == 0)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Kindless noderev in cache"));
if (*data == 'F')
node->kind = svn_node_file;
else if (*data == 'D')
node->kind = svn_node_dir;
else
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Unknown kind for noderev in cache: '%c'"),
*data);
data++; data_len--;
newline = memchr(data, '\n', data_len);
if (!newline)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Unterminated ID in cache"));
id_len = newline - 1 - data;
node->id = svn_fs_fs__id_parse(data, id_len, pool);
if (! node->id)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Bogus ID '%s' in cache"),
apr_pstrndup(pool, data, id_len));
data += id_len; data_len -= id_len;
data++; data_len--;
if (data_len == 0)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("No created path"));
node->created_path = apr_pstrndup(pool, data, data_len);
}
else if (*data == 'I')
{
node_revision_t *noderev;
apr_pool_t *subpool = svn_pool_create(pool);
svn_stream_t *stream =
svn_stream_from_stringbuf(svn_stringbuf_ncreate(data + 1,
data_len - 1,
subpool),
subpool);
SVN_ERR(svn_fs_fs__read_noderev(&noderev, stream, pool));
node->kind = noderev->kind;
node->id = svn_fs_fs__id_copy(noderev->id, pool);
node->created_path = apr_pstrdup(pool, noderev->created_path);
if (noderev->is_fresh_txn_root)
node->fresh_root_predecessor_id = noderev->predecessor_id;
node->node_revision = noderev;
svn_pool_destroy(subpool);
}
else
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("Unknown node type in cache: '%c'"), *data);
*out = node;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__dag_open(dag_node_t **child_p,
dag_node_t *parent,
const char *name,
apr_pool_t *pool)
{
const svn_fs_id_t *node_id;
SVN_ERR(dir_entry_id_from_node(&node_id, parent, name, pool));
if (! node_id)
return svn_error_createf
(SVN_ERR_FS_NOT_FOUND, NULL,
"Attempted to open non-existent child node '%s'", name);
if (! svn_path_is_single_path_component(name))
return svn_error_createf
(SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
"Attempted to open node with an illegal name '%s'", name);
return svn_fs_fs__dag_get_node(child_p, svn_fs_fs__dag_get_fs(parent),
node_id, pool);
}
svn_error_t *
svn_fs_fs__dag_copy(dag_node_t *to_node,
const char *entry,
dag_node_t *from_node,
svn_boolean_t preserve_history,
svn_revnum_t from_rev,
const char *from_path,
const char *txn_id,
apr_pool_t *pool)
{
const svn_fs_id_t *id;
if (preserve_history)
{
node_revision_t *from_noderev, *to_noderev;
const char *copy_id;
const svn_fs_id_t *src_id = svn_fs_fs__dag_get_id(from_node);
svn_fs_t *fs = svn_fs_fs__dag_get_fs(from_node);
SVN_ERR(get_node_revision(&from_noderev, from_node, pool));
to_noderev = copy_node_revision(from_noderev, pool);
SVN_ERR(svn_fs_fs__reserve_copy_id(©_id, fs, txn_id, pool));
to_noderev->predecessor_id = svn_fs_fs__id_copy(src_id, pool);
if (to_noderev->predecessor_count != -1)
to_noderev->predecessor_count++;
to_noderev->created_path =
svn_path_join(svn_fs_fs__dag_get_created_path(to_node), entry,
pool);
to_noderev->copyfrom_path = apr_pstrdup(pool, from_path);
to_noderev->copyfrom_rev = from_rev;
to_noderev->copyroot_path = NULL;
SVN_ERR(svn_fs_fs__create_successor(&id, fs, src_id, to_noderev,
copy_id, txn_id, pool));
}
else
{
id = svn_fs_fs__dag_get_id(from_node);
}
return svn_fs_fs__dag_set_entry(to_node, entry, id, from_node->kind,
txn_id, pool);
}
svn_error_t *
svn_fs_fs__dag_things_different(svn_boolean_t *props_changed,
svn_boolean_t *contents_changed,
dag_node_t *node1,
dag_node_t *node2,
apr_pool_t *pool)
{
node_revision_t *noderev1, *noderev2;
if (! props_changed && ! contents_changed)
return SVN_NO_ERROR;
SVN_ERR(get_node_revision(&noderev1, node1, pool));
SVN_ERR(get_node_revision(&noderev2, node2, pool));
if (props_changed != NULL)
*props_changed = (! svn_fs_fs__noderev_same_rep_key(noderev1->prop_rep,
noderev2->prop_rep));
if (contents_changed != NULL)
*contents_changed =
(! svn_fs_fs__noderev_same_rep_key(noderev1->data_rep,
noderev2->data_rep));
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__dag_get_copyroot(svn_revnum_t *rev,
const char **path,
dag_node_t *node,
apr_pool_t *pool)
{
node_revision_t *noderev;
SVN_ERR(get_node_revision(&noderev, node, pool));
*rev = noderev->copyroot_rev;
*path = noderev->copyroot_path;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__dag_get_copyfrom_rev(svn_revnum_t *rev,
dag_node_t *node,
apr_pool_t *pool)
{
node_revision_t *noderev;
SVN_ERR(get_node_revision(&noderev, node, pool));
*rev = noderev->copyfrom_rev;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__dag_get_copyfrom_path(const char **path,
dag_node_t *node,
apr_pool_t *pool)
{
node_revision_t *noderev;
SVN_ERR(get_node_revision(&noderev, node, pool));
*path = noderev->copyfrom_path;
return SVN_NO_ERROR;
}