Exemple #1
0
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)
{
  node_revision_t *noderev1, *noderev2;

  /* If we have no place to store our results, don't bother doing
     anything. */
  if (! props_changed && ! contents_changed)
    return SVN_NO_ERROR;

  /* The node revision skels for these two nodes. */
  SVN_ERR(get_node_revision(&noderev1, node1));
  SVN_ERR(get_node_revision(&noderev2, node2));

  /* Compare property keys. */
  if (props_changed != NULL)
    *props_changed = (! svn_fs_x__noderev_same_rep_key(noderev1->prop_rep,
                                                       noderev2->prop_rep));

  /* Compare contents keys. */
  if (contents_changed != NULL)
    *contents_changed =
      (! svn_fs_x__noderev_same_rep_key(noderev1->data_rep,
                                        noderev2->data_rep));

  return SVN_NO_ERROR;
}
Exemple #2
0
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 *pool)
{
  node_revision_t *src_noderev;
  node_revision_t *tgt_noderev;

  /* Make sure our nodes are files. */
  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");

  /* Go get fresh node-revisions for the nodes. */
  if (source)
    SVN_ERR(get_node_revision(&src_noderev, source));
  else
    src_noderev = NULL;
  SVN_ERR(get_node_revision(&tgt_noderev, target));

  /* Get the delta stream. */
  return svn_fs_x__get_file_delta_stream(stream_p, target->fs,
                                         src_noderev, tgt_noderev, pool);
}
Exemple #3
0
svn_error_t *
svn_fs_x__dag_get_contents(svn_stream_t **contents_p,
                           dag_node_t *file,
                           apr_pool_t *pool)
{
  node_revision_t *noderev;
  svn_stream_t *contents;

  /* Make sure our node is a file. */
  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");

  /* Go get a fresh node-revision for FILE. */
  SVN_ERR(get_node_revision(&noderev, file));

  /* Get a stream to the contents. */
  SVN_ERR(svn_fs_x__get_contents(&contents, file->fs,
                                 noderev->data_rep, pool));

  *contents_p = contents;

  return SVN_NO_ERROR;
}
Exemple #4
0
svn_error_t *
svn_fs_x__dag_set_has_mergeinfo(dag_node_t *node,
                                 svn_boolean_t has_mergeinfo,
                                 apr_pool_t *pool)
{
  node_revision_t *noderev;

  /* 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->id, pool);
      return svn_error_createf
        (SVN_ERR_FS_NOT_MUTABLE, NULL,
         "Can't set mergeinfo flag on *immutable* node-revision %s",
         idstr->data);
    }

  /* Go get a fresh NODE-REVISION for this node. */
  SVN_ERR(get_node_revision(&noderev, node));

  noderev->has_mergeinfo = has_mergeinfo;

  /* Flush it out. */
  return svn_fs_x__put_node_revision(node->fs, noderev->id,
                                     noderev, FALSE, pool);
}
Exemple #5
0
svn_error_t *
svn_fs_x__dag_get_edit_stream(svn_stream_t **contents,
                              dag_node_t *file,
                              apr_pool_t *pool)
{
  node_revision_t *noderev;
  svn_stream_t *ws;

  /* Make sure our node is a file. */
  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");

  /* 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");

  /* Get the node revision. */
  SVN_ERR(get_node_revision(&noderev, file));

  SVN_ERR(svn_fs_x__set_contents(&ws, file->fs, noderev, pool));

  *contents = ws;

  return SVN_NO_ERROR;
}
Exemple #6
0
svn_error_t *
svn_fs_x__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;

  /* Construct the node. */
  new_node = apr_pcalloc(pool, sizeof(*new_node));
  new_node->fs = fs;
  new_node->id = svn_fs_x__id_copy(id, pool);

  /* Grab the contents so we can inspect the node's kind and created path. */
  new_node->node_pool = pool;
  SVN_ERR(get_node_revision(&noderev, new_node));

  /* Initialize the KIND and CREATED_PATH attributes */
  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;

  /* Return a fresh new node */
  *node = new_node;
  return SVN_NO_ERROR;
}
Exemple #7
0
svn_error_t *
svn_fs_x__dag_delete(dag_node_t *parent,
                     const char *name,
                     const svn_fs_x__id_part_t *txn_id,
                     apr_pool_t *pool)
{
  node_revision_t *parent_noderev;
  svn_fs_t *fs = parent->fs;
  svn_fs_dirent_t *dirent;
  svn_fs_id_t *id;
  apr_pool_t *subpool;

  /* Make sure parent is a directory. */
  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);

  /* 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);

  /* Get a fresh NODE-REVISION for the parent node. */
  SVN_ERR(get_node_revision(&parent_noderev, parent));

  subpool = svn_pool_create(pool);

  /* Search this directory for a dirent with that NAME. */
  SVN_ERR(svn_fs_x__rep_contents_dir_entry(&dirent, fs, parent_noderev,
                                           name, 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);

  /* Copy the ID out of the subpool and release the rest of the
     directory listing. */
  id = svn_fs_x__id_copy(dirent->id, pool);
  svn_pool_destroy(subpool);

  /* If mutable, remove it and any mutable children from db. */
  SVN_ERR(svn_fs_x__dag_delete_if_mutable(parent->fs, id, pool));

  /* Remove this entry from its parent's entries list. */
  return svn_fs_x__set_entry(parent->fs, txn_id, parent_noderev, name,
                             NULL, svn_node_unknown, pool);
}
Exemple #8
0
/* 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 POOL.  PARENT
   must be mutable, and must not have an entry named NAME.

   Use POOL for all allocations, except caching the node_revision in PARENT.
 */
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 svn_fs_x__id_part_t *txn_id,
           apr_pool_t *pool)
{
  const svn_fs_id_t *new_node_id;
  node_revision_t new_noderev, *parent_noderev;

  /* 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->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, pool);

  SVN_ERR(get_node_revision(&parent_noderev, parent));
  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_x__create_node
          (&new_node_id, svn_fs_x__dag_get_fs(parent), &new_noderev,
           svn_fs_x__id_copy_id(svn_fs_x__dag_get_id(parent)),
           txn_id, 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_node_id, 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, svn_fs_x__dag_get_id(*child_p),
                   new_noderev.kind, txn_id, pool);
}
Exemple #9
0
svn_error_t *
svn_fs_x__dag_get_mergeinfo_count(apr_int64_t *count,
                                  dag_node_t *node)
{
  node_revision_t *noderev;

  SVN_ERR(get_node_revision(&noderev, node));
  *count = noderev->mergeinfo_count;
  return SVN_NO_ERROR;
}
Exemple #10
0
svn_error_t *
svn_fs_x__dag_has_mergeinfo(svn_boolean_t *has_mergeinfo,
                            dag_node_t *node)
{
  node_revision_t *noderev;

  SVN_ERR(get_node_revision(&noderev, node));
  *has_mergeinfo = noderev->has_mergeinfo;
  return SVN_NO_ERROR;
}
Exemple #11
0
svn_error_t *
svn_fs_x__dag_get_predecessor_count(int *count,
                                    dag_node_t *node)
{
  node_revision_t *noderev;

  SVN_ERR(get_node_revision(&noderev, node));
  *count = noderev->predecessor_count;
  return SVN_NO_ERROR;
}
Exemple #12
0
svn_error_t *
svn_fs_x__dag_get_predecessor_id(const svn_fs_id_t **id_p,
                                 dag_node_t *node)
{
  node_revision_t *noderev;

  SVN_ERR(get_node_revision(&noderev, node));
  *id_p = noderev->predecessor_id;
  return SVN_NO_ERROR;
}
Exemple #13
0
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,
                   const svn_fs_x__id_part_t *txn_id,
                   apr_pool_t *pool)
{
  const svn_fs_id_t *id;

  if (preserve_history)
    {
      node_revision_t *from_noderev, *to_noderev;
      svn_fs_x__id_part_t copy_id;
      const svn_fs_id_t *src_id = svn_fs_x__dag_get_id(from_node);
      svn_fs_t *fs = svn_fs_x__dag_get_fs(from_node);

      /* Make a copy of the original node revision. */
      SVN_ERR(get_node_revision(&from_noderev, from_node));
      to_noderev = copy_node_revision(from_noderev, pool);

      /* Reserve a copy ID for this new copy. */
      SVN_ERR(svn_fs_x__reserve_copy_id(&copy_id, fs, txn_id, pool));

      /* Create a successor with its predecessor pointing at the copy
         source. */
      to_noderev->predecessor_id = svn_fs_x__id_copy(src_id, pool);
      if (to_noderev->predecessor_count != -1)
        to_noderev->predecessor_count++;
      to_noderev->created_path =
        svn_fspath__join(svn_fs_x__dag_get_created_path(to_node), entry,
                         pool);
      to_noderev->copyfrom_path = apr_pstrdup(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(&id, fs, src_id, to_noderev,
                                         &copy_id, txn_id, pool));

    }
  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->kind,
                                 txn_id, pool);
}
Exemple #14
0
svn_error_t *
svn_fs_x__dag_get_copyfrom_rev(svn_revnum_t *rev,
                               dag_node_t *node)
{
  node_revision_t *noderev;

  /* Go get a fresh node-revision for NODE. */
  SVN_ERR(get_node_revision(&noderev, node));

  *rev = noderev->copyfrom_rev;

  return SVN_NO_ERROR;
}
Exemple #15
0
svn_error_t *
svn_fs_x__dag_get_copyfrom_path(const char **path,
                                dag_node_t *node)
{
  node_revision_t *noderev;

  /* Go get a fresh node-revision for NODE. */
  SVN_ERR(get_node_revision(&noderev, node));

  *path = noderev->copyfrom_path;

  return SVN_NO_ERROR;
}
Exemple #16
0
svn_error_t *
svn_fs_x__dag_increment_mergeinfo_count(dag_node_t *node,
                                        apr_int64_t increment,
                                        apr_pool_t *pool)
{
  node_revision_t *noderev;

  /* 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->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;

  /* Go get a fresh NODE-REVISION for this node. */
  SVN_ERR(get_node_revision(&noderev, node));

  noderev->mergeinfo_count += increment;
  if (noderev->mergeinfo_count < 0)
    {
      svn_string_t *idstr = svn_fs_x__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_x__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);
    }

  /* Flush it out. */
  return svn_fs_x__put_node_revision(node->fs, noderev->id,
                                     noderev, FALSE, pool);
}
Exemple #17
0
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;

    /* Go get a fresh node-revision for FILE. */
    SVN_ERR(get_node_revision(&noderev, node, pool));

    *rev = noderev->copyfrom_rev;

    return SVN_NO_ERROR;
}
Exemple #18
0
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;

    /* Go get a fresh node-revision for FILE. */
    SVN_ERR(get_node_revision(&noderev, node, pool));

    *path = noderev->copyfrom_path;

    return SVN_NO_ERROR;
}
Exemple #19
0
svn_error_t *
svn_fs_x__dag_update_ancestry(dag_node_t *target,
                              dag_node_t *source,
                              apr_pool_t *pool)
{
  node_revision_t *source_noderev, *target_noderev;

  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"));

  SVN_ERR(get_node_revision(&source_noderev, source));
  SVN_ERR(get_node_revision(&target_noderev, target));

  target_noderev->predecessor_id = source->id;
  target_noderev->predecessor_count = source_noderev->predecessor_count;
  if (target_noderev->predecessor_count != -1)
    target_noderev->predecessor_count++;

  return svn_fs_x__put_node_revision(target->fs, target->id, target_noderev,
                                     FALSE, pool);
}
Exemple #20
0
svn_error_t *
svn_fs_x__dag_dir_entries(apr_hash_t **entries,
                           dag_node_t *node,
                           apr_pool_t *pool)
{
  node_revision_t *noderev;

  SVN_ERR(get_node_revision(&noderev, node));

  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, pool);
}
Exemple #21
0
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 *pool)
{
  node_revision_t *noderev;

  /* Go get fresh node-revisions for the nodes. */
  SVN_ERR(get_node_revision(&noderev, node));

  return svn_fs_x__try_process_file_contents(success, node->fs,
                                             noderev,
                                             processor, baton, pool);
}
Exemple #22
0
svn_error_t *
svn_fs_x__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));

  SVN_ERR(svn_fs_x__get_proplist(&proplist, node->fs, noderev, pool));

  *proplist_p = proplist;

  return SVN_NO_ERROR;
}
Exemple #23
0
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 *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));

  return svn_fs_x__file_checksum(checksum, noderev, kind, pool);
}
Exemple #24
0
/* Add or set in PARENT a directory entry NAME pointing to ID.
   Allocations are done in 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_id_t *id,
          svn_node_kind_t kind,
          const svn_fs_x__id_part_t *txn_id,
          apr_pool_t *pool)
{
  node_revision_t *parent_noderev;

  /* Get the parent's node-revision. */
  SVN_ERR(get_node_revision(&parent_noderev, parent));

  /* Set the new entry. */
  return svn_fs_x__set_entry(parent->fs, txn_id, parent_noderev, name, id,
                             kind, pool);
}
Exemple #25
0
svn_error_t *
svn_fs_x__dag_dir_entry(svn_fs_dirent_t **dirent,
                         dag_node_t *node,
                         const char* name,
                         apr_pool_t *result_pool,
                         apr_pool_t *scratch_pool)
{
  node_revision_t *noderev;
  SVN_ERR(get_node_revision(&noderev, node));

  if (noderev->kind != svn_node_dir)
    return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
                            _("Can't get entries of non-directory"));

  /* Get a dirent hash for this directory. */
  return svn_fs_x__rep_contents_dir_entry(dirent, node->fs, noderev, name,
                                          result_pool, scratch_pool);
}
Exemple #26
0
svn_error_t *
svn_fs_x__dag_file_length(svn_filesize_t *length,
                          dag_node_t *file,
                          apr_pool_t *pool)
{
  node_revision_t *noderev;

  /* Make sure our node is a file. */
  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");

  /* Go get a fresh node-revision for FILE, and . */
  SVN_ERR(get_node_revision(&noderev, file));

  return svn_fs_x__file_length(length, noderev, pool);
}
Exemple #27
0
svn_error_t *
svn_fs_x__dag_has_descendants_with_mergeinfo(svn_boolean_t *do_they,
                                             dag_node_t *node)
{
  node_revision_t *noderev;

  if (node->kind != svn_node_dir)
    {
      *do_they = FALSE;
      return SVN_NO_ERROR;
    }

  SVN_ERR(get_node_revision(&noderev, node));
  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;
}
Exemple #28
0
svn_error_t *
svn_fs_x__dag_set_proplist(dag_node_t *node,
                           apr_hash_t *proplist,
                           apr_pool_t *pool)
{
  node_revision_t *noderev;

  /* 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->id, pool);
      return svn_error_createf
        (SVN_ERR_FS_NOT_MUTABLE, NULL,
         "Can't set proplist on *immutable* node-revision %s",
         idstr->data);
    }

  /* Go get a fresh NODE-REVISION for this node. */
  SVN_ERR(get_node_revision(&noderev, node));

  /* Set the new proplist. */
  return svn_fs_x__set_proplist(node->fs, noderev, proplist, pool);
}
Exemple #29
0
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_part_t *copy_id,
                          const svn_fs_x__id_part_t *txn_id,
                          svn_boolean_t is_parent_copyroot,
                          apr_pool_t *pool)
{
  dag_node_t *cur_entry; /* parent's current entry named NAME */
  const svn_fs_id_t *new_node_id; /* node id we'll put into NEW_NODE */
  svn_fs_t *fs = svn_fs_x__dag_get_fs(parent);
  apr_pool_t *subpool = svn_pool_create(pool);

  /* 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, pool, subpool));

  /* 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 = cur_entry->id;
    }
  else
    {
      node_revision_t *noderev, *parent_noderev;

      /* Go get a fresh NODE-REVISION for current child node. */
      SVN_ERR(get_node_revision(&noderev, cur_entry));

      if (is_parent_copyroot)
        {
          SVN_ERR(get_node_revision(&parent_noderev, parent));
          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_x__id_copy(cur_entry->id, pool);
      if (noderev->predecessor_count != -1)
        noderev->predecessor_count++;
      noderev->created_path = svn_fspath__join(parent_path, name, pool);

      SVN_ERR(svn_fs_x__create_successor(&new_node_id, fs, cur_entry->id,
                                          noderev, copy_id, txn_id, pool));

      /* 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,
                        pool));
    }

  /* Initialize the youngster. */
  svn_pool_destroy(subpool);
  return svn_fs_x__dag_get_node(child_p, fs, new_node_id, pool);
}