/* 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 #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(©_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, ©_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); }