1 /* dag.c : DAG-like interface filesystem, private to libsvn_fs
3 * ====================================================================
4 * Copyright (c) 2000-2006 CollabNet. All rights reserved.
6 * This software is licensed as described in the file COPYING, which
7 * you should have received as part of this distribution. The terms
8 * are also available at http://subversion.tigris.org/license-1.html.
9 * If newer versions of this license are posted there, you may use a
10 * newer version instead, at your option.
12 * This software consists of voluntary contributions made by many
13 * individuals. For exact contribution history, see the revision
14 * history and logs, available at http://subversion.tigris.org/.
15 * ====================================================================
22 #include "svn_error.h"
24 #include "svn_props.h"
25 #include "svn_pools.h"
35 #include "../libsvn_fs/fs-loader.h"
37 #include "svn_private_config.h"
40 /* Initializing a filesystem. */
44 /* The filesystem this dag node came from. */
47 /* The node revision ID for this dag node, allocated in POOL. */
50 /* In the special case that this node is the root of a transaction
51 that has not yet been modified, the node revision ID for this dag
52 node's predecessor; otherwise NULL. (Used in
53 svn_fs_node_created_rev.) */
54 const svn_fs_id_t
*fresh_root_predecessor_id
;
56 /* The node's type (file, dir, etc.) */
59 /* The node's NODE-REVISION, or NULL if we haven't read it in yet.
60 This is allocated in this node's POOL.
62 If you're willing to respect all the rules above, you can munge
63 this yourself, but you're probably better off just calling
64 `get_node_revision' and `set_node_revision', which take care of
66 node_revision_t
*node_revision
;
68 /* the path at which this node was created. */
69 const char *created_path
;
74 /* Trivial helper/accessor functions. */
75 svn_node_kind_t
svn_fs_fs__dag_node_kind(dag_node_t
*node
)
82 svn_fs_fs__dag_get_id(dag_node_t
*node
)
89 svn_fs_fs__dag_get_created_path(dag_node_t
*node
)
91 return node
->created_path
;
96 svn_fs_fs__dag_get_fs(dag_node_t
*node
)
102 /* Dup NODEREV and all associated data into POOL.
103 Leaves the id and is_fresh_txn_root fields as zero bytes. */
104 static node_revision_t
*
105 copy_node_revision(node_revision_t
*noderev
,
108 node_revision_t
*nr
= apr_pcalloc(pool
, sizeof(*nr
));
109 nr
->kind
= noderev
->kind
;
110 if (noderev
->predecessor_id
)
111 nr
->predecessor_id
= svn_fs_fs__id_copy(noderev
->predecessor_id
, pool
);
112 nr
->predecessor_count
= noderev
->predecessor_count
;
113 if (noderev
->copyfrom_path
)
114 nr
->copyfrom_path
= apr_pstrdup(pool
, noderev
->copyfrom_path
);
115 nr
->copyfrom_rev
= noderev
->copyfrom_rev
;
116 nr
->copyroot_path
= apr_pstrdup(pool
, noderev
->copyroot_path
);
117 nr
->copyroot_rev
= noderev
->copyroot_rev
;
118 nr
->predecessor_count
= noderev
->predecessor_count
;
119 nr
->data_rep
= svn_fs_fs__rep_copy(noderev
->data_rep
, pool
);
120 nr
->prop_rep
= svn_fs_fs__rep_copy(noderev
->prop_rep
, pool
);
121 nr
->mergeinfo_count
= noderev
->mergeinfo_count
;
122 nr
->has_mergeinfo
= noderev
->has_mergeinfo
;
124 if (noderev
->created_path
)
125 nr
->created_path
= apr_pstrdup(pool
, noderev
->created_path
);
130 /* Set *NODEREV_P to the cached node-revision for NODE in POOL.
132 If you plan to change the contents of NODE, be careful! We're
133 handing you a pointer directly to our cached node-revision, not
134 your own copy. If you change it as part of some operation, but
135 then some Berkeley DB function deadlocks or gets an error, you'll
136 need to back out your changes, or else the cache will reflect
137 changes that never got committed. It's probably best not to change
138 the structure at all. */
140 get_node_revision(node_revision_t
**noderev_p
,
144 node_revision_t
*noderev
;
146 /* If we've already got a copy, there's no need to read it in. */
147 if (! node
->node_revision
)
149 SVN_ERR(svn_fs_fs__get_node_revision(&noderev
, node
->fs
,
151 node
->node_revision
= noderev
;
154 /* Now NODE->node_revision is set. */
155 *noderev_p
= node
->node_revision
;
160 svn_boolean_t
svn_fs_fs__dag_check_mutable(dag_node_t
*node
)
162 return (svn_fs_fs__id_txn_id(svn_fs_fs__dag_get_id(node
)) != NULL
);
167 svn_fs_fs__dag_get_node(dag_node_t
**node
,
169 const svn_fs_id_t
*id
,
172 dag_node_t
*new_node
;
173 node_revision_t
*noderev
;
175 /* Construct the node. */
176 new_node
= apr_pcalloc(pool
, sizeof(*new_node
));
178 new_node
->id
= svn_fs_fs__id_copy(id
, pool
);
180 /* Grab the contents so we can inspect the node's kind and created path. */
181 SVN_ERR(get_node_revision(&noderev
, new_node
, pool
));
183 /* Initialize the KIND and CREATED_PATH attributes */
184 new_node
->kind
= noderev
->kind
;
185 new_node
->created_path
= apr_pstrdup(pool
, noderev
->created_path
);
187 if (noderev
->is_fresh_txn_root
)
188 new_node
->fresh_root_predecessor_id
= noderev
->predecessor_id
;
190 new_node
->fresh_root_predecessor_id
= NULL
;
192 /* Return a fresh new node */
199 svn_fs_fs__dag_get_revision(svn_revnum_t
*rev
,
203 /* In the special case that this is an unmodified transaction root,
204 we need to actually get the revision of the noderev's predecessor
205 (the revision root); see Issue #2608. */
206 const svn_fs_id_t
*correct_id
= node
->fresh_root_predecessor_id
207 ? node
->fresh_root_predecessor_id
: node
->id
;
209 /* Look up the committed revision from the Node-ID. */
210 *rev
= svn_fs_fs__id_rev(correct_id
);
217 svn_fs_fs__dag_get_predecessor_id(const svn_fs_id_t
**id_p
,
221 node_revision_t
*noderev
;
223 SVN_ERR(get_node_revision(&noderev
, node
, pool
));
224 *id_p
= noderev
->predecessor_id
;
230 svn_fs_fs__dag_get_predecessor_count(int *count
,
234 node_revision_t
*noderev
;
236 SVN_ERR(get_node_revision(&noderev
, node
, pool
));
237 *count
= noderev
->predecessor_count
;
242 svn_fs_fs__dag_get_mergeinfo_count(apr_int64_t
*count
,
246 node_revision_t
*noderev
;
248 SVN_ERR(get_node_revision(&noderev
, node
, pool
));
249 *count
= noderev
->mergeinfo_count
;
254 svn_fs_fs__dag_has_mergeinfo(svn_boolean_t
*has_mergeinfo
,
258 node_revision_t
*noderev
;
260 SVN_ERR(get_node_revision(&noderev
, node
, pool
));
261 *has_mergeinfo
= noderev
->has_mergeinfo
;
266 svn_fs_fs__dag_has_descendants_with_mergeinfo(svn_boolean_t
*do_they
,
270 node_revision_t
*noderev
;
272 if (node
->kind
!= svn_node_dir
)
278 SVN_ERR(get_node_revision(&noderev
, node
, pool
));
279 if (noderev
->mergeinfo_count
> 1)
281 else if (noderev
->mergeinfo_count
== 1 && !noderev
->has_mergeinfo
)
289 /*** Directory node functions ***/
291 /* Some of these are helpers for functions outside this section. */
293 /* Set *ID_P to the node-id for entry NAME in PARENT. If no such
294 entry, set *ID_P to NULL but do not error. The node-id is
295 allocated in POOL. */
297 dir_entry_id_from_node(const svn_fs_id_t
**id_p
,
303 svn_fs_dirent_t
*dirent
;
304 apr_pool_t
*subpool
= svn_pool_create(pool
);
306 SVN_ERR(svn_fs_fs__dag_dir_entries(&entries
, parent
, subpool
, pool
));
308 dirent
= apr_hash_get(entries
, name
, APR_HASH_KEY_STRING
);
312 *id_p
= dirent
? svn_fs_fs__id_copy(dirent
->id
, pool
) : NULL
;
314 svn_pool_destroy(subpool
);
320 /* Add or set in PARENT a directory entry NAME pointing to ID.
321 Allocations are done in POOL.
324 - PARENT is a mutable directory.
325 - ID does not refer to an ancestor of parent
326 - NAME is a single path component
329 set_entry(dag_node_t
*parent
,
331 const svn_fs_id_t
*id
,
332 svn_node_kind_t kind
,
336 node_revision_t
*parent_noderev
;
338 /* Get the parent's node-revision. */
339 SVN_ERR(get_node_revision(&parent_noderev
, parent
, pool
));
341 /* Set the new entry. */
342 SVN_ERR(svn_fs_fs__set_entry(parent
->fs
, txn_id
, parent_noderev
, name
, id
,
349 /* Make a new entry named NAME in PARENT. If IS_DIR is true, then the
350 node revision the new entry points to will be a directory, else it
351 will be a file. The new node will be allocated in POOL. PARENT
352 must be mutable, and must not have an entry named NAME. */
354 make_entry(dag_node_t
**child_p
,
356 const char *parent_path
,
358 svn_boolean_t is_dir
,
362 const svn_fs_id_t
*new_node_id
;
363 node_revision_t new_noderev
, *parent_noderev
;
365 /* Make sure that NAME is a single path component. */
366 if (! svn_path_is_single_path_component(name
))
367 return svn_error_createf
368 (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT
, NULL
,
369 _("Attempted to create a node with an illegal name '%s'"), name
);
371 /* Make sure that parent is a directory */
372 if (parent
->kind
!= svn_node_dir
)
373 return svn_error_create
374 (SVN_ERR_FS_NOT_DIRECTORY
, NULL
,
375 _("Attempted to create entry in non-directory parent"));
377 /* Check that the parent is mutable. */
378 if (! svn_fs_fs__dag_check_mutable(parent
))
379 return svn_error_createf
380 (SVN_ERR_FS_NOT_MUTABLE
, NULL
,
381 _("Attempted to clone child of non-mutable node"));
383 /* Create the new node's NODE-REVISION */
384 memset(&new_noderev
, 0, sizeof(new_noderev
));
385 new_noderev
.kind
= is_dir
? svn_node_dir
: svn_node_file
;
386 new_noderev
.created_path
= svn_path_join(parent_path
, name
, pool
);
388 SVN_ERR(get_node_revision(&parent_noderev
, parent
, pool
));
389 new_noderev
.copyroot_path
= apr_pstrdup(pool
,
390 parent_noderev
->copyroot_path
);
391 new_noderev
.copyroot_rev
= parent_noderev
->copyroot_rev
;
392 new_noderev
.copyfrom_rev
= SVN_INVALID_REVNUM
;
393 new_noderev
.copyfrom_path
= NULL
;
395 SVN_ERR(svn_fs_fs__create_node
396 (&new_node_id
, svn_fs_fs__dag_get_fs(parent
), &new_noderev
,
397 svn_fs_fs__id_copy_id(svn_fs_fs__dag_get_id(parent
)),
400 /* Create a new dag_node_t for our new node */
401 SVN_ERR(svn_fs_fs__dag_get_node(child_p
, svn_fs_fs__dag_get_fs(parent
),
404 /* We can safely call set_entry because we already know that
405 PARENT is mutable, and we just created CHILD, so we know it has
406 no ancestors (therefore, PARENT cannot be an ancestor of CHILD) */
407 SVN_ERR(set_entry(parent
, name
, svn_fs_fs__dag_get_id(*child_p
),
408 new_noderev
.kind
, txn_id
, pool
));
415 svn_fs_fs__dag_dir_entries(apr_hash_t
**entries
,
418 apr_pool_t
*node_pool
)
420 node_revision_t
*noderev
;
422 SVN_ERR(get_node_revision(&noderev
, node
, node_pool
));
424 if (noderev
->kind
!= svn_node_dir
)
425 return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY
, NULL
,
426 _("Can't get entries of non-directory"));
428 return svn_fs_fs__rep_contents_dir(entries
, node
->fs
, noderev
, pool
);
433 svn_fs_fs__dag_set_entry(dag_node_t
*node
,
434 const char *entry_name
,
435 const svn_fs_id_t
*id
,
436 svn_node_kind_t kind
,
440 /* Check it's a directory. */
441 if (node
->kind
!= svn_node_dir
)
442 return svn_error_create
443 (SVN_ERR_FS_NOT_DIRECTORY
, NULL
,
444 _("Attempted to set entry in non-directory node"));
446 /* Check it's mutable. */
447 if (! svn_fs_fs__dag_check_mutable(node
))
448 return svn_error_create
449 (SVN_ERR_FS_NOT_MUTABLE
, NULL
,
450 _("Attempted to set entry in immutable node"));
452 return set_entry(node
, entry_name
, id
, kind
, txn_id
, pool
);
460 svn_fs_fs__dag_get_proplist(apr_hash_t
**proplist_p
,
464 node_revision_t
*noderev
;
465 apr_hash_t
*proplist
= NULL
;
467 SVN_ERR(get_node_revision(&noderev
, node
, pool
));
469 SVN_ERR(svn_fs_fs__get_proplist(&proplist
, node
->fs
,
472 *proplist_p
= proplist
;
479 svn_fs_fs__dag_set_proplist(dag_node_t
*node
,
480 apr_hash_t
*proplist
,
483 node_revision_t
*noderev
;
485 /* Sanity check: this node better be mutable! */
486 if (! svn_fs_fs__dag_check_mutable(node
))
488 svn_string_t
*idstr
= svn_fs_fs__id_unparse(node
->id
, pool
);
489 return svn_error_createf
490 (SVN_ERR_FS_NOT_MUTABLE
, NULL
,
491 "Can't set proplist on *immutable* node-revision %s",
495 /* Go get a fresh NODE-REVISION for this node. */
496 SVN_ERR(get_node_revision(&noderev
, node
, pool
));
498 /* Set the new proplist. */
499 SVN_ERR(svn_fs_fs__set_proplist(node
->fs
, noderev
, proplist
, pool
));
506 svn_fs_fs__dag_increment_mergeinfo_count(dag_node_t
*node
,
507 apr_int64_t increment
,
510 node_revision_t
*noderev
;
512 /* Sanity check: this node better be mutable! */
513 if (! svn_fs_fs__dag_check_mutable(node
))
515 svn_string_t
*idstr
= svn_fs_fs__id_unparse(node
->id
, pool
);
516 return svn_error_createf
517 (SVN_ERR_FS_NOT_MUTABLE
, NULL
,
518 "Can't increment mergeinfo count on *immutable* node-revision %s",
525 /* Go get a fresh NODE-REVISION for this node. */
526 SVN_ERR(get_node_revision(&noderev
, node
, pool
));
528 noderev
->mergeinfo_count
+= increment
;
529 if (noderev
->mergeinfo_count
< 0)
531 svn_string_t
*idstr
= svn_fs_fs__id_unparse(node
->id
, pool
);
532 return svn_error_createf
533 (SVN_ERR_FS_CORRUPT
, NULL
,
535 _("Can't increment mergeinfo count on node-revision %%s "
536 "to negative value %%%s"),
538 idstr
->data
, noderev
->mergeinfo_count
);
540 if (noderev
->mergeinfo_count
> 1 && noderev
->kind
== svn_node_file
)
542 svn_string_t
*idstr
= svn_fs_fs__id_unparse(node
->id
, pool
);
543 return svn_error_createf
544 (SVN_ERR_FS_CORRUPT
, NULL
,
546 _("Can't increment mergeinfo count on *file* "
547 "node-revision %%s to %%%s (> 1)"),
549 idstr
->data
, noderev
->mergeinfo_count
);
553 SVN_ERR(svn_fs_fs__put_node_revision(node
->fs
, noderev
->id
,
554 noderev
, FALSE
, pool
));
560 svn_fs_fs__dag_set_has_mergeinfo(dag_node_t
*node
,
561 svn_boolean_t has_mergeinfo
,
564 node_revision_t
*noderev
;
566 /* Sanity check: this node better be mutable! */
567 if (! svn_fs_fs__dag_check_mutable(node
))
569 svn_string_t
*idstr
= svn_fs_fs__id_unparse(node
->id
, pool
);
570 return svn_error_createf
571 (SVN_ERR_FS_NOT_MUTABLE
, NULL
,
572 "Can't set mergeinfo flag on *immutable* node-revision %s",
576 /* Go get a fresh NODE-REVISION for this node. */
577 SVN_ERR(get_node_revision(&noderev
, node
, pool
));
579 noderev
->has_mergeinfo
= has_mergeinfo
;
582 SVN_ERR(svn_fs_fs__put_node_revision(node
->fs
, noderev
->id
,
583 noderev
, FALSE
, pool
));
592 svn_fs_fs__dag_revision_root(dag_node_t
**node_p
,
597 svn_fs_id_t
*root_id
;
599 SVN_ERR(svn_fs_fs__rev_get_root(&root_id
, fs
, rev
, pool
));
600 return svn_fs_fs__dag_get_node(node_p
, fs
, root_id
, pool
);
605 svn_fs_fs__dag_txn_root(dag_node_t
**node_p
,
610 const svn_fs_id_t
*root_id
, *ignored
;
612 SVN_ERR(svn_fs_fs__get_txn_ids(&root_id
, &ignored
, fs
, txn_id
, pool
));
613 return svn_fs_fs__dag_get_node(node_p
, fs
, root_id
, pool
);
618 svn_fs_fs__dag_txn_base_root(dag_node_t
**node_p
,
623 const svn_fs_id_t
*base_root_id
, *ignored
;
625 SVN_ERR(svn_fs_fs__get_txn_ids(&ignored
, &base_root_id
, fs
, txn_id
, pool
));
626 return svn_fs_fs__dag_get_node(node_p
, fs
, base_root_id
, pool
);
631 svn_fs_fs__dag_clone_child(dag_node_t
**child_p
,
633 const char *parent_path
,
637 svn_boolean_t is_parent_copyroot
,
640 dag_node_t
*cur_entry
; /* parent's current entry named NAME */
641 const svn_fs_id_t
*new_node_id
; /* node id we'll put into NEW_NODE */
642 svn_fs_t
*fs
= svn_fs_fs__dag_get_fs(parent
);
644 /* First check that the parent is mutable. */
645 if (! svn_fs_fs__dag_check_mutable(parent
))
646 return svn_error_createf
647 (SVN_ERR_FS_NOT_MUTABLE
, NULL
,
648 "Attempted to clone child of non-mutable node");
650 /* Make sure that NAME is a single path component. */
651 if (! svn_path_is_single_path_component(name
))
652 return svn_error_createf
653 (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT
, NULL
,
654 "Attempted to make a child clone with an illegal name '%s'", name
);
656 /* Find the node named NAME in PARENT's entries list if it exists. */
657 SVN_ERR(svn_fs_fs__dag_open(&cur_entry
, parent
, name
, pool
));
659 /* Check for mutability in the node we found. If it's mutable, we
660 don't need to clone it. */
661 if (svn_fs_fs__dag_check_mutable(cur_entry
))
663 /* This has already been cloned */
664 new_node_id
= cur_entry
->id
;
668 node_revision_t
*noderev
, *parent_noderev
;
670 /* Go get a fresh NODE-REVISION for current child node. */
671 SVN_ERR(get_node_revision(&noderev
, cur_entry
, pool
));
673 if (is_parent_copyroot
)
675 SVN_ERR(get_node_revision(&parent_noderev
, parent
, pool
));
676 noderev
->copyroot_rev
= parent_noderev
->copyroot_rev
;
677 noderev
->copyroot_path
= apr_pstrdup(pool
,
678 parent_noderev
->copyroot_path
);
681 noderev
->copyfrom_path
= NULL
;
682 noderev
->copyfrom_rev
= SVN_INVALID_REVNUM
;
684 noderev
->predecessor_id
= svn_fs_fs__id_copy(cur_entry
->id
, pool
);
685 if (noderev
->predecessor_count
!= -1)
686 noderev
->predecessor_count
++;
687 noderev
->created_path
= svn_path_join(parent_path
, name
, pool
);
689 SVN_ERR(svn_fs_fs__create_successor(&new_node_id
, fs
, cur_entry
->id
,
690 noderev
, copy_id
, txn_id
, pool
));
692 /* Replace the ID in the parent's ENTRY list with the ID which
693 refers to the mutable clone of this child. */
694 SVN_ERR(set_entry(parent
, name
, new_node_id
, noderev
->kind
, txn_id
,
698 /* Initialize the youngster. */
699 return svn_fs_fs__dag_get_node(child_p
, fs
, new_node_id
, pool
);
705 svn_fs_fs__dag_clone_root(dag_node_t
**root_p
,
710 const svn_fs_id_t
*base_root_id
, *root_id
;
712 /* Get the node ID's of the root directories of the transaction and
713 its base revision. */
714 SVN_ERR(svn_fs_fs__get_txn_ids(&root_id
, &base_root_id
, fs
, txn_id
, pool
));
716 /* Oh, give me a clone...
717 (If they're the same, we haven't cloned the transaction's root
719 if (svn_fs_fs__id_eq(root_id
, base_root_id
))
724 /* One way or another, root_id now identifies a cloned root node. */
725 SVN_ERR(svn_fs_fs__dag_get_node(root_p
, fs
, root_id
, pool
));
728 * (Sung to the tune of "Home, Home on the Range", with thanks to
729 * Randall Garrett and Isaac Asimov.)
737 svn_fs_fs__dag_delete(dag_node_t
*parent
,
742 node_revision_t
*parent_noderev
;
744 svn_fs_t
*fs
= parent
->fs
;
745 svn_fs_dirent_t
*dirent
;
749 /* Make sure parent is a directory. */
750 if (parent
->kind
!= svn_node_dir
)
751 return svn_error_createf
752 (SVN_ERR_FS_NOT_DIRECTORY
, NULL
,
753 "Attempted to delete entry '%s' from *non*-directory node", name
);
755 /* Make sure parent is mutable. */
756 if (! svn_fs_fs__dag_check_mutable(parent
))
757 return svn_error_createf
758 (SVN_ERR_FS_NOT_MUTABLE
, NULL
,
759 "Attempted to delete entry '%s' from immutable directory node", name
);
761 /* Make sure that NAME is a single path component. */
762 if (! svn_path_is_single_path_component(name
))
763 return svn_error_createf
764 (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT
, NULL
,
765 "Attempted to delete a node with an illegal name '%s'", name
);
767 /* Get a fresh NODE-REVISION for the parent node. */
768 SVN_ERR(get_node_revision(&parent_noderev
, parent
, pool
));
770 subpool
= svn_pool_create(pool
);
772 /* Get a dirent hash for this directory. */
773 SVN_ERR(svn_fs_fs__rep_contents_dir(&entries
, fs
, parent_noderev
, subpool
));
775 /* Find name in the ENTRIES hash. */
776 dirent
= apr_hash_get(entries
, name
, APR_HASH_KEY_STRING
);
778 /* If we never found ID in ENTRIES (perhaps because there are no
779 ENTRIES, perhaps because ID just isn't in the existing ENTRIES
780 ... it doesn't matter), return an error. */
782 return svn_error_createf
783 (SVN_ERR_FS_NO_SUCH_ENTRY
, NULL
,
784 "Delete failed--directory has no entry '%s'", name
);
786 /* Copy the ID out of the subpool and release the rest of the
787 directory listing. */
788 id
= svn_fs_fs__id_copy(dirent
->id
, pool
);
789 svn_pool_destroy(subpool
);
791 /* If mutable, remove it and any mutable children from db. */
792 SVN_ERR(svn_fs_fs__dag_delete_if_mutable(parent
->fs
, id
, pool
));
794 /* Remove this entry from its parent's entries list. */
795 SVN_ERR(svn_fs_fs__set_entry(parent
->fs
, txn_id
, parent_noderev
, name
,
796 NULL
, svn_node_unknown
, pool
));
803 svn_fs_fs__dag_remove_node(svn_fs_t
*fs
,
804 const svn_fs_id_t
*id
,
809 /* Fetch the node. */
810 SVN_ERR(svn_fs_fs__dag_get_node(&node
, fs
, id
, pool
));
812 /* If immutable, do nothing and return immediately. */
813 if (! svn_fs_fs__dag_check_mutable(node
))
814 return svn_error_createf(SVN_ERR_FS_NOT_MUTABLE
, NULL
,
815 "Attempted removal of immutable node");
817 /* Delete the node revision. */
818 SVN_ERR(svn_fs_fs__delete_node_revision(fs
, id
, pool
));
825 svn_fs_fs__dag_delete_if_mutable(svn_fs_t
*fs
,
826 const svn_fs_id_t
*id
,
832 SVN_ERR(svn_fs_fs__dag_get_node(&node
, fs
, id
, pool
));
834 /* If immutable, do nothing and return immediately. */
835 if (! svn_fs_fs__dag_check_mutable(node
))
838 /* Else it's mutable. Recurse on directories... */
839 if (node
->kind
== svn_node_dir
)
842 apr_hash_index_t
*hi
;
844 /* Loop over hash entries */
845 SVN_ERR(svn_fs_fs__dag_dir_entries(&entries
, node
, pool
, pool
));
848 for (hi
= apr_hash_first(pool
, entries
);
850 hi
= apr_hash_next(hi
))
853 svn_fs_dirent_t
*dirent
;
855 apr_hash_this(hi
, NULL
, NULL
, &val
);
857 SVN_ERR(svn_fs_fs__dag_delete_if_mutable(fs
, dirent
->id
,
863 /* ... then delete the node itself, after deleting any mutable
864 representations and strings it points to. */
865 SVN_ERR(svn_fs_fs__dag_remove_node(fs
, id
, pool
));
871 svn_fs_fs__dag_make_file(dag_node_t
**child_p
,
873 const char *parent_path
,
878 /* Call our little helper function */
879 return make_entry(child_p
, parent
, parent_path
, name
, FALSE
, txn_id
, pool
);
884 svn_fs_fs__dag_make_dir(dag_node_t
**child_p
,
886 const char *parent_path
,
891 /* Call our little helper function */
892 return make_entry(child_p
, parent
, parent_path
, name
, TRUE
, txn_id
, pool
);
897 svn_fs_fs__dag_get_contents(svn_stream_t
**contents_p
,
901 node_revision_t
*noderev
;
902 svn_stream_t
*contents
;
904 /* Make sure our node is a file. */
905 if (file
->kind
!= svn_node_file
)
906 return svn_error_createf
907 (SVN_ERR_FS_NOT_FILE
, NULL
,
908 "Attempted to get textual contents of a *non*-file node");
910 /* Go get a fresh node-revision for FILE. */
911 SVN_ERR(get_node_revision(&noderev
, file
, pool
));
913 /* Get a stream to the contents. */
914 SVN_ERR(svn_fs_fs__get_contents(&contents
, file
->fs
,
917 *contents_p
= contents
;
924 svn_fs_fs__dag_get_file_delta_stream(svn_txdelta_stream_t
**stream_p
,
929 node_revision_t
*src_noderev
;
930 node_revision_t
*tgt_noderev
;
932 /* Make sure our nodes are files. */
933 if ((source
&& source
->kind
!= svn_node_file
)
934 || target
->kind
!= svn_node_file
)
935 return svn_error_createf
936 (SVN_ERR_FS_NOT_FILE
, NULL
,
937 "Attempted to get textual contents of a *non*-file node");
939 /* Go get fresh node-revisions for the nodes. */
941 SVN_ERR(get_node_revision(&src_noderev
, source
, pool
));
944 SVN_ERR(get_node_revision(&tgt_noderev
, target
, pool
));
946 /* Get the delta stream. */
947 SVN_ERR(svn_fs_fs__get_file_delta_stream(stream_p
, target
->fs
,
948 src_noderev
, tgt_noderev
, pool
));
955 svn_fs_fs__dag_file_length(svn_filesize_t
*length
,
959 node_revision_t
*noderev
;
961 /* Make sure our node is a file. */
962 if (file
->kind
!= svn_node_file
)
963 return svn_error_createf
964 (SVN_ERR_FS_NOT_FILE
, NULL
,
965 "Attempted to get length of a *non*-file node");
967 /* Go get a fresh node-revision for FILE, and . */
968 SVN_ERR(get_node_revision(&noderev
, file
, pool
));
970 SVN_ERR(svn_fs_fs__file_length(length
, noderev
, pool
));
977 svn_fs_fs__dag_file_checksum(unsigned char digest
[],
981 node_revision_t
*noderev
;
983 if (file
->kind
!= svn_node_file
)
984 return svn_error_createf
985 (SVN_ERR_FS_NOT_FILE
, NULL
,
986 "Attempted to get checksum of a *non*-file node");
988 SVN_ERR(get_node_revision(&noderev
, file
, pool
));
990 SVN_ERR(svn_fs_fs__file_checksum(digest
, noderev
, pool
));
997 svn_fs_fs__dag_get_edit_stream(svn_stream_t
**contents
,
1001 node_revision_t
*noderev
;
1004 /* Make sure our node is a file. */
1005 if (file
->kind
!= svn_node_file
)
1006 return svn_error_createf
1007 (SVN_ERR_FS_NOT_FILE
, NULL
,
1008 "Attempted to set textual contents of a *non*-file node");
1010 /* Make sure our node is mutable. */
1011 if (! svn_fs_fs__dag_check_mutable(file
))
1012 return svn_error_createf
1013 (SVN_ERR_FS_NOT_MUTABLE
, NULL
,
1014 "Attempted to set textual contents of an immutable node");
1016 /* Get the node revision. */
1017 SVN_ERR(get_node_revision(&noderev
, file
, pool
));
1019 SVN_ERR(svn_fs_fs__set_contents(&ws
, file
->fs
, noderev
, pool
));
1023 return SVN_NO_ERROR
;
1029 svn_fs_fs__dag_finalize_edits(dag_node_t
*file
,
1030 const char *checksum
,
1033 unsigned char digest
[APR_MD5_DIGESTSIZE
];
1038 SVN_ERR(svn_fs_fs__dag_file_checksum(digest
, file
, pool
));
1039 hex
= svn_md5_digest_to_cstring(digest
, pool
);
1040 if (hex
&& strcmp(checksum
, hex
) != 0)
1041 return svn_error_createf(SVN_ERR_CHECKSUM_MISMATCH
, NULL
,
1042 _("Checksum mismatch, file '%s':\n"
1045 file
->created_path
, checksum
, hex
);
1048 return SVN_NO_ERROR
;
1053 svn_fs_fs__dag_dup(dag_node_t
*node
,
1056 /* Allocate our new node. */
1057 dag_node_t
*new_node
= apr_pcalloc(pool
, sizeof(*new_node
));
1059 new_node
->fs
= node
->fs
;
1060 new_node
->id
= svn_fs_fs__id_copy(node
->id
, pool
);
1061 new_node
->kind
= node
->kind
;
1062 new_node
->created_path
= apr_pstrdup(pool
, node
->created_path
);
1064 /* Only copy cached node_revision_t for immutable nodes. */
1065 if (node
->node_revision
&& !svn_fs_fs__dag_check_mutable(node
))
1067 new_node
->node_revision
= copy_node_revision(node
->node_revision
, pool
);
1068 new_node
->node_revision
->id
=
1069 svn_fs_fs__id_copy(node
->node_revision
->id
, pool
);
1070 new_node
->node_revision
->is_fresh_txn_root
=
1071 node
->node_revision
->is_fresh_txn_root
;
1078 svn_fs_fs__dag_open(dag_node_t
**child_p
,
1083 const svn_fs_id_t
*node_id
;
1085 /* Ensure that NAME exists in PARENT's entry list. */
1086 SVN_ERR(dir_entry_id_from_node(&node_id
, parent
, name
, pool
));
1088 return svn_error_createf
1089 (SVN_ERR_FS_NOT_FOUND
, NULL
,
1090 "Attempted to open non-existent child node '%s'", name
);
1092 /* Make sure that NAME is a single path component. */
1093 if (! svn_path_is_single_path_component(name
))
1094 return svn_error_createf
1095 (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT
, NULL
,
1096 "Attempted to open node with an illegal name '%s'", name
);
1098 /* Now get the node that was requested. */
1099 return svn_fs_fs__dag_get_node(child_p
, svn_fs_fs__dag_get_fs(parent
),
1105 svn_fs_fs__dag_copy(dag_node_t
*to_node
,
1107 dag_node_t
*from_node
,
1108 svn_boolean_t preserve_history
,
1109 svn_revnum_t from_rev
,
1110 const char *from_path
,
1114 const svn_fs_id_t
*id
;
1116 if (preserve_history
)
1118 node_revision_t
*from_noderev
, *to_noderev
;
1119 const char *copy_id
;
1120 const svn_fs_id_t
*src_id
= svn_fs_fs__dag_get_id(from_node
);
1121 svn_fs_t
*fs
= svn_fs_fs__dag_get_fs(from_node
);
1123 /* Make a copy of the original node revision. */
1124 SVN_ERR(get_node_revision(&from_noderev
, from_node
, pool
));
1125 to_noderev
= copy_node_revision(from_noderev
, pool
);
1127 /* Reserve a copy ID for this new copy. */
1128 SVN_ERR(svn_fs_fs__reserve_copy_id(©_id
, fs
, txn_id
, pool
));
1130 /* Create a successor with its predecessor pointing at the copy
1132 to_noderev
->predecessor_id
= svn_fs_fs__id_copy(src_id
, pool
);
1133 if (to_noderev
->predecessor_count
!= -1)
1134 to_noderev
->predecessor_count
++;
1135 to_noderev
->created_path
=
1136 svn_path_join(svn_fs_fs__dag_get_created_path(to_node
), entry
,
1138 to_noderev
->copyfrom_path
= apr_pstrdup(pool
, from_path
);
1139 to_noderev
->copyfrom_rev
= from_rev
;
1141 /* Set the copyroot equal to our own id. */
1142 to_noderev
->copyroot_path
= NULL
;
1144 SVN_ERR(svn_fs_fs__create_successor(&id
, fs
, src_id
, to_noderev
,
1145 copy_id
, txn_id
, pool
));
1148 else /* don't preserve history */
1150 id
= svn_fs_fs__dag_get_id(from_node
);
1153 /* Set the entry in to_node to the new id. */
1154 SVN_ERR(svn_fs_fs__dag_set_entry(to_node
, entry
, id
, from_node
->kind
,
1157 return SVN_NO_ERROR
;
1162 /*** Comparison. ***/
1165 svn_fs_fs__dag_things_different(svn_boolean_t
*props_changed
,
1166 svn_boolean_t
*contents_changed
,
1171 node_revision_t
*noderev1
, *noderev2
;
1173 /* If we have no place to store our results, don't bother doing
1175 if (! props_changed
&& ! contents_changed
)
1176 return SVN_NO_ERROR
;
1178 /* The node revision skels for these two nodes. */
1179 SVN_ERR(get_node_revision(&noderev1
, node1
, pool
));
1180 SVN_ERR(get_node_revision(&noderev2
, node2
, pool
));
1182 /* Compare property keys. */
1183 if (props_changed
!= NULL
)
1184 *props_changed
= (! svn_fs_fs__noderev_same_rep_key(noderev1
->prop_rep
,
1185 noderev2
->prop_rep
));
1187 /* Compare contents keys. */
1188 if (contents_changed
!= NULL
)
1190 (! svn_fs_fs__noderev_same_rep_key(noderev1
->data_rep
,
1191 noderev2
->data_rep
));
1193 return SVN_NO_ERROR
;
1197 svn_fs_fs__dag_get_copyroot(svn_revnum_t
*rev
,
1202 node_revision_t
*noderev
;
1204 /* Go get a fresh node-revision for FILE. */
1205 SVN_ERR(get_node_revision(&noderev
, node
, pool
));
1207 *rev
= noderev
->copyroot_rev
;
1208 *path
= noderev
->copyroot_path
;
1210 return SVN_NO_ERROR
;
1214 svn_fs_fs__dag_get_copyfrom_rev(svn_revnum_t
*rev
,
1218 node_revision_t
*noderev
;
1220 /* Go get a fresh node-revision for FILE. */
1221 SVN_ERR(get_node_revision(&noderev
, node
, pool
));
1223 *rev
= noderev
->copyfrom_rev
;
1225 return SVN_NO_ERROR
;
1229 svn_fs_fs__dag_get_copyfrom_path(const char **path
,
1233 node_revision_t
*noderev
;
1235 /* Go get a fresh node-revision for FILE. */
1236 SVN_ERR(get_node_revision(&noderev
, node
, pool
));
1238 *path
= noderev
->copyfrom_path
;
1240 return SVN_NO_ERROR
;