1 /* dag.c : DAG-like interface filesystem, private to libsvn_fs
3 * ====================================================================
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
20 * ====================================================================
26 #include "svn_error.h"
28 #include "svn_props.h"
29 #include "svn_pools.h"
37 #include "../libsvn_fs/fs-loader.h"
39 #include "private/svn_fspath.h"
40 #include "svn_private_config.h"
41 #include "private/svn_temp_serializer.h"
42 #include "temp_serializer.h"
45 /* Initializing a filesystem. */
49 /* The filesystem this dag node came from. */
52 /* The node revision ID for this dag node, allocated in POOL. */
55 /* In the special case that this node is the root of a transaction
56 that has not yet been modified, the node revision ID for this dag
57 node's predecessor; otherwise NULL. (Used in
58 svn_fs_node_created_rev.) */
59 const svn_fs_id_t *fresh_root_predecessor_id;
61 /* The node's type (file, dir, etc.) */
64 /* The node's NODE-REVISION, or NULL if we haven't read it in yet.
65 This is allocated in this node's POOL.
67 If you're willing to respect all the rules above, you can munge
68 this yourself, but you're probably better off just calling
69 `get_node_revision' and `set_node_revision', which take care of
71 node_revision_t *node_revision;
73 /* The pool to allocate NODE_REVISION in. */
74 apr_pool_t *node_pool;
76 /* the path at which this node was created. */
77 const char *created_path;
82 /* Trivial helper/accessor functions. */
83 svn_node_kind_t svn_fs_fs__dag_node_kind(dag_node_t *node)
90 svn_fs_fs__dag_get_id(const dag_node_t *node)
97 svn_fs_fs__dag_get_created_path(dag_node_t *node)
99 return node->created_path;
104 svn_fs_fs__dag_get_fs(dag_node_t *node)
110 svn_fs_fs__dag_set_fs(dag_node_t *node, svn_fs_t *fs)
116 /* Dup NODEREV and all associated data into POOL.
117 Leaves the id and is_fresh_txn_root fields as zero bytes. */
118 static node_revision_t *
119 copy_node_revision(node_revision_t *noderev,
122 node_revision_t *nr = apr_pcalloc(pool, sizeof(*nr));
123 nr->kind = noderev->kind;
124 if (noderev->predecessor_id)
125 nr->predecessor_id = svn_fs_fs__id_copy(noderev->predecessor_id, pool);
126 nr->predecessor_count = noderev->predecessor_count;
127 if (noderev->copyfrom_path)
128 nr->copyfrom_path = apr_pstrdup(pool, noderev->copyfrom_path);
129 nr->copyfrom_rev = noderev->copyfrom_rev;
130 nr->copyroot_path = apr_pstrdup(pool, noderev->copyroot_path);
131 nr->copyroot_rev = noderev->copyroot_rev;
132 nr->data_rep = svn_fs_fs__rep_copy(noderev->data_rep, pool);
133 nr->prop_rep = svn_fs_fs__rep_copy(noderev->prop_rep, pool);
134 nr->mergeinfo_count = noderev->mergeinfo_count;
135 nr->has_mergeinfo = noderev->has_mergeinfo;
137 if (noderev->created_path)
138 nr->created_path = apr_pstrdup(pool, noderev->created_path);
143 /* Set *NODEREV_P to the cached node-revision for NODE.
144 If the node-revision was not already cached in NODE, read it in,
145 allocating the cache in NODE->NODE_POOL.
147 If you plan to change the contents of NODE, be careful! We're
148 handing you a pointer directly to our cached node-revision, not
149 your own copy. If you change it as part of some operation, but
150 then some Berkeley DB function deadlocks or gets an error, you'll
151 need to back out your changes, or else the cache will reflect
152 changes that never got committed. It's probably best not to change
153 the structure at all. */
155 get_node_revision(node_revision_t **noderev_p,
158 /* If we've already got a copy, there's no need to read it in. */
159 if (! node->node_revision)
161 node_revision_t *noderev;
163 SVN_ERR(svn_fs_fs__get_node_revision(&noderev, node->fs,
164 node->id, node->node_pool));
165 node->node_revision = noderev;
168 /* Now NODE->node_revision is set. */
169 *noderev_p = node->node_revision;
174 svn_boolean_t svn_fs_fs__dag_check_mutable(const dag_node_t *node)
176 return (svn_fs_fs__id_txn_id(svn_fs_fs__dag_get_id(node)) != NULL);
181 svn_fs_fs__dag_get_node(dag_node_t **node,
183 const svn_fs_id_t *id,
186 dag_node_t *new_node;
187 node_revision_t *noderev;
189 /* Construct the node. */
190 new_node = apr_pcalloc(pool, sizeof(*new_node));
192 new_node->id = svn_fs_fs__id_copy(id, pool);
194 /* Grab the contents so we can inspect the node's kind and created path. */
195 new_node->node_pool = pool;
196 SVN_ERR(get_node_revision(&noderev, new_node));
198 /* Initialize the KIND and CREATED_PATH attributes */
199 new_node->kind = noderev->kind;
200 new_node->created_path = apr_pstrdup(pool, noderev->created_path);
202 if (noderev->is_fresh_txn_root)
203 new_node->fresh_root_predecessor_id = noderev->predecessor_id;
205 new_node->fresh_root_predecessor_id = NULL;
207 /* Return a fresh new node */
214 svn_fs_fs__dag_get_revision(svn_revnum_t *rev,
218 /* In the special case that this is an unmodified transaction root,
219 we need to actually get the revision of the noderev's predecessor
220 (the revision root); see Issue #2608. */
221 const svn_fs_id_t *correct_id = node->fresh_root_predecessor_id
222 ? node->fresh_root_predecessor_id : node->id;
224 /* Look up the committed revision from the Node-ID. */
225 *rev = svn_fs_fs__id_rev(correct_id);
232 svn_fs_fs__dag_get_predecessor_id(const svn_fs_id_t **id_p,
235 node_revision_t *noderev;
237 SVN_ERR(get_node_revision(&noderev, node));
238 *id_p = noderev->predecessor_id;
244 svn_fs_fs__dag_get_predecessor_count(int *count,
247 node_revision_t *noderev;
249 SVN_ERR(get_node_revision(&noderev, node));
250 *count = noderev->predecessor_count;
255 svn_fs_fs__dag_get_mergeinfo_count(apr_int64_t *count,
258 node_revision_t *noderev;
260 SVN_ERR(get_node_revision(&noderev, node));
261 *count = noderev->mergeinfo_count;
266 svn_fs_fs__dag_has_mergeinfo(svn_boolean_t *has_mergeinfo,
269 node_revision_t *noderev;
271 SVN_ERR(get_node_revision(&noderev, node));
272 *has_mergeinfo = noderev->has_mergeinfo;
277 svn_fs_fs__dag_has_descendants_with_mergeinfo(svn_boolean_t *do_they,
280 node_revision_t *noderev;
282 if (node->kind != svn_node_dir)
288 SVN_ERR(get_node_revision(&noderev, node));
289 if (noderev->mergeinfo_count > 1)
291 else if (noderev->mergeinfo_count == 1 && !noderev->has_mergeinfo)
299 /*** Directory node functions ***/
301 /* Some of these are helpers for functions outside this section. */
303 /* Set *ID_P to the node-id for entry NAME in PARENT. If no such
304 entry, set *ID_P to NULL but do not error. The node-id is
305 allocated in POOL. */
307 dir_entry_id_from_node(const svn_fs_id_t **id_p,
310 apr_pool_t *result_pool,
311 apr_pool_t *scratch_pool)
313 svn_fs_dirent_t *dirent;
315 SVN_ERR(svn_fs_fs__dag_dir_entry(&dirent, parent, name, scratch_pool));
316 *id_p = dirent ? svn_fs_fs__id_copy(dirent->id, result_pool) : NULL;
322 /* Add or set in PARENT a directory entry NAME pointing to ID.
323 Allocations are done in POOL.
326 - PARENT is a mutable directory.
327 - ID does not refer to an ancestor of parent
328 - NAME is a single path component
331 set_entry(dag_node_t *parent,
333 const svn_fs_id_t *id,
334 svn_node_kind_t kind,
338 node_revision_t *parent_noderev;
340 /* Get the parent's node-revision. */
341 SVN_ERR(get_node_revision(&parent_noderev, parent));
343 /* Set the new entry. */
344 return 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 Use POOL for all allocations, except caching the node_revision in PARENT.
357 make_entry(dag_node_t **child_p,
359 const char *parent_path,
361 svn_boolean_t is_dir,
365 const svn_fs_id_t *new_node_id;
366 node_revision_t new_noderev, *parent_noderev;
368 /* Make sure that NAME is a single path component. */
369 if (! svn_path_is_single_path_component(name))
370 return svn_error_createf
371 (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
372 _("Attempted to create a node with an illegal name '%s'"), name);
374 /* Make sure that parent is a directory */
375 if (parent->kind != svn_node_dir)
376 return svn_error_create
377 (SVN_ERR_FS_NOT_DIRECTORY, NULL,
378 _("Attempted to create entry in non-directory parent"));
380 /* Check that the parent is mutable. */
381 if (! svn_fs_fs__dag_check_mutable(parent))
382 return svn_error_createf
383 (SVN_ERR_FS_NOT_MUTABLE, NULL,
384 _("Attempted to clone child of non-mutable node"));
386 /* Create the new node's NODE-REVISION */
387 memset(&new_noderev, 0, sizeof(new_noderev));
388 new_noderev.kind = is_dir ? svn_node_dir : svn_node_file;
389 new_noderev.created_path = svn_fspath__join(parent_path, name, pool);
391 SVN_ERR(get_node_revision(&parent_noderev, parent));
392 new_noderev.copyroot_path = apr_pstrdup(pool,
393 parent_noderev->copyroot_path);
394 new_noderev.copyroot_rev = parent_noderev->copyroot_rev;
395 new_noderev.copyfrom_rev = SVN_INVALID_REVNUM;
396 new_noderev.copyfrom_path = NULL;
398 SVN_ERR(svn_fs_fs__create_node
399 (&new_node_id, svn_fs_fs__dag_get_fs(parent), &new_noderev,
400 svn_fs_fs__id_copy_id(svn_fs_fs__dag_get_id(parent)),
403 /* Create a new dag_node_t for our new node */
404 SVN_ERR(svn_fs_fs__dag_get_node(child_p, svn_fs_fs__dag_get_fs(parent),
407 /* We can safely call set_entry because we already know that
408 PARENT is mutable, and we just created CHILD, so we know it has
409 no ancestors (therefore, PARENT cannot be an ancestor of CHILD) */
410 return set_entry(parent, name, svn_fs_fs__dag_get_id(*child_p),
411 new_noderev.kind, txn_id, pool);
416 svn_fs_fs__dag_dir_entries(apr_hash_t **entries,
420 node_revision_t *noderev;
422 SVN_ERR(get_node_revision(&noderev, node));
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);
432 svn_fs_fs__dag_dir_entry(svn_fs_dirent_t **dirent,
437 node_revision_t *noderev;
438 SVN_ERR(get_node_revision(&noderev, node));
440 if (noderev->kind != svn_node_dir)
441 return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
442 _("Can't get entries of non-directory"));
444 /* Get a dirent hash for this directory. */
445 return svn_fs_fs__rep_contents_dir_entry(dirent, node->fs,
446 noderev, name, pool, pool);
451 svn_fs_fs__dag_set_entry(dag_node_t *node,
452 const char *entry_name,
453 const svn_fs_id_t *id,
454 svn_node_kind_t kind,
458 /* Check it's a directory. */
459 if (node->kind != svn_node_dir)
460 return svn_error_create
461 (SVN_ERR_FS_NOT_DIRECTORY, NULL,
462 _("Attempted to set entry in non-directory node"));
464 /* Check it's mutable. */
465 if (! svn_fs_fs__dag_check_mutable(node))
466 return svn_error_create
467 (SVN_ERR_FS_NOT_MUTABLE, NULL,
468 _("Attempted to set entry in immutable node"));
470 return set_entry(node, entry_name, id, kind, txn_id, pool);
478 svn_fs_fs__dag_get_proplist(apr_hash_t **proplist_p,
482 node_revision_t *noderev;
483 apr_hash_t *proplist = NULL;
485 SVN_ERR(get_node_revision(&noderev, node));
487 SVN_ERR(svn_fs_fs__get_proplist(&proplist, node->fs,
490 *proplist_p = proplist;
497 svn_fs_fs__dag_set_proplist(dag_node_t *node,
498 apr_hash_t *proplist,
501 node_revision_t *noderev;
503 /* Sanity check: this node better be mutable! */
504 if (! svn_fs_fs__dag_check_mutable(node))
506 svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
507 return svn_error_createf
508 (SVN_ERR_FS_NOT_MUTABLE, NULL,
509 "Can't set proplist on *immutable* node-revision %s",
513 /* Go get a fresh NODE-REVISION for this node. */
514 SVN_ERR(get_node_revision(&noderev, node));
516 /* Set the new proplist. */
517 return svn_fs_fs__set_proplist(node->fs, noderev, proplist, pool);
522 svn_fs_fs__dag_increment_mergeinfo_count(dag_node_t *node,
523 apr_int64_t increment,
526 node_revision_t *noderev;
528 /* Sanity check: this node better be mutable! */
529 if (! svn_fs_fs__dag_check_mutable(node))
531 svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
532 return svn_error_createf
533 (SVN_ERR_FS_NOT_MUTABLE, NULL,
534 "Can't increment mergeinfo count on *immutable* node-revision %s",
541 /* Go get a fresh NODE-REVISION for this node. */
542 SVN_ERR(get_node_revision(&noderev, node));
544 noderev->mergeinfo_count += increment;
545 if (noderev->mergeinfo_count < 0)
547 svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
548 return svn_error_createf
549 (SVN_ERR_FS_CORRUPT, NULL,
551 _("Can't increment mergeinfo count on node-revision %%s "
552 "to negative value %%%s"),
554 idstr->data, noderev->mergeinfo_count);
556 if (noderev->mergeinfo_count > 1 && noderev->kind == svn_node_file)
558 svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
559 return svn_error_createf
560 (SVN_ERR_FS_CORRUPT, NULL,
562 _("Can't increment mergeinfo count on *file* "
563 "node-revision %%s to %%%s (> 1)"),
565 idstr->data, noderev->mergeinfo_count);
569 return svn_fs_fs__put_node_revision(node->fs, noderev->id,
570 noderev, FALSE, pool);
574 svn_fs_fs__dag_set_has_mergeinfo(dag_node_t *node,
575 svn_boolean_t has_mergeinfo,
578 node_revision_t *noderev;
580 /* Sanity check: this node better be mutable! */
581 if (! svn_fs_fs__dag_check_mutable(node))
583 svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
584 return svn_error_createf
585 (SVN_ERR_FS_NOT_MUTABLE, NULL,
586 "Can't set mergeinfo flag on *immutable* node-revision %s",
590 /* Go get a fresh NODE-REVISION for this node. */
591 SVN_ERR(get_node_revision(&noderev, node));
593 noderev->has_mergeinfo = has_mergeinfo;
596 return svn_fs_fs__put_node_revision(node->fs, noderev->id,
597 noderev, FALSE, pool);
604 svn_fs_fs__dag_revision_root(dag_node_t **node_p,
609 svn_fs_id_t *root_id;
611 SVN_ERR(svn_fs_fs__rev_get_root(&root_id, fs, rev, pool));
612 return svn_fs_fs__dag_get_node(node_p, fs, root_id, pool);
617 svn_fs_fs__dag_txn_root(dag_node_t **node_p,
622 const svn_fs_id_t *root_id, *ignored;
624 SVN_ERR(svn_fs_fs__get_txn_ids(&root_id, &ignored, fs, txn_id, pool));
625 return svn_fs_fs__dag_get_node(node_p, fs, root_id, pool);
630 svn_fs_fs__dag_txn_base_root(dag_node_t **node_p,
635 const svn_fs_id_t *base_root_id, *ignored;
637 SVN_ERR(svn_fs_fs__get_txn_ids(&ignored, &base_root_id, fs, txn_id, pool));
638 return svn_fs_fs__dag_get_node(node_p, fs, base_root_id, pool);
643 svn_fs_fs__dag_clone_child(dag_node_t **child_p,
645 const char *parent_path,
649 svn_boolean_t is_parent_copyroot,
652 dag_node_t *cur_entry; /* parent's current entry named NAME */
653 const svn_fs_id_t *new_node_id; /* node id we'll put into NEW_NODE */
654 svn_fs_t *fs = svn_fs_fs__dag_get_fs(parent);
655 apr_pool_t *subpool = svn_pool_create(pool);
657 /* First check that the parent is mutable. */
658 if (! svn_fs_fs__dag_check_mutable(parent))
659 return svn_error_createf
660 (SVN_ERR_FS_NOT_MUTABLE, NULL,
661 "Attempted to clone child of non-mutable node");
663 /* Make sure that NAME is a single path component. */
664 if (! svn_path_is_single_path_component(name))
665 return svn_error_createf
666 (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
667 "Attempted to make a child clone with an illegal name '%s'", name);
669 /* Find the node named NAME in PARENT's entries list if it exists. */
670 SVN_ERR(svn_fs_fs__dag_open(&cur_entry, parent, name, pool, subpool));
672 /* Check for mutability in the node we found. If it's mutable, we
673 don't need to clone it. */
674 if (svn_fs_fs__dag_check_mutable(cur_entry))
676 /* This has already been cloned */
677 new_node_id = cur_entry->id;
681 node_revision_t *noderev, *parent_noderev;
683 /* Go get a fresh NODE-REVISION for current child node. */
684 SVN_ERR(get_node_revision(&noderev, cur_entry));
686 if (is_parent_copyroot)
688 SVN_ERR(get_node_revision(&parent_noderev, parent));
689 noderev->copyroot_rev = parent_noderev->copyroot_rev;
690 noderev->copyroot_path = apr_pstrdup(pool,
691 parent_noderev->copyroot_path);
694 noderev->copyfrom_path = NULL;
695 noderev->copyfrom_rev = SVN_INVALID_REVNUM;
697 noderev->predecessor_id = svn_fs_fs__id_copy(cur_entry->id, pool);
698 if (noderev->predecessor_count != -1)
699 noderev->predecessor_count++;
700 noderev->created_path = svn_fspath__join(parent_path, name, pool);
702 SVN_ERR(svn_fs_fs__create_successor(&new_node_id, fs, cur_entry->id,
703 noderev, copy_id, txn_id, pool));
705 /* Replace the ID in the parent's ENTRY list with the ID which
706 refers to the mutable clone of this child. */
707 SVN_ERR(set_entry(parent, name, new_node_id, noderev->kind, txn_id,
711 /* Initialize the youngster. */
712 svn_pool_destroy(subpool);
713 return svn_fs_fs__dag_get_node(child_p, fs, new_node_id, pool);
719 svn_fs_fs__dag_clone_root(dag_node_t **root_p,
724 const svn_fs_id_t *base_root_id, *root_id;
726 /* Get the node ID's of the root directories of the transaction and
727 its base revision. */
728 SVN_ERR(svn_fs_fs__get_txn_ids(&root_id, &base_root_id, fs, txn_id, pool));
730 /* Oh, give me a clone...
731 (If they're the same, we haven't cloned the transaction's root
733 SVN_ERR_ASSERT(!svn_fs_fs__id_eq(root_id, base_root_id));
736 * (Sung to the tune of "Home, Home on the Range", with thanks to
737 * Randall Garrett and Isaac Asimov.)
740 /* One way or another, root_id now identifies a cloned root node. */
741 return svn_fs_fs__dag_get_node(root_p, fs, root_id, pool);
746 svn_fs_fs__dag_delete(dag_node_t *parent,
751 node_revision_t *parent_noderev;
752 svn_fs_t *fs = parent->fs;
753 svn_fs_dirent_t *dirent;
757 /* Make sure parent is a directory. */
758 if (parent->kind != svn_node_dir)
759 return svn_error_createf
760 (SVN_ERR_FS_NOT_DIRECTORY, NULL,
761 "Attempted to delete entry '%s' from *non*-directory node", name);
763 /* Make sure parent is mutable. */
764 if (! svn_fs_fs__dag_check_mutable(parent))
765 return svn_error_createf
766 (SVN_ERR_FS_NOT_MUTABLE, NULL,
767 "Attempted to delete entry '%s' from immutable directory node", name);
769 /* Make sure that NAME is a single path component. */
770 if (! svn_path_is_single_path_component(name))
771 return svn_error_createf
772 (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
773 "Attempted to delete a node with an illegal name '%s'", name);
775 /* Get a fresh NODE-REVISION for the parent node. */
776 SVN_ERR(get_node_revision(&parent_noderev, parent));
778 subpool = svn_pool_create(pool);
780 /* Search this directory for a dirent with that NAME. */
781 SVN_ERR(svn_fs_fs__rep_contents_dir_entry(&dirent, fs, parent_noderev,
782 name, subpool, subpool));
784 /* If we never found ID in ENTRIES (perhaps because there are no
785 ENTRIES, perhaps because ID just isn't in the existing ENTRIES
786 ... it doesn't matter), return an error. */
788 return svn_error_createf
789 (SVN_ERR_FS_NO_SUCH_ENTRY, NULL,
790 "Delete failed--directory has no entry '%s'", name);
792 /* Copy the ID out of the subpool and release the rest of the
793 directory listing. */
794 id = svn_fs_fs__id_copy(dirent->id, pool);
795 svn_pool_destroy(subpool);
797 /* If mutable, remove it and any mutable children from db. */
798 SVN_ERR(svn_fs_fs__dag_delete_if_mutable(parent->fs, id, pool));
800 /* Remove this entry from its parent's entries list. */
801 return svn_fs_fs__set_entry(parent->fs, txn_id, parent_noderev, name,
802 NULL, svn_node_unknown, pool);
807 svn_fs_fs__dag_remove_node(svn_fs_t *fs,
808 const svn_fs_id_t *id,
813 /* Fetch the node. */
814 SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, id, pool));
816 /* If immutable, do nothing and return immediately. */
817 if (! svn_fs_fs__dag_check_mutable(node))
818 return svn_error_createf(SVN_ERR_FS_NOT_MUTABLE, NULL,
819 "Attempted removal of immutable node");
821 /* Delete the node revision. */
822 return svn_fs_fs__delete_node_revision(fs, id, pool);
827 svn_fs_fs__dag_delete_if_mutable(svn_fs_t *fs,
828 const svn_fs_id_t *id,
834 SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, id, pool));
836 /* If immutable, do nothing and return immediately. */
837 if (! svn_fs_fs__dag_check_mutable(node))
840 /* Else it's mutable. Recurse on directories... */
841 if (node->kind == svn_node_dir)
844 apr_hash_index_t *hi;
846 /* Loop over hash entries */
847 SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool));
850 for (hi = apr_hash_first(pool, entries);
852 hi = apr_hash_next(hi))
854 svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
856 SVN_ERR(svn_fs_fs__dag_delete_if_mutable(fs, dirent->id,
862 /* ... then delete the node itself, after deleting any mutable
863 representations and strings it points to. */
864 return svn_fs_fs__dag_remove_node(fs, id, pool);
868 svn_fs_fs__dag_make_file(dag_node_t **child_p,
870 const char *parent_path,
875 /* Call our little helper function */
876 return make_entry(child_p, parent, parent_path, name, FALSE, txn_id, pool);
881 svn_fs_fs__dag_make_dir(dag_node_t **child_p,
883 const char *parent_path,
888 /* Call our little helper function */
889 return make_entry(child_p, parent, parent_path, name, TRUE, txn_id, pool);
894 svn_fs_fs__dag_get_contents(svn_stream_t **contents_p,
898 node_revision_t *noderev;
899 svn_stream_t *contents;
901 /* Make sure our node is a file. */
902 if (file->kind != svn_node_file)
903 return svn_error_createf
904 (SVN_ERR_FS_NOT_FILE, NULL,
905 "Attempted to get textual contents of a *non*-file node");
907 /* Go get a fresh node-revision for FILE. */
908 SVN_ERR(get_node_revision(&noderev, file));
910 /* Get a stream to the contents. */
911 SVN_ERR(svn_fs_fs__get_contents(&contents, file->fs,
914 *contents_p = contents;
921 svn_fs_fs__dag_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
926 node_revision_t *src_noderev;
927 node_revision_t *tgt_noderev;
929 /* Make sure our nodes are files. */
930 if ((source && source->kind != svn_node_file)
931 || target->kind != svn_node_file)
932 return svn_error_createf
933 (SVN_ERR_FS_NOT_FILE, NULL,
934 "Attempted to get textual contents of a *non*-file node");
936 /* Go get fresh node-revisions for the nodes. */
938 SVN_ERR(get_node_revision(&src_noderev, source));
941 SVN_ERR(get_node_revision(&tgt_noderev, target));
943 /* Get the delta stream. */
944 return svn_fs_fs__get_file_delta_stream(stream_p, target->fs,
945 src_noderev, tgt_noderev, pool);
950 svn_fs_fs__dag_try_process_file_contents(svn_boolean_t *success,
952 svn_fs_process_contents_func_t processor,
956 node_revision_t *noderev;
958 /* Go get fresh node-revisions for the nodes. */
959 SVN_ERR(get_node_revision(&noderev, node));
961 return svn_fs_fs__try_process_file_contents(success, node->fs,
963 processor, baton, pool);
968 svn_fs_fs__dag_file_length(svn_filesize_t *length,
972 node_revision_t *noderev;
974 /* Make sure our node is a file. */
975 if (file->kind != svn_node_file)
976 return svn_error_createf
977 (SVN_ERR_FS_NOT_FILE, NULL,
978 "Attempted to get length of a *non*-file node");
980 /* Go get a fresh node-revision for FILE, and . */
981 SVN_ERR(get_node_revision(&noderev, file));
983 return svn_fs_fs__file_length(length, noderev, pool);
988 svn_fs_fs__dag_file_checksum(svn_checksum_t **checksum,
990 svn_checksum_kind_t kind,
993 node_revision_t *noderev;
995 if (file->kind != svn_node_file)
996 return svn_error_createf
997 (SVN_ERR_FS_NOT_FILE, NULL,
998 "Attempted to get checksum of a *non*-file node");
1000 SVN_ERR(get_node_revision(&noderev, file));
1002 return svn_fs_fs__file_checksum(checksum, noderev, kind, pool);
1007 svn_fs_fs__dag_get_edit_stream(svn_stream_t **contents,
1011 node_revision_t *noderev;
1014 /* Make sure our node is a file. */
1015 if (file->kind != svn_node_file)
1016 return svn_error_createf
1017 (SVN_ERR_FS_NOT_FILE, NULL,
1018 "Attempted to set textual contents of a *non*-file node");
1020 /* Make sure our node is mutable. */
1021 if (! svn_fs_fs__dag_check_mutable(file))
1022 return svn_error_createf
1023 (SVN_ERR_FS_NOT_MUTABLE, NULL,
1024 "Attempted to set textual contents of an immutable node");
1026 /* Get the node revision. */
1027 SVN_ERR(get_node_revision(&noderev, file));
1029 SVN_ERR(svn_fs_fs__set_contents(&ws, file->fs, noderev, pool));
1033 return SVN_NO_ERROR;
1039 svn_fs_fs__dag_finalize_edits(dag_node_t *file,
1040 const svn_checksum_t *checksum,
1045 svn_checksum_t *file_checksum;
1047 SVN_ERR(svn_fs_fs__dag_file_checksum(&file_checksum, file,
1048 checksum->kind, pool));
1049 if (!svn_checksum_match(checksum, file_checksum))
1050 return svn_checksum_mismatch_err(checksum, file_checksum, pool,
1051 _("Checksum mismatch for '%s'"),
1052 file->created_path);
1055 return SVN_NO_ERROR;
1060 svn_fs_fs__dag_dup(const dag_node_t *node,
1063 /* Allocate our new node. */
1064 dag_node_t *new_node = apr_pcalloc(pool, sizeof(*new_node));
1066 new_node->fs = node->fs;
1067 new_node->id = svn_fs_fs__id_copy(node->id, pool);
1068 new_node->kind = node->kind;
1069 new_node->created_path = apr_pstrdup(pool, node->created_path);
1071 /* Only copy cached node_revision_t for immutable nodes. */
1072 if (node->node_revision && !svn_fs_fs__dag_check_mutable(node))
1074 new_node->node_revision = copy_node_revision(node->node_revision, pool);
1075 new_node->node_revision->id =
1076 svn_fs_fs__id_copy(node->node_revision->id, pool);
1077 new_node->node_revision->is_fresh_txn_root =
1078 node->node_revision->is_fresh_txn_root;
1080 new_node->node_pool = pool;
1086 svn_fs_fs__dag_serialize(void **data,
1087 apr_size_t *data_len,
1091 dag_node_t *node = in;
1092 svn_stringbuf_t *serialized;
1094 /* create an serialization context and serialize the dag node as root */
1095 svn_temp_serializer__context_t *context =
1096 svn_temp_serializer__init(node,
1098 1024 - SVN_TEMP_SERIALIZER__OVERHEAD,
1101 /* for mutable nodes, we will _never_ cache the noderev */
1102 if (node->node_revision && !svn_fs_fs__dag_check_mutable(node))
1103 svn_fs_fs__noderev_serialize(context, &node->node_revision);
1105 svn_temp_serializer__set_null(context,
1106 (const void * const *)&node->node_revision);
1108 /* The deserializer will use its own pool. */
1109 svn_temp_serializer__set_null(context,
1110 (const void * const *)&node->node_pool);
1112 /* serialize other sub-structures */
1113 svn_fs_fs__id_serialize(context, (const svn_fs_id_t **)&node->id);
1114 svn_fs_fs__id_serialize(context, &node->fresh_root_predecessor_id);
1115 svn_temp_serializer__add_string(context, &node->created_path);
1117 /* return serialized data */
1118 serialized = svn_temp_serializer__get(context);
1119 *data = serialized->data;
1120 *data_len = serialized->len;
1122 return SVN_NO_ERROR;
1126 svn_fs_fs__dag_deserialize(void **out,
1128 apr_size_t data_len,
1131 dag_node_t *node = (dag_node_t *)data;
1133 return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
1134 _("Empty noderev in cache"));
1136 /* Copy the _full_ buffer as it also contains the sub-structures. */
1139 /* fixup all references to sub-structures */
1140 svn_fs_fs__id_deserialize(node, &node->id);
1141 svn_fs_fs__id_deserialize(node,
1142 (svn_fs_id_t **)&node->fresh_root_predecessor_id);
1143 svn_fs_fs__noderev_deserialize(node, &node->node_revision);
1144 node->node_pool = pool;
1146 svn_temp_deserializer__resolve(node, (void**)&node->created_path);
1151 return SVN_NO_ERROR;
1155 svn_fs_fs__dag_open(dag_node_t **child_p,
1158 apr_pool_t *result_pool,
1159 apr_pool_t *scratch_pool)
1161 const svn_fs_id_t *node_id;
1163 /* Ensure that NAME exists in PARENT's entry list. */
1164 SVN_ERR(dir_entry_id_from_node(&node_id, parent, name,
1165 scratch_pool, scratch_pool));
1167 return svn_error_createf
1168 (SVN_ERR_FS_NOT_FOUND, NULL,
1169 "Attempted to open non-existent child node '%s'", name);
1171 /* Make sure that NAME is a single path component. */
1172 if (! svn_path_is_single_path_component(name))
1173 return svn_error_createf
1174 (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
1175 "Attempted to open node with an illegal name '%s'", name);
1177 /* Now get the node that was requested. */
1178 return svn_fs_fs__dag_get_node(child_p, svn_fs_fs__dag_get_fs(parent),
1179 node_id, result_pool);
1184 svn_fs_fs__dag_copy(dag_node_t *to_node,
1186 dag_node_t *from_node,
1187 svn_boolean_t preserve_history,
1188 svn_revnum_t from_rev,
1189 const char *from_path,
1193 const svn_fs_id_t *id;
1195 if (preserve_history)
1197 node_revision_t *from_noderev, *to_noderev;
1198 const char *copy_id;
1199 const svn_fs_id_t *src_id = svn_fs_fs__dag_get_id(from_node);
1200 svn_fs_t *fs = svn_fs_fs__dag_get_fs(from_node);
1202 /* Make a copy of the original node revision. */
1203 SVN_ERR(get_node_revision(&from_noderev, from_node));
1204 to_noderev = copy_node_revision(from_noderev, pool);
1206 /* Reserve a copy ID for this new copy. */
1207 SVN_ERR(svn_fs_fs__reserve_copy_id(©_id, fs, txn_id, pool));
1209 /* Create a successor with its predecessor pointing at the copy
1211 to_noderev->predecessor_id = svn_fs_fs__id_copy(src_id, pool);
1212 if (to_noderev->predecessor_count != -1)
1213 to_noderev->predecessor_count++;
1214 to_noderev->created_path =
1215 svn_fspath__join(svn_fs_fs__dag_get_created_path(to_node), entry,
1217 to_noderev->copyfrom_path = apr_pstrdup(pool, from_path);
1218 to_noderev->copyfrom_rev = from_rev;
1220 /* Set the copyroot equal to our own id. */
1221 to_noderev->copyroot_path = NULL;
1223 SVN_ERR(svn_fs_fs__create_successor(&id, fs, src_id, to_noderev,
1224 copy_id, txn_id, pool));
1227 else /* don't preserve history */
1229 id = svn_fs_fs__dag_get_id(from_node);
1232 /* Set the entry in to_node to the new id. */
1233 return svn_fs_fs__dag_set_entry(to_node, entry, id, from_node->kind,
1239 /*** Comparison. ***/
1242 svn_fs_fs__dag_things_different(svn_boolean_t *props_changed,
1243 svn_boolean_t *contents_changed,
1247 node_revision_t *noderev1, *noderev2;
1249 /* If we have no place to store our results, don't bother doing
1251 if (! props_changed && ! contents_changed)
1252 return SVN_NO_ERROR;
1254 /* The node revision skels for these two nodes. */
1255 SVN_ERR(get_node_revision(&noderev1, node1));
1256 SVN_ERR(get_node_revision(&noderev2, node2));
1258 /* Compare property keys. */
1259 if (props_changed != NULL)
1260 *props_changed = (! svn_fs_fs__noderev_same_rep_key(noderev1->prop_rep,
1261 noderev2->prop_rep));
1263 /* Compare contents keys. */
1264 if (contents_changed != NULL)
1266 (! svn_fs_fs__noderev_same_rep_key(noderev1->data_rep,
1267 noderev2->data_rep));
1269 return SVN_NO_ERROR;
1273 svn_fs_fs__dag_get_copyroot(svn_revnum_t *rev,
1277 node_revision_t *noderev;
1279 /* Go get a fresh node-revision for NODE. */
1280 SVN_ERR(get_node_revision(&noderev, node));
1282 *rev = noderev->copyroot_rev;
1283 *path = noderev->copyroot_path;
1285 return SVN_NO_ERROR;
1289 svn_fs_fs__dag_get_copyfrom_rev(svn_revnum_t *rev,
1292 node_revision_t *noderev;
1294 /* Go get a fresh node-revision for NODE. */
1295 SVN_ERR(get_node_revision(&noderev, node));
1297 *rev = noderev->copyfrom_rev;
1299 return SVN_NO_ERROR;
1303 svn_fs_fs__dag_get_copyfrom_path(const char **path,
1306 node_revision_t *noderev;
1308 /* Go get a fresh node-revision for NODE. */
1309 SVN_ERR(get_node_revision(&noderev, node));
1311 *path = noderev->copyfrom_path;
1313 return SVN_NO_ERROR;
1317 svn_fs_fs__dag_update_ancestry(dag_node_t *target,
1321 node_revision_t *source_noderev, *target_noderev;
1323 if (! svn_fs_fs__dag_check_mutable(target))
1324 return svn_error_createf
1325 (SVN_ERR_FS_NOT_MUTABLE, NULL,
1326 _("Attempted to update ancestry of non-mutable node"));
1328 SVN_ERR(get_node_revision(&source_noderev, source));
1329 SVN_ERR(get_node_revision(&target_noderev, target));
1331 target_noderev->predecessor_id = source->id;
1332 target_noderev->predecessor_count = source_noderev->predecessor_count;
1333 if (target_noderev->predecessor_count != -1)
1334 target_noderev->predecessor_count++;
1336 return svn_fs_fs__put_node_revision(target->fs, target->id, target_noderev,