blob: 5656dbe60034fc848494147b1413086df2ac49bd [file] [log] [blame]
/* dag.c : DAG-like interface filesystem
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#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 "fs.h"
#include "fs_x.h"
#include "fs_id.h"
#include "cached_data.h"
#include "transaction.h"
#include "../libsvn_fs/fs-loader.h"
#include "private/svn_fspath.h"
#include "svn_private_config.h"
#include "private/svn_temp_serializer.h"
#include "temp_serializer.h"
#include "dag_cache.h"
/* Initializing a filesystem. */
struct dag_node_t
{
/* The filesystem this dag node came from. */
svn_fs_t *fs;
/* The node's NODE-REVISION. */
svn_fs_x__noderev_t *node_revision;
/* The pool to allocate NODE_REVISION in. */
apr_pool_t *node_pool;
/* Directory entry lookup hint to speed up consecutive calls to
svn_fs_x__rep_contents_dir_entry(). Only used for directory nodes.
Any value is legal but should default to APR_SIZE_MAX. */
apr_size_t hint;
};
/* Trivial helper/accessor functions. */
svn_node_kind_t
svn_fs_x__dag_node_kind(dag_node_t *node)
{
return node->node_revision->kind;
}
const svn_fs_x__id_t *
svn_fs_x__dag_get_id(const dag_node_t *node)
{
return &node->node_revision->noderev_id;
}
const char *
svn_fs_x__dag_get_created_path(dag_node_t *node)
{
return node->node_revision->created_path;
}
svn_fs_t *
svn_fs_x__dag_get_fs(dag_node_t *node)
{
return node->fs;
}
void
svn_fs_x__dag_set_fs(dag_node_t *node,
svn_fs_t *fs)
{
node->fs = fs;
}
/* Dup NODEREV and all associated data into RESULT_POOL.
Leaves the id and is_fresh_txn_root fields as zero bytes. */
static svn_fs_x__noderev_t *
copy_node_revision(svn_fs_x__noderev_t *noderev,
apr_pool_t *result_pool)
{
svn_fs_x__noderev_t *nr = apr_pmemdup(result_pool, noderev,
sizeof(*noderev));
if (noderev->copyfrom_path)
nr->copyfrom_path = apr_pstrdup(result_pool, noderev->copyfrom_path);
nr->copyroot_path = apr_pstrdup(result_pool, noderev->copyroot_path);
nr->data_rep = svn_fs_x__rep_copy(noderev->data_rep, result_pool);
nr->prop_rep = svn_fs_x__rep_copy(noderev->prop_rep, result_pool);
if (noderev->created_path)
nr->created_path = apr_pstrdup(result_pool, noderev->created_path);
return nr;
}
const svn_fs_x__id_t *
svn_fs_x__dag_get_node_id(dag_node_t *node)
{
return &node->node_revision->node_id;
}
const svn_fs_x__id_t *
svn_fs_x__dag_get_copy_id(dag_node_t *node)
{
return &node->node_revision->copy_id;
}
svn_boolean_t
svn_fs_x__dag_related_node(dag_node_t *lhs,
dag_node_t *rhs)
{
return svn_fs_x__id_eq(&lhs->node_revision->node_id,
&rhs->node_revision->node_id);
}
svn_boolean_t
svn_fs_x__dag_same_line_of_history(dag_node_t *lhs,
dag_node_t *rhs)
{
svn_fs_x__noderev_t *lhs_noderev = lhs->node_revision;
svn_fs_x__noderev_t *rhs_noderev = rhs->node_revision;
return svn_fs_x__id_eq(&lhs_noderev->node_id, &rhs_noderev->node_id)
&& svn_fs_x__id_eq(&lhs_noderev->copy_id, &rhs_noderev->copy_id);
}
svn_boolean_t
svn_fs_x__dag_check_mutable(const dag_node_t *node)
{
return svn_fs_x__is_txn(svn_fs_x__dag_get_id(node)->change_set);
}
svn_error_t *
svn_fs_x__dag_get_node(dag_node_t **node,
svn_fs_t *fs,
const svn_fs_x__id_t *id,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
dag_node_t *new_node;
svn_fs_x__noderev_t *noderev;
/* Construct the node. */
new_node = apr_pcalloc(result_pool, sizeof(*new_node));
new_node->fs = fs;
new_node->hint = APR_SIZE_MAX;
/* Grab the contents so we can inspect the node's kind and created path. */
SVN_ERR(svn_fs_x__get_node_revision(&noderev, fs, id,
result_pool, scratch_pool));
new_node->node_pool = result_pool;
new_node->node_revision = noderev;
/* Return a fresh new node */
*node = new_node;
return SVN_NO_ERROR;
}
svn_revnum_t
svn_fs_x__dag_get_revision(const dag_node_t *node)
{
svn_fs_x__noderev_t *noderev = node->node_revision;
return ( svn_fs_x__is_fresh_txn_root(noderev)
? svn_fs_x__get_revnum(noderev->predecessor_id.change_set)
: svn_fs_x__get_revnum(noderev->noderev_id.change_set));
}
const svn_fs_x__id_t *
svn_fs_x__dag_get_predecessor_id(dag_node_t *node)
{
return &node->node_revision->predecessor_id;
}
int
svn_fs_x__dag_get_predecessor_count(dag_node_t *node)
{
return node->node_revision->predecessor_count;
}
apr_int64_t
svn_fs_x__dag_get_mergeinfo_count(dag_node_t *node)
{
return node->node_revision->mergeinfo_count;
}
svn_boolean_t
svn_fs_x__dag_has_mergeinfo(dag_node_t *node)
{
return node->node_revision->has_mergeinfo;
}
svn_boolean_t
svn_fs_x__dag_has_descendants_with_mergeinfo(dag_node_t *node)
{
svn_fs_x__noderev_t *noderev = node->node_revision;
if (noderev->kind != svn_node_dir)
return FALSE;
if (noderev->mergeinfo_count > 1)
return TRUE;
else if (noderev->mergeinfo_count == 1 && !noderev->has_mergeinfo)
return TRUE;
return FALSE;
}
/*** Directory node functions ***/
/* Some of these are helpers for functions outside this section. */
/* Set *ID_P to the noderev-id for entry NAME in PARENT. If no such
entry, set *ID_P to NULL but do not error. */
svn_error_t *
svn_fs_x__dir_entry_id(svn_fs_x__id_t *id_p,
dag_node_t *parent,
const char *name,
apr_pool_t *scratch_pool)
{
svn_fs_x__dirent_t *dirent;
svn_fs_x__noderev_t *noderev = parent->node_revision;
if (noderev->kind != svn_node_dir)
return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
_("Can't get entries of non-directory"));
/* Make sure that NAME is a single path component. */
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);
/* Get a dirent hash for this directory. */
SVN_ERR(svn_fs_x__rep_contents_dir_entry(&dirent, parent->fs, noderev,
name, &parent->hint,
scratch_pool, scratch_pool));
if (dirent)
*id_p = dirent->id;
else
svn_fs_x__id_reset(id_p);
return SVN_NO_ERROR;
}
/* Add or set in PARENT a directory entry NAME pointing to ID.
Temporary allocations are done in SCRATCH_POOL.
Assumptions:
- PARENT is a mutable directory.
- ID does not refer to an ancestor of parent
- NAME is a single path component
*/
static svn_error_t *
set_entry(dag_node_t *parent,
const char *name,
const svn_fs_x__id_t *id,
svn_node_kind_t kind,
svn_fs_x__txn_id_t txn_id,
apr_pool_t *scratch_pool)
{
svn_fs_x__noderev_t *parent_noderev = parent->node_revision;
/* Set the new entry. */
SVN_ERR(svn_fs_x__set_entry(parent->fs, txn_id, parent_noderev, name, id,
kind, parent->node_pool, scratch_pool));
/* Update cached data. */
svn_fs_x__update_dag_cache(parent);
return SVN_NO_ERROR;
}
/* Make a new entry named NAME in PARENT. If IS_DIR is true, then the
node revision the new entry points to will be a directory, else it
will be a file. The new node will be allocated in RESULT_POOL. PARENT
must be mutable, and must not have an entry named NAME.
Use SCRATCH_POOL for all temporary allocations.
*/
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,
svn_fs_x__txn_id_t txn_id,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_fs_x__noderev_t new_noderev;
svn_fs_x__noderev_t *parent_noderev = parent->node_revision;
/* Make sure that NAME is a single path component. */
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);
/* Make sure that parent is a directory */
if (parent_noderev->kind != svn_node_dir)
return svn_error_create
(SVN_ERR_FS_NOT_DIRECTORY, NULL,
_("Attempted to create entry in non-directory parent"));
/* Check that the parent is mutable. */
if (! svn_fs_x__dag_check_mutable(parent))
return svn_error_createf
(SVN_ERR_FS_NOT_MUTABLE, NULL,
_("Attempted to clone child of non-mutable node"));
/* Create the new node's NODE-REVISION */
memset(&new_noderev, 0, sizeof(new_noderev));
new_noderev.kind = is_dir ? svn_node_dir : svn_node_file;
new_noderev.created_path = svn_fspath__join(parent_path, name, result_pool);
new_noderev.copyroot_path = apr_pstrdup(result_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_fs_x__id_reset(&new_noderev.predecessor_id);
SVN_ERR(svn_fs_x__create_node
(svn_fs_x__dag_get_fs(parent), &new_noderev,
&parent_noderev->copy_id, txn_id, scratch_pool));
/* Create a new dag_node_t for our new node */
SVN_ERR(svn_fs_x__dag_get_node(child_p, svn_fs_x__dag_get_fs(parent),
&new_noderev.noderev_id, result_pool,
scratch_pool));
/* We can safely call set_entry because we already know that
PARENT is mutable, and we just created CHILD, so we know it has
no ancestors (therefore, PARENT cannot be an ancestor of CHILD) */
return set_entry(parent, name, &new_noderev.noderev_id,
new_noderev.kind, txn_id, scratch_pool);
}
svn_error_t *
svn_fs_x__dag_dir_entries(apr_array_header_t **entries,
dag_node_t *node,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_fs_x__noderev_t *noderev = node->node_revision;
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_x__rep_contents_dir(entries, node->fs, noderev, result_pool,
scratch_pool);
}
svn_error_t *
svn_fs_x__dag_set_entry(dag_node_t *node,
const char *entry_name,
const svn_fs_x__id_t *id,
svn_node_kind_t kind,
svn_fs_x__txn_id_t txn_id,
apr_pool_t *scratch_pool)
{
/* Check it's a directory. */
if (node->node_revision->kind != svn_node_dir)
return svn_error_create
(SVN_ERR_FS_NOT_DIRECTORY, NULL,
_("Attempted to set entry in non-directory node"));
/* Check it's mutable. */
if (! svn_fs_x__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, scratch_pool);
}
/*** Proplists. ***/
svn_error_t *
svn_fs_x__dag_get_proplist(apr_hash_t **proplist_p,
dag_node_t *node,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
SVN_ERR(svn_fs_x__get_proplist(proplist_p, node->fs, node->node_revision,
result_pool, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_x__dag_set_proplist(dag_node_t *node,
apr_hash_t *proplist,
apr_pool_t *scratch_pool)
{
/* Sanity check: this node better be mutable! */
if (! svn_fs_x__dag_check_mutable(node))
{
svn_string_t *idstr
= svn_fs_x__id_unparse(&node->node_revision->noderev_id,
scratch_pool);
return svn_error_createf
(SVN_ERR_FS_NOT_MUTABLE, NULL,
"Can't set proplist on *immutable* node-revision %s",
idstr->data);
}
/* Set the new proplist. */
SVN_ERR(svn_fs_x__set_proplist(node->fs, node->node_revision, proplist,
scratch_pool));
svn_fs_x__update_dag_cache(node);
return SVN_NO_ERROR;
}
/* Write NODE's NODEREV element to disk. Update the DAG cache.
Use SCRATCH_POOL for temporary allocations. */
static svn_error_t *
noderev_changed(dag_node_t *node,
apr_pool_t *scratch_pool)
{
SVN_ERR(svn_fs_x__put_node_revision(node->fs, node->node_revision,
scratch_pool));
svn_fs_x__update_dag_cache(node);
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_x__dag_increment_mergeinfo_count(dag_node_t *node,
apr_int64_t increment,
apr_pool_t *scratch_pool)
{
svn_fs_x__noderev_t *noderev = node->node_revision;
/* Sanity check: this node better be mutable! */
if (! svn_fs_x__dag_check_mutable(node))
{
svn_string_t *idstr = svn_fs_x__id_unparse(&noderev->noderev_id,
scratch_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;
noderev->mergeinfo_count += increment;
if (noderev->mergeinfo_count < 0)
{
svn_string_t *idstr = svn_fs_x__id_unparse(&noderev->noderev_id,
scratch_pool);
return svn_error_createf
(SVN_ERR_FS_CORRUPT, NULL,
apr_psprintf(scratch_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_x__id_unparse(&noderev->noderev_id,
scratch_pool);
return svn_error_createf
(SVN_ERR_FS_CORRUPT, NULL,
apr_psprintf(scratch_pool,
_("Can't increment mergeinfo count on *file* "
"node-revision %%s to %%%s (> 1)"),
APR_INT64_T_FMT),
idstr->data, noderev->mergeinfo_count);
}
/* Flush it out. */
return noderev_changed(node, scratch_pool);
}
svn_error_t *
svn_fs_x__dag_set_has_mergeinfo(dag_node_t *node,
svn_boolean_t has_mergeinfo,
apr_pool_t *scratch_pool)
{
/* Sanity check: this node better be mutable! */
if (! svn_fs_x__dag_check_mutable(node))
{
svn_string_t *idstr
= svn_fs_x__id_unparse(&node->node_revision->noderev_id,
scratch_pool);
return svn_error_createf
(SVN_ERR_FS_NOT_MUTABLE, NULL,
"Can't set mergeinfo flag on *immutable* node-revision %s",
idstr->data);
}
node->node_revision->has_mergeinfo = has_mergeinfo;
/* Flush it out. */
return noderev_changed(node, scratch_pool);
}
/*** Roots. ***/
svn_error_t *
svn_fs_x__dag_root(dag_node_t **node_p,
svn_fs_t *fs,
svn_fs_x__change_set_t change_set,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_fs_x__id_t root_id;
root_id.change_set = change_set;
root_id.number = SVN_FS_X__ITEM_INDEX_ROOT_NODE;
return svn_fs_x__dag_get_node(node_p, fs, &root_id, result_pool,
scratch_pool);
}
svn_error_t *
svn_fs_x__dag_clone_child(dag_node_t **child_p,
dag_node_t *parent,
const char *parent_path,
const char *name,
const svn_fs_x__id_t *copy_id,
svn_fs_x__txn_id_t txn_id,
svn_boolean_t is_parent_copyroot,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
dag_node_t *cur_entry; /* parent's current entry named NAME */
const svn_fs_x__id_t *new_node_id; /* node id we'll put into NEW_NODE */
svn_fs_t *fs = svn_fs_x__dag_get_fs(parent);
/* First check that the parent is mutable. */
if (! svn_fs_x__dag_check_mutable(parent))
return svn_error_createf
(SVN_ERR_FS_NOT_MUTABLE, NULL,
"Attempted to clone child of non-mutable node");
/* Make sure that NAME is a single path component. */
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);
/* Find the node named NAME in PARENT's entries list if it exists. */
SVN_ERR(svn_fs_x__dag_open(&cur_entry, parent, name, scratch_pool,
scratch_pool));
if (! cur_entry)
return svn_error_createf
(SVN_ERR_FS_NOT_FOUND, NULL,
"Attempted to open non-existent child node '%s'", name);
/* Check for mutability in the node we found. If it's mutable, we
don't need to clone it. */
if (svn_fs_x__dag_check_mutable(cur_entry))
{
/* This has already been cloned */
new_node_id = svn_fs_x__dag_get_id(cur_entry);
}
else
{
svn_fs_x__noderev_t *noderev = cur_entry->node_revision;
if (is_parent_copyroot)
{
svn_fs_x__noderev_t *parent_noderev = parent->node_revision;
noderev->copyroot_rev = parent_noderev->copyroot_rev;
noderev->copyroot_path = apr_pstrdup(scratch_pool,
parent_noderev->copyroot_path);
}
noderev->copyfrom_path = NULL;
noderev->copyfrom_rev = SVN_INVALID_REVNUM;
noderev->predecessor_id = noderev->noderev_id;
noderev->predecessor_count++;
noderev->created_path = svn_fspath__join(parent_path, name,
scratch_pool);
if (copy_id == NULL)
copy_id = &noderev->copy_id;
SVN_ERR(svn_fs_x__create_successor(fs, noderev, copy_id, txn_id,
scratch_pool));
new_node_id = &noderev->noderev_id;
/* Replace the ID in the parent's ENTRY list with the ID which
refers to the mutable clone of this child. */
SVN_ERR(set_entry(parent, name, new_node_id, noderev->kind, txn_id,
scratch_pool));
}
/* Initialize the youngster. */
return svn_fs_x__dag_get_node(child_p, fs, new_node_id, result_pool,
scratch_pool);
}
/* Delete all mutable node revisions reachable from node ID, including
ID itself, from FS's `nodes' table. Also delete any mutable
representations and strings associated with that node revision.
ID may refer to a file or directory, which may be mutable or immutable.
Use SCRATCH_POOL for temporary allocations.
*/
static svn_error_t *
delete_if_mutable(svn_fs_t *fs,
const svn_fs_x__id_t *id,
apr_pool_t *scratch_pool)
{
dag_node_t *node;
/* Get the node. */
SVN_ERR(svn_fs_x__dag_get_node(&node, fs, id, scratch_pool, scratch_pool));
/* If immutable, do nothing and return immediately. */
if (! svn_fs_x__dag_check_mutable(node))
return SVN_NO_ERROR;
/* Else it's mutable. Recurse on directories... */
if (node->node_revision->kind == svn_node_dir)
{
apr_array_header_t *entries;
int i;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
/* Loop over directory entries */
SVN_ERR(svn_fs_x__dag_dir_entries(&entries, node, scratch_pool,
iterpool));
for (i = 0; i < entries->nelts; ++i)
{
const svn_fs_x__id_t *noderev_id
= &APR_ARRAY_IDX(entries, i, svn_fs_x__dirent_t *)->id;
svn_pool_clear(iterpool);
SVN_ERR(delete_if_mutable(fs, noderev_id, iterpool));
}
svn_pool_destroy(iterpool);
}
/* ... then delete the node itself, after deleting any mutable
representations and strings it points to. */
return svn_fs_x__delete_node_revision(fs, id, scratch_pool);
}
svn_error_t *
svn_fs_x__dag_delete(dag_node_t *parent,
const char *name,
svn_fs_x__txn_id_t txn_id,
apr_pool_t *scratch_pool)
{
svn_fs_x__noderev_t *parent_noderev = parent->node_revision;
svn_fs_t *fs = parent->fs;
svn_fs_x__dirent_t *dirent;
apr_pool_t *subpool;
/* Make sure parent is a directory. */
if (parent_noderev->kind != svn_node_dir)
return svn_error_createf
(SVN_ERR_FS_NOT_DIRECTORY, NULL,
"Attempted to delete entry '%s' from *non*-directory node", name);
/* Make sure parent is mutable. */
if (! svn_fs_x__dag_check_mutable(parent))
return svn_error_createf
(SVN_ERR_FS_NOT_MUTABLE, NULL,
"Attempted to delete entry '%s' from immutable directory node", name);
/* Make sure that NAME is a single path component. */
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);
/* We allocate a few potentially heavy temporary objects (file buffers
and directories). Make sure we don't keep them around for longer
than necessary. */
subpool = svn_pool_create(scratch_pool);
/* Search this directory for a dirent with that NAME. */
SVN_ERR(svn_fs_x__rep_contents_dir_entry(&dirent, fs, parent_noderev,
name, &parent->hint,
subpool, subpool));
/* If we never found ID in ENTRIES (perhaps because there are no
ENTRIES, perhaps because ID just isn't in the existing ENTRIES
... it doesn't matter), return an error. */
if (! dirent)
return svn_error_createf
(SVN_ERR_FS_NO_SUCH_ENTRY, NULL,
"Delete failed--directory has no entry '%s'", name);
/* If mutable, remove it and any mutable children from db. */
SVN_ERR(delete_if_mutable(parent->fs, &dirent->id, subpool));
/* Remove this entry from its parent's entries list. */
SVN_ERR(set_entry(parent, name, NULL, svn_node_unknown, txn_id, subpool));
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_x__dag_make_file(dag_node_t **child_p,
dag_node_t *parent,
const char *parent_path,
const char *name,
svn_fs_x__txn_id_t txn_id,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
/* Call our little helper function */
return make_entry(child_p, parent, parent_path, name, FALSE, txn_id,
result_pool, scratch_pool);
}
svn_error_t *
svn_fs_x__dag_make_dir(dag_node_t **child_p,
dag_node_t *parent,
const char *parent_path,
const char *name,
svn_fs_x__txn_id_t txn_id,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
/* Call our little helper function */
return make_entry(child_p, parent, parent_path, name, TRUE, txn_id,
result_pool, scratch_pool);
}
svn_error_t *
svn_fs_x__dag_get_contents(svn_stream_t **contents_p,
dag_node_t *file,
apr_pool_t *result_pool)
{
/* Make sure our node is a file. */
if (file->node_revision->kind != svn_node_file)
return svn_error_createf
(SVN_ERR_FS_NOT_FILE, NULL,
"Attempted to get textual contents of a *non*-file node");
/* Get a stream to the contents. */
SVN_ERR(svn_fs_x__get_contents(contents_p, file->fs,
file->node_revision->data_rep, TRUE,
result_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_x__dag_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
dag_node_t *source,
dag_node_t *target,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_fs_x__noderev_t *src_noderev = source ? source->node_revision : NULL;
svn_fs_x__noderev_t *tgt_noderev = target->node_revision;
/* Make sure our nodes are files. */
if ((source && src_noderev->kind != svn_node_file)
|| tgt_noderev->kind != svn_node_file)
return svn_error_createf
(SVN_ERR_FS_NOT_FILE, NULL,
"Attempted to get textual contents of a *non*-file node");
/* Get the delta stream. */
return svn_fs_x__get_file_delta_stream(stream_p, target->fs,
src_noderev, tgt_noderev,
result_pool, scratch_pool);
}
svn_error_t *
svn_fs_x__dag_try_process_file_contents(svn_boolean_t *success,
dag_node_t *node,
svn_fs_process_contents_func_t processor,
void* baton,
apr_pool_t *scratch_pool)
{
return svn_fs_x__try_process_file_contents(success, node->fs,
node->node_revision,
processor, baton, scratch_pool);
}
svn_error_t *
svn_fs_x__dag_file_length(svn_filesize_t *length,
dag_node_t *file)
{
/* Make sure our node is a file. */
if (file->node_revision->kind != svn_node_file)
return svn_error_createf
(SVN_ERR_FS_NOT_FILE, NULL,
"Attempted to get length of a *non*-file node");
return svn_fs_x__file_length(length, file->node_revision);
}
svn_error_t *
svn_fs_x__dag_file_checksum(svn_checksum_t **checksum,
dag_node_t *file,
svn_checksum_kind_t kind,
apr_pool_t *result_pool)
{
if (file->node_revision->kind != svn_node_file)
return svn_error_createf
(SVN_ERR_FS_NOT_FILE, NULL,
"Attempted to get checksum of a *non*-file node");
return svn_fs_x__file_checksum(checksum, file->node_revision, kind,
result_pool);
}
svn_error_t *
svn_fs_x__dag_get_edit_stream(svn_stream_t **contents,
dag_node_t *file,
apr_pool_t *result_pool)
{
/* Make sure our node is a file. */
if (file->node_revision->kind != svn_node_file)
return svn_error_createf
(SVN_ERR_FS_NOT_FILE, NULL,
"Attempted to set textual contents of a *non*-file node");
/* Make sure our node is mutable. */
if (! svn_fs_x__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(svn_fs_x__set_contents(contents, file->fs, file->node_revision,
result_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_x__dag_finalize_edits(dag_node_t *file,
const svn_checksum_t *checksum,
apr_pool_t *scratch_pool)
{
if (checksum)
{
svn_checksum_t *file_checksum;
SVN_ERR(svn_fs_x__dag_file_checksum(&file_checksum, file,
checksum->kind, scratch_pool));
if (!svn_checksum_match(checksum, file_checksum))
return svn_checksum_mismatch_err(checksum, file_checksum,
scratch_pool,
_("Checksum mismatch for '%s'"),
file->node_revision->created_path);
}
svn_fs_x__update_dag_cache(file);
return SVN_NO_ERROR;
}
dag_node_t *
svn_fs_x__dag_dup(const dag_node_t *node,
apr_pool_t *result_pool)
{
/* Allocate our new node. */
dag_node_t *new_node = apr_pmemdup(result_pool, node, sizeof(*new_node));
/* Copy sub-structures. */
new_node->node_revision = copy_node_revision(node->node_revision,
result_pool);
new_node->node_pool = result_pool;
return new_node;
}
svn_error_t *
svn_fs_x__dag_open(dag_node_t **child_p,
dag_node_t *parent,
const char *name,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_fs_x__id_t node_id;
/* Ensure that NAME exists in PARENT's entry list. */
SVN_ERR(svn_fs_x__dir_entry_id(&node_id, parent, name, scratch_pool));
if (! svn_fs_x__id_used(&node_id))
{
*child_p = NULL;
return SVN_NO_ERROR;
}
/* Now get the node that was requested. */
return svn_fs_x__dag_get_node(child_p, svn_fs_x__dag_get_fs(parent),
&node_id, result_pool, scratch_pool);
}
svn_error_t *
svn_fs_x__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,
svn_fs_x__txn_id_t txn_id,
apr_pool_t *scratch_pool)
{
const svn_fs_x__id_t *id;
if (preserve_history)
{
svn_fs_x__noderev_t *to_noderev;
svn_fs_x__id_t copy_id;
svn_fs_t *fs = svn_fs_x__dag_get_fs(from_node);
/* Make a copy of the original node revision. */
to_noderev = copy_node_revision(from_node->node_revision, scratch_pool);
/* Reserve a copy ID for this new copy. */
SVN_ERR(svn_fs_x__reserve_copy_id(&copy_id, fs, txn_id, scratch_pool));
/* Create a successor with its predecessor pointing at the copy
source. */
to_noderev->predecessor_id = to_noderev->noderev_id;
to_noderev->predecessor_count++;
to_noderev->created_path =
svn_fspath__join(svn_fs_x__dag_get_created_path(to_node), entry,
scratch_pool);
to_noderev->copyfrom_path = apr_pstrdup(scratch_pool, from_path);
to_noderev->copyfrom_rev = from_rev;
/* Set the copyroot equal to our own id. */
to_noderev->copyroot_path = NULL;
SVN_ERR(svn_fs_x__create_successor(fs, to_noderev,
&copy_id, txn_id, scratch_pool));
id = &to_noderev->noderev_id;
}
else /* don't preserve history */
{
id = svn_fs_x__dag_get_id(from_node);
}
/* Set the entry in to_node to the new id. */
return svn_fs_x__dag_set_entry(to_node, entry, id,
from_node->node_revision->kind,
txn_id, scratch_pool);
}
/*** Comparison. ***/
svn_error_t *
svn_fs_x__dag_things_different(svn_boolean_t *props_changed,
svn_boolean_t *contents_changed,
dag_node_t *node1,
dag_node_t *node2,
svn_boolean_t strict,
apr_pool_t *scratch_pool)
{
svn_fs_x__noderev_t *noderev1 = node1->node_revision;
svn_fs_x__noderev_t *noderev2 = node2->node_revision;
svn_fs_t *fs;
svn_boolean_t same;
/* If we have no place to store our results, don't bother doing
anything. */
if (! props_changed && ! contents_changed)
return SVN_NO_ERROR;
fs = svn_fs_x__dag_get_fs(node1);
/* Compare property keys. */
if (props_changed != NULL)
{
SVN_ERR(svn_fs_x__prop_rep_equal(&same, fs, noderev1, noderev2,
strict, scratch_pool));
*props_changed = !same;
}
/* Compare contents keys. */
if (contents_changed != NULL)
*contents_changed = !svn_fs_x__file_text_rep_equal(noderev1->data_rep,
noderev2->data_rep);
return SVN_NO_ERROR;
}
void
svn_fs_x__dag_get_copyroot(svn_revnum_t *rev,
const char **path,
dag_node_t *node)
{
*rev = node->node_revision->copyroot_rev;
*path = node->node_revision->copyroot_path;
}
svn_revnum_t
svn_fs_x__dag_get_copyfrom_rev(dag_node_t *node)
{
return node->node_revision->copyfrom_rev;
}
const char *
svn_fs_x__dag_get_copyfrom_path(dag_node_t *node)
{
return node->node_revision->copyfrom_path;
}
svn_error_t *
svn_fs_x__dag_update_ancestry(dag_node_t *target,
dag_node_t *source,
apr_pool_t *scratch_pool)
{
svn_fs_x__noderev_t *source_noderev = source->node_revision;
svn_fs_x__noderev_t *target_noderev = target->node_revision;
if (! svn_fs_x__dag_check_mutable(target))
return svn_error_createf
(SVN_ERR_FS_NOT_MUTABLE, NULL,
_("Attempted to update ancestry of non-mutable node"));
target_noderev->predecessor_id = source_noderev->noderev_id;
target_noderev->predecessor_count = source_noderev->predecessor_count;
target_noderev->predecessor_count++;
return noderev_changed(target, scratch_pool);
}