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"
35 #include "cached_data.h"
36 #include "transaction.h"
38 #include "../libsvn_fs/fs-loader.h"
40 #include "private/svn_fspath.h"
41 #include "svn_private_config.h"
42 #include "private/svn_temp_serializer.h"
43 #include "temp_serializer.h"
46 /* Initializing a filesystem. */
50 /* The filesystem this dag node came from. */
53 /* The node revision ID for this dag node. */
56 /* In the special case that this node is the root of a transaction
57 that has not yet been modified, the revision of this node is the
58 respective txn's base rev. Otherwise, this is SVN_INVALID_REVNUM
59 for txn nodes and the respective crev for committed nodes.
60 (Used in svn_fs_node_created_rev.) */
61 svn_revnum_t revision;
63 /* The node's type (file, dir, etc.) */
66 /* The node's NODE-REVISION, or NULL if we haven't read it in yet.
67 This is allocated in this node's POOL.
69 If you're willing to respect all the rules above, you can munge
70 this yourself, but you're probably better off just calling
71 `get_node_revision' and `set_node_revision', which take care of
73 svn_fs_x__noderev_t *node_revision;
75 /* The pool to allocate NODE_REVISION in. */
76 apr_pool_t *node_pool;
78 /* the path at which this node was created. */
79 const char *created_path;
81 /* Directory entry lookup hint to speed up consecutive calls to
82 svn_fs_x__rep_contents_dir_entry(). Only used for directory nodes.
83 Any value is legal but should default to APR_SIZE_MAX. */
89 /* Trivial helper/accessor functions. */
91 svn_fs_x__dag_node_kind(dag_node_t *node)
96 const svn_fs_x__id_t *
97 svn_fs_x__dag_get_id(const dag_node_t *node)
104 svn_fs_x__dag_get_created_path(dag_node_t *node)
106 return node->created_path;
111 svn_fs_x__dag_get_fs(dag_node_t *node)
117 svn_fs_x__dag_set_fs(dag_node_t *node,
124 /* Dup NODEREV and all associated data into RESULT_POOL.
125 Leaves the id and is_fresh_txn_root fields as zero bytes. */
126 static svn_fs_x__noderev_t *
127 copy_node_revision(svn_fs_x__noderev_t *noderev,
128 apr_pool_t *result_pool)
130 svn_fs_x__noderev_t *nr = apr_pmemdup(result_pool, noderev,
133 if (noderev->copyfrom_path)
134 nr->copyfrom_path = apr_pstrdup(result_pool, noderev->copyfrom_path);
136 nr->copyroot_path = apr_pstrdup(result_pool, noderev->copyroot_path);
137 nr->data_rep = svn_fs_x__rep_copy(noderev->data_rep, result_pool);
138 nr->prop_rep = svn_fs_x__rep_copy(noderev->prop_rep, result_pool);
140 if (noderev->created_path)
141 nr->created_path = apr_pstrdup(result_pool, noderev->created_path);
147 /* Set *NODEREV_P to the cached node-revision for NODE.
148 If the node-revision was not already cached in NODE, read it in,
149 allocating the cache in NODE->NODE_POOL.
151 If you plan to change the contents of NODE, be careful! We're
152 handing you a pointer directly to our cached node-revision, not
153 your own copy. If you change it as part of some operation, but
154 then some Berkeley DB function deadlocks or gets an error, you'll
155 need to back out your changes, or else the cache will reflect
156 changes that never got committed. It's probably best not to change
157 the structure at all. */
159 get_node_revision(svn_fs_x__noderev_t **noderev_p,
162 /* If we've already got a copy, there's no need to read it in. */
163 if (! node->node_revision)
165 svn_fs_x__noderev_t *noderev;
166 apr_pool_t *scratch_pool = svn_pool_create(node->node_pool);
168 SVN_ERR(svn_fs_x__get_node_revision(&noderev, node->fs, &node->id,
169 node->node_pool, scratch_pool));
170 node->node_revision = noderev;
171 svn_pool_destroy(scratch_pool);
174 /* Now NODE->node_revision is set. */
175 *noderev_p = node->node_revision;
179 /* Return the node revision ID of NODE. The value returned is shared
180 with NODE, and will be deallocated when NODE is. */
182 svn_fs_x__dag_get_node_id(svn_fs_x__id_t *node_id,
185 svn_fs_x__noderev_t *noderev;
186 SVN_ERR(get_node_revision(&noderev, node));
188 *node_id = noderev->node_id;
192 /* Return the node revision ID of NODE. The value returned is shared
193 with NODE, and will be deallocated when NODE is. */
195 svn_fs_x__dag_get_copy_id(svn_fs_x__id_t *copy_id,
198 svn_fs_x__noderev_t *noderev;
199 SVN_ERR(get_node_revision(&noderev, node));
201 *copy_id = noderev->copy_id;
205 /* Return the node ID of NODE. The value returned is shared with NODE,
206 and will be deallocated when NODE is. */
208 svn_fs_x__dag_related_node(svn_boolean_t *same,
212 svn_fs_x__id_t lhs_node, rhs_node;
214 SVN_ERR(svn_fs_x__dag_get_node_id(&lhs_node, lhs));
215 SVN_ERR(svn_fs_x__dag_get_node_id(&rhs_node, rhs));
216 *same = svn_fs_x__id_eq(&lhs_node, &rhs_node);
222 svn_fs_x__dag_same_line_of_history(svn_boolean_t *same,
226 svn_fs_x__noderev_t *lhs_noderev, *rhs_noderev;
228 SVN_ERR(get_node_revision(&lhs_noderev, lhs));
229 SVN_ERR(get_node_revision(&rhs_noderev, rhs));
231 *same = svn_fs_x__id_eq(&lhs_noderev->node_id, &rhs_noderev->node_id)
232 && svn_fs_x__id_eq(&lhs_noderev->copy_id, &rhs_noderev->copy_id);
238 svn_fs_x__dag_check_mutable(const dag_node_t *node)
240 return svn_fs_x__is_txn(svn_fs_x__dag_get_id(node)->change_set);
245 svn_fs_x__dag_get_node(dag_node_t **node,
247 const svn_fs_x__id_t *id,
248 apr_pool_t *result_pool,
249 apr_pool_t *scratch_pool)
251 dag_node_t *new_node;
252 svn_fs_x__noderev_t *noderev;
254 /* Construct the node. */
255 new_node = apr_pcalloc(result_pool, sizeof(*new_node));
258 new_node->hint = APR_SIZE_MAX;
260 /* Grab the contents so we can inspect the node's kind and created path. */
261 SVN_ERR(svn_fs_x__get_node_revision(&noderev, fs, id,
262 result_pool, scratch_pool));
263 new_node->node_pool = result_pool;
264 new_node->node_revision = noderev;
266 /* Initialize the KIND and CREATED_PATH attributes */
267 new_node->kind = noderev->kind;
268 new_node->created_path = noderev->created_path;
270 /* Support our quirky svn_fs_node_created_rev API.
271 Untouched txn roots report the base rev as theirs. */
273 = ( svn_fs_x__is_fresh_txn_root(noderev)
274 ? svn_fs_x__get_revnum(noderev->predecessor_id.change_set)
275 : svn_fs_x__get_revnum(id->change_set));
277 /* Return a fresh new node */
284 svn_fs_x__dag_get_revision(const dag_node_t *node)
286 return node->revision;
291 svn_fs_x__dag_get_predecessor_id(svn_fs_x__id_t *id_p,
294 svn_fs_x__noderev_t *noderev;
296 SVN_ERR(get_node_revision(&noderev, node));
297 *id_p = noderev->predecessor_id;
304 svn_fs_x__dag_get_predecessor_count(int *count,
307 svn_fs_x__noderev_t *noderev;
309 SVN_ERR(get_node_revision(&noderev, node));
310 *count = noderev->predecessor_count;
315 svn_fs_x__dag_get_mergeinfo_count(apr_int64_t *count,
318 svn_fs_x__noderev_t *noderev;
320 SVN_ERR(get_node_revision(&noderev, node));
321 *count = noderev->mergeinfo_count;
326 svn_fs_x__dag_has_mergeinfo(svn_boolean_t *has_mergeinfo,
329 svn_fs_x__noderev_t *noderev;
331 SVN_ERR(get_node_revision(&noderev, node));
332 *has_mergeinfo = noderev->has_mergeinfo;
337 svn_fs_x__dag_has_descendants_with_mergeinfo(svn_boolean_t *do_they,
340 svn_fs_x__noderev_t *noderev;
342 if (node->kind != svn_node_dir)
348 SVN_ERR(get_node_revision(&noderev, node));
349 if (noderev->mergeinfo_count > 1)
351 else if (noderev->mergeinfo_count == 1 && !noderev->has_mergeinfo)
359 /*** Directory node functions ***/
361 /* Some of these are helpers for functions outside this section. */
363 /* Set *ID_P to the noderev-id for entry NAME in PARENT. If no such
364 entry, set *ID_P to NULL but do not error. */
366 dir_entry_id_from_node(svn_fs_x__id_t *id_p,
369 apr_pool_t *scratch_pool)
371 svn_fs_x__dirent_t *dirent;
372 svn_fs_x__noderev_t *noderev;
374 SVN_ERR(get_node_revision(&noderev, parent));
375 if (noderev->kind != svn_node_dir)
376 return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
377 _("Can't get entries of non-directory"));
379 /* Make sure that NAME is a single path component. */
380 if (! svn_path_is_single_path_component(name))
381 return svn_error_createf
382 (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
383 "Attempted to open node with an illegal name '%s'", name);
385 /* Get a dirent hash for this directory. */
386 SVN_ERR(svn_fs_x__rep_contents_dir_entry(&dirent, parent->fs, noderev,
388 scratch_pool, scratch_pool));
392 svn_fs_x__id_reset(id_p);
398 /* Add or set in PARENT a directory entry NAME pointing to ID.
399 Temporary allocations are done in SCRATCH_POOL.
402 - PARENT is a mutable directory.
403 - ID does not refer to an ancestor of parent
404 - NAME is a single path component
407 set_entry(dag_node_t *parent,
409 const svn_fs_x__id_t *id,
410 svn_node_kind_t kind,
411 svn_fs_x__txn_id_t txn_id,
412 apr_pool_t *scratch_pool)
414 svn_fs_x__noderev_t *parent_noderev;
416 /* Get the parent's node-revision. */
417 SVN_ERR(get_node_revision(&parent_noderev, parent));
419 /* Set the new entry. */
420 return svn_fs_x__set_entry(parent->fs, txn_id, parent_noderev, name, id,
421 kind, parent->node_pool, scratch_pool);
425 /* Make a new entry named NAME in PARENT. If IS_DIR is true, then the
426 node revision the new entry points to will be a directory, else it
427 will be a file. The new node will be allocated in RESULT_POOL. PARENT
428 must be mutable, and must not have an entry named NAME.
430 Use SCRATCH_POOL for all temporary allocations.
433 make_entry(dag_node_t **child_p,
435 const char *parent_path,
437 svn_boolean_t is_dir,
438 svn_fs_x__txn_id_t txn_id,
439 apr_pool_t *result_pool,
440 apr_pool_t *scratch_pool)
442 svn_fs_x__noderev_t new_noderev, *parent_noderev;
444 /* Make sure that NAME is a single path component. */
445 if (! svn_path_is_single_path_component(name))
446 return svn_error_createf
447 (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
448 _("Attempted to create a node with an illegal name '%s'"), name);
450 /* Make sure that parent is a directory */
451 if (parent->kind != svn_node_dir)
452 return svn_error_create
453 (SVN_ERR_FS_NOT_DIRECTORY, NULL,
454 _("Attempted to create entry in non-directory parent"));
456 /* Check that the parent is mutable. */
457 if (! svn_fs_x__dag_check_mutable(parent))
458 return svn_error_createf
459 (SVN_ERR_FS_NOT_MUTABLE, NULL,
460 _("Attempted to clone child of non-mutable node"));
462 /* Create the new node's NODE-REVISION */
463 memset(&new_noderev, 0, sizeof(new_noderev));
464 new_noderev.kind = is_dir ? svn_node_dir : svn_node_file;
465 new_noderev.created_path = svn_fspath__join(parent_path, name, result_pool);
467 SVN_ERR(get_node_revision(&parent_noderev, parent));
468 new_noderev.copyroot_path = apr_pstrdup(result_pool,
469 parent_noderev->copyroot_path);
470 new_noderev.copyroot_rev = parent_noderev->copyroot_rev;
471 new_noderev.copyfrom_rev = SVN_INVALID_REVNUM;
472 new_noderev.copyfrom_path = NULL;
473 svn_fs_x__id_reset(&new_noderev.predecessor_id);
475 SVN_ERR(svn_fs_x__create_node
476 (svn_fs_x__dag_get_fs(parent), &new_noderev,
477 &parent_noderev->copy_id, txn_id, scratch_pool));
479 /* Create a new dag_node_t for our new node */
480 SVN_ERR(svn_fs_x__dag_get_node(child_p, svn_fs_x__dag_get_fs(parent),
481 &new_noderev.noderev_id, result_pool,
484 /* We can safely call set_entry because we already know that
485 PARENT is mutable, and we just created CHILD, so we know it has
486 no ancestors (therefore, PARENT cannot be an ancestor of CHILD) */
487 return set_entry(parent, name, &new_noderev.noderev_id,
488 new_noderev.kind, txn_id, scratch_pool);
493 svn_fs_x__dag_dir_entries(apr_array_header_t **entries,
495 apr_pool_t *result_pool,
496 apr_pool_t *scratch_pool)
498 svn_fs_x__noderev_t *noderev;
500 SVN_ERR(get_node_revision(&noderev, node));
502 if (noderev->kind != svn_node_dir)
503 return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
504 _("Can't get entries of non-directory"));
506 return svn_fs_x__rep_contents_dir(entries, node->fs, noderev, result_pool,
512 svn_fs_x__dag_set_entry(dag_node_t *node,
513 const char *entry_name,
514 const svn_fs_x__id_t *id,
515 svn_node_kind_t kind,
516 svn_fs_x__txn_id_t txn_id,
517 apr_pool_t *scratch_pool)
519 /* Check it's a directory. */
520 if (node->kind != svn_node_dir)
521 return svn_error_create
522 (SVN_ERR_FS_NOT_DIRECTORY, NULL,
523 _("Attempted to set entry in non-directory node"));
525 /* Check it's mutable. */
526 if (! svn_fs_x__dag_check_mutable(node))
527 return svn_error_create
528 (SVN_ERR_FS_NOT_MUTABLE, NULL,
529 _("Attempted to set entry in immutable node"));
531 return set_entry(node, entry_name, id, kind, txn_id, scratch_pool);
539 svn_fs_x__dag_get_proplist(apr_hash_t **proplist_p,
541 apr_pool_t *result_pool,
542 apr_pool_t *scratch_pool)
544 svn_fs_x__noderev_t *noderev;
545 apr_hash_t *proplist = NULL;
547 SVN_ERR(get_node_revision(&noderev, node));
549 SVN_ERR(svn_fs_x__get_proplist(&proplist, node->fs, noderev, result_pool,
552 *proplist_p = proplist;
559 svn_fs_x__dag_set_proplist(dag_node_t *node,
560 apr_hash_t *proplist,
561 apr_pool_t *scratch_pool)
563 svn_fs_x__noderev_t *noderev;
565 /* Sanity check: this node better be mutable! */
566 if (! svn_fs_x__dag_check_mutable(node))
568 svn_string_t *idstr = svn_fs_x__id_unparse(&node->id, scratch_pool);
569 return svn_error_createf
570 (SVN_ERR_FS_NOT_MUTABLE, NULL,
571 "Can't set proplist on *immutable* node-revision %s",
575 /* Go get a fresh NODE-REVISION for this node. */
576 SVN_ERR(get_node_revision(&noderev, node));
578 /* Set the new proplist. */
579 return svn_fs_x__set_proplist(node->fs, noderev, proplist, scratch_pool);
584 svn_fs_x__dag_increment_mergeinfo_count(dag_node_t *node,
585 apr_int64_t increment,
586 apr_pool_t *scratch_pool)
588 svn_fs_x__noderev_t *noderev;
590 /* Sanity check: this node better be mutable! */
591 if (! svn_fs_x__dag_check_mutable(node))
593 svn_string_t *idstr = svn_fs_x__id_unparse(&node->id, scratch_pool);
594 return svn_error_createf
595 (SVN_ERR_FS_NOT_MUTABLE, NULL,
596 "Can't increment mergeinfo count on *immutable* node-revision %s",
603 /* Go get a fresh NODE-REVISION for this node. */
604 SVN_ERR(get_node_revision(&noderev, node));
606 noderev->mergeinfo_count += increment;
607 if (noderev->mergeinfo_count < 0)
609 svn_string_t *idstr = svn_fs_x__id_unparse(&node->id, scratch_pool);
610 return svn_error_createf
611 (SVN_ERR_FS_CORRUPT, NULL,
612 apr_psprintf(scratch_pool,
613 _("Can't increment mergeinfo count on node-revision %%s "
614 "to negative value %%%s"),
616 idstr->data, noderev->mergeinfo_count);
618 if (noderev->mergeinfo_count > 1 && noderev->kind == svn_node_file)
620 svn_string_t *idstr = svn_fs_x__id_unparse(&node->id, scratch_pool);
621 return svn_error_createf
622 (SVN_ERR_FS_CORRUPT, NULL,
623 apr_psprintf(scratch_pool,
624 _("Can't increment mergeinfo count on *file* "
625 "node-revision %%s to %%%s (> 1)"),
627 idstr->data, noderev->mergeinfo_count);
631 return svn_fs_x__put_node_revision(node->fs, noderev, scratch_pool);
635 svn_fs_x__dag_set_has_mergeinfo(dag_node_t *node,
636 svn_boolean_t has_mergeinfo,
637 apr_pool_t *scratch_pool)
639 svn_fs_x__noderev_t *noderev;
641 /* Sanity check: this node better be mutable! */
642 if (! svn_fs_x__dag_check_mutable(node))
644 svn_string_t *idstr = svn_fs_x__id_unparse(&node->id, scratch_pool);
645 return svn_error_createf
646 (SVN_ERR_FS_NOT_MUTABLE, NULL,
647 "Can't set mergeinfo flag on *immutable* node-revision %s",
651 /* Go get a fresh NODE-REVISION for this node. */
652 SVN_ERR(get_node_revision(&noderev, node));
654 noderev->has_mergeinfo = has_mergeinfo;
657 return svn_fs_x__put_node_revision(node->fs, noderev, scratch_pool);
664 svn_fs_x__dag_revision_root(dag_node_t **node_p,
667 apr_pool_t *result_pool,
668 apr_pool_t *scratch_pool)
670 svn_fs_x__id_t root_id;
672 svn_fs_x__init_rev_root(&root_id, rev);
673 return svn_fs_x__dag_get_node(node_p, fs, &root_id, result_pool,
679 svn_fs_x__dag_txn_root(dag_node_t **node_p,
681 svn_fs_x__txn_id_t txn_id,
682 apr_pool_t *result_pool,
683 apr_pool_t *scratch_pool)
685 svn_fs_x__id_t root_id;
687 svn_fs_x__init_txn_root(&root_id, txn_id);
688 return svn_fs_x__dag_get_node(node_p, fs, &root_id, result_pool,
694 svn_fs_x__dag_clone_child(dag_node_t **child_p,
696 const char *parent_path,
698 const svn_fs_x__id_t *copy_id,
699 svn_fs_x__txn_id_t txn_id,
700 svn_boolean_t is_parent_copyroot,
701 apr_pool_t *result_pool,
702 apr_pool_t *scratch_pool)
704 dag_node_t *cur_entry; /* parent's current entry named NAME */
705 const svn_fs_x__id_t *new_node_id; /* node id we'll put into NEW_NODE */
706 svn_fs_t *fs = svn_fs_x__dag_get_fs(parent);
708 /* First check that the parent is mutable. */
709 if (! svn_fs_x__dag_check_mutable(parent))
710 return svn_error_createf
711 (SVN_ERR_FS_NOT_MUTABLE, NULL,
712 "Attempted to clone child of non-mutable node");
714 /* Make sure that NAME is a single path component. */
715 if (! svn_path_is_single_path_component(name))
716 return svn_error_createf
717 (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
718 "Attempted to make a child clone with an illegal name '%s'", name);
720 /* Find the node named NAME in PARENT's entries list if it exists. */
721 SVN_ERR(svn_fs_x__dag_open(&cur_entry, parent, name, scratch_pool,
724 return svn_error_createf
725 (SVN_ERR_FS_NOT_FOUND, NULL,
726 "Attempted to open non-existent child node '%s'", name);
728 /* Check for mutability in the node we found. If it's mutable, we
729 don't need to clone it. */
730 if (svn_fs_x__dag_check_mutable(cur_entry))
732 /* This has already been cloned */
733 new_node_id = svn_fs_x__dag_get_id(cur_entry);
737 svn_fs_x__noderev_t *noderev, *parent_noderev;
739 /* Go get a fresh NODE-REVISION for current child node. */
740 SVN_ERR(get_node_revision(&noderev, cur_entry));
742 if (is_parent_copyroot)
744 SVN_ERR(get_node_revision(&parent_noderev, parent));
745 noderev->copyroot_rev = parent_noderev->copyroot_rev;
746 noderev->copyroot_path = apr_pstrdup(scratch_pool,
747 parent_noderev->copyroot_path);
750 noderev->copyfrom_path = NULL;
751 noderev->copyfrom_rev = SVN_INVALID_REVNUM;
753 noderev->predecessor_id = noderev->noderev_id;
754 noderev->predecessor_count++;
755 noderev->created_path = svn_fspath__join(parent_path, name,
759 copy_id = &noderev->copy_id;
761 SVN_ERR(svn_fs_x__create_successor(fs, noderev, copy_id, txn_id,
763 new_node_id = &noderev->noderev_id;
765 /* Replace the ID in the parent's ENTRY list with the ID which
766 refers to the mutable clone of this child. */
767 SVN_ERR(set_entry(parent, name, new_node_id, noderev->kind, txn_id,
771 /* Initialize the youngster. */
772 return svn_fs_x__dag_get_node(child_p, fs, new_node_id, result_pool,
777 /* Delete all mutable node revisions reachable from node ID, including
778 ID itself, from FS's `nodes' table. Also delete any mutable
779 representations and strings associated with that node revision.
780 ID may refer to a file or directory, which may be mutable or immutable.
782 Use SCRATCH_POOL for temporary allocations.
785 delete_if_mutable(svn_fs_t *fs,
786 const svn_fs_x__id_t *id,
787 apr_pool_t *scratch_pool)
792 SVN_ERR(svn_fs_x__dag_get_node(&node, fs, id, scratch_pool, scratch_pool));
794 /* If immutable, do nothing and return immediately. */
795 if (! svn_fs_x__dag_check_mutable(node))
798 /* Else it's mutable. Recurse on directories... */
799 if (node->kind == svn_node_dir)
801 apr_array_header_t *entries;
803 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
805 /* Loop over directory entries */
806 SVN_ERR(svn_fs_x__dag_dir_entries(&entries, node, scratch_pool,
808 for (i = 0; i < entries->nelts; ++i)
810 const svn_fs_x__id_t *noderev_id
811 = &APR_ARRAY_IDX(entries, i, svn_fs_x__dirent_t *)->id;
813 svn_pool_clear(iterpool);
814 SVN_ERR(delete_if_mutable(fs, noderev_id, iterpool));
817 svn_pool_destroy(iterpool);
820 /* ... then delete the node itself, after deleting any mutable
821 representations and strings it points to. */
822 return svn_fs_x__delete_node_revision(fs, id, scratch_pool);
827 svn_fs_x__dag_delete(dag_node_t *parent,
829 svn_fs_x__txn_id_t txn_id,
830 apr_pool_t *scratch_pool)
832 svn_fs_x__noderev_t *parent_noderev;
833 svn_fs_t *fs = parent->fs;
834 svn_fs_x__dirent_t *dirent;
837 /* Make sure parent is a directory. */
838 if (parent->kind != svn_node_dir)
839 return svn_error_createf
840 (SVN_ERR_FS_NOT_DIRECTORY, NULL,
841 "Attempted to delete entry '%s' from *non*-directory node", name);
843 /* Make sure parent is mutable. */
844 if (! svn_fs_x__dag_check_mutable(parent))
845 return svn_error_createf
846 (SVN_ERR_FS_NOT_MUTABLE, NULL,
847 "Attempted to delete entry '%s' from immutable directory node", name);
849 /* Make sure that NAME is a single path component. */
850 if (! svn_path_is_single_path_component(name))
851 return svn_error_createf
852 (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
853 "Attempted to delete a node with an illegal name '%s'", name);
855 /* Get a fresh NODE-REVISION for the parent node. */
856 SVN_ERR(get_node_revision(&parent_noderev, parent));
858 subpool = svn_pool_create(scratch_pool);
860 /* Search this directory for a dirent with that NAME. */
861 SVN_ERR(svn_fs_x__rep_contents_dir_entry(&dirent, fs, parent_noderev,
865 /* If we never found ID in ENTRIES (perhaps because there are no
866 ENTRIES, perhaps because ID just isn't in the existing ENTRIES
867 ... it doesn't matter), return an error. */
869 return svn_error_createf
870 (SVN_ERR_FS_NO_SUCH_ENTRY, NULL,
871 "Delete failed--directory has no entry '%s'", name);
873 /* If mutable, remove it and any mutable children from db. */
874 SVN_ERR(delete_if_mutable(parent->fs, &dirent->id, scratch_pool));
875 svn_pool_destroy(subpool);
877 /* Remove this entry from its parent's entries list. */
878 return svn_fs_x__set_entry(parent->fs, txn_id, parent_noderev, name,
879 NULL, svn_node_unknown, parent->node_pool,
885 svn_fs_x__dag_make_file(dag_node_t **child_p,
887 const char *parent_path,
889 svn_fs_x__txn_id_t txn_id,
890 apr_pool_t *result_pool,
891 apr_pool_t *scratch_pool)
893 /* Call our little helper function */
894 return make_entry(child_p, parent, parent_path, name, FALSE, txn_id,
895 result_pool, scratch_pool);
900 svn_fs_x__dag_make_dir(dag_node_t **child_p,
902 const char *parent_path,
904 svn_fs_x__txn_id_t txn_id,
905 apr_pool_t *result_pool,
906 apr_pool_t *scratch_pool)
908 /* Call our little helper function */
909 return make_entry(child_p, parent, parent_path, name, TRUE, txn_id,
910 result_pool, scratch_pool);
915 svn_fs_x__dag_get_contents(svn_stream_t **contents_p,
917 apr_pool_t *result_pool)
919 svn_fs_x__noderev_t *noderev;
920 svn_stream_t *contents;
922 /* Make sure our node is a file. */
923 if (file->kind != svn_node_file)
924 return svn_error_createf
925 (SVN_ERR_FS_NOT_FILE, NULL,
926 "Attempted to get textual contents of a *non*-file node");
928 /* Go get a fresh node-revision for FILE. */
929 SVN_ERR(get_node_revision(&noderev, file));
931 /* Get a stream to the contents. */
932 SVN_ERR(svn_fs_x__get_contents(&contents, file->fs,
933 noderev->data_rep, TRUE, result_pool));
935 *contents_p = contents;
942 svn_fs_x__dag_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
945 apr_pool_t *result_pool,
946 apr_pool_t *scratch_pool)
948 svn_fs_x__noderev_t *src_noderev;
949 svn_fs_x__noderev_t *tgt_noderev;
951 /* Make sure our nodes are files. */
952 if ((source && source->kind != svn_node_file)
953 || target->kind != svn_node_file)
954 return svn_error_createf
955 (SVN_ERR_FS_NOT_FILE, NULL,
956 "Attempted to get textual contents of a *non*-file node");
958 /* Go get fresh node-revisions for the nodes. */
960 SVN_ERR(get_node_revision(&src_noderev, source));
963 SVN_ERR(get_node_revision(&tgt_noderev, target));
965 /* Get the delta stream. */
966 return svn_fs_x__get_file_delta_stream(stream_p, target->fs,
967 src_noderev, tgt_noderev,
968 result_pool, scratch_pool);
973 svn_fs_x__dag_try_process_file_contents(svn_boolean_t *success,
975 svn_fs_process_contents_func_t processor,
977 apr_pool_t *scratch_pool)
979 svn_fs_x__noderev_t *noderev;
981 /* Go get fresh node-revisions for the nodes. */
982 SVN_ERR(get_node_revision(&noderev, node));
984 return svn_fs_x__try_process_file_contents(success, node->fs,
986 processor, baton, scratch_pool);
991 svn_fs_x__dag_file_length(svn_filesize_t *length,
994 svn_fs_x__noderev_t *noderev;
996 /* Make sure our node is a file. */
997 if (file->kind != svn_node_file)
998 return svn_error_createf
999 (SVN_ERR_FS_NOT_FILE, NULL,
1000 "Attempted to get length of a *non*-file node");
1002 /* Go get a fresh node-revision for FILE, and . */
1003 SVN_ERR(get_node_revision(&noderev, file));
1005 return svn_fs_x__file_length(length, noderev);
1010 svn_fs_x__dag_file_checksum(svn_checksum_t **checksum,
1012 svn_checksum_kind_t kind,
1013 apr_pool_t *result_pool)
1015 svn_fs_x__noderev_t *noderev;
1017 if (file->kind != svn_node_file)
1018 return svn_error_createf
1019 (SVN_ERR_FS_NOT_FILE, NULL,
1020 "Attempted to get checksum of a *non*-file node");
1022 SVN_ERR(get_node_revision(&noderev, file));
1024 return svn_fs_x__file_checksum(checksum, noderev, kind, result_pool);
1029 svn_fs_x__dag_get_edit_stream(svn_stream_t **contents,
1031 apr_pool_t *result_pool)
1033 svn_fs_x__noderev_t *noderev;
1036 /* Make sure our node is a file. */
1037 if (file->kind != svn_node_file)
1038 return svn_error_createf
1039 (SVN_ERR_FS_NOT_FILE, NULL,
1040 "Attempted to set textual contents of a *non*-file node");
1042 /* Make sure our node is mutable. */
1043 if (! svn_fs_x__dag_check_mutable(file))
1044 return svn_error_createf
1045 (SVN_ERR_FS_NOT_MUTABLE, NULL,
1046 "Attempted to set textual contents of an immutable node");
1048 /* Get the node revision. */
1049 SVN_ERR(get_node_revision(&noderev, file));
1051 SVN_ERR(svn_fs_x__set_contents(&ws, file->fs, noderev, result_pool));
1055 return SVN_NO_ERROR;
1061 svn_fs_x__dag_finalize_edits(dag_node_t *file,
1062 const svn_checksum_t *checksum,
1063 apr_pool_t *scratch_pool)
1067 svn_checksum_t *file_checksum;
1069 SVN_ERR(svn_fs_x__dag_file_checksum(&file_checksum, file,
1070 checksum->kind, scratch_pool));
1071 if (!svn_checksum_match(checksum, file_checksum))
1072 return svn_checksum_mismatch_err(checksum, file_checksum,
1074 _("Checksum mismatch for '%s'"),
1075 file->created_path);
1078 return SVN_NO_ERROR;
1083 svn_fs_x__dag_dup(const dag_node_t *node,
1084 apr_pool_t *result_pool)
1086 /* Allocate our new node. */
1087 dag_node_t *new_node = apr_pmemdup(result_pool, node, sizeof(*new_node));
1089 /* Only copy cached svn_fs_x__noderev_t for immutable nodes. */
1090 if (node->node_revision && !svn_fs_x__dag_check_mutable(node))
1092 new_node->node_revision = copy_node_revision(node->node_revision,
1094 new_node->created_path = new_node->node_revision->created_path;
1098 new_node->node_revision = NULL;
1099 new_node->created_path = apr_pstrdup(result_pool, node->created_path);
1102 new_node->node_pool = result_pool;
1108 svn_fs_x__dag_copy_into_pool(dag_node_t *node,
1109 apr_pool_t *result_pool)
1111 return (node->node_pool == result_pool
1113 : svn_fs_x__dag_dup(node, result_pool));
1117 svn_fs_x__dag_serialize(void **data,
1118 apr_size_t *data_len,
1122 dag_node_t *node = in;
1123 svn_stringbuf_t *serialized;
1125 /* create an serialization context and serialize the dag node as root */
1126 svn_temp_serializer__context_t *context =
1127 svn_temp_serializer__init(node,
1129 1024 - SVN_TEMP_SERIALIZER__OVERHEAD,
1132 /* for mutable nodes, we will _never_ cache the noderev */
1133 if (node->node_revision && !svn_fs_x__dag_check_mutable(node))
1135 svn_fs_x__noderev_serialize(context, &node->node_revision);
1139 svn_temp_serializer__set_null(context,
1140 (const void * const *)&node->node_revision);
1141 svn_temp_serializer__add_string(context, &node->created_path);
1144 /* The deserializer will use its own pool. */
1145 svn_temp_serializer__set_null(context,
1146 (const void * const *)&node->node_pool);
1148 /* return serialized data */
1149 serialized = svn_temp_serializer__get(context);
1150 *data = serialized->data;
1151 *data_len = serialized->len;
1153 return SVN_NO_ERROR;
1157 svn_fs_x__dag_deserialize(void **out,
1159 apr_size_t data_len,
1162 dag_node_t *node = (dag_node_t *)data;
1164 return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
1165 _("Empty noderev in cache"));
1167 /* Copy the _full_ buffer as it also contains the sub-structures. */
1170 /* fixup all references to sub-structures */
1171 svn_fs_x__noderev_deserialize(node, &node->node_revision, pool);
1172 node->node_pool = pool;
1174 if (node->node_revision)
1175 node->created_path = node->node_revision->created_path;
1177 svn_temp_deserializer__resolve(node, (void**)&node->created_path);
1182 return SVN_NO_ERROR;
1186 svn_fs_x__dag_open(dag_node_t **child_p,
1189 apr_pool_t *result_pool,
1190 apr_pool_t *scratch_pool)
1192 svn_fs_x__id_t node_id;
1194 /* Ensure that NAME exists in PARENT's entry list. */
1195 SVN_ERR(dir_entry_id_from_node(&node_id, parent, name, scratch_pool));
1196 if (! svn_fs_x__id_used(&node_id))
1199 return SVN_NO_ERROR;
1202 /* Now get the node that was requested. */
1203 return svn_fs_x__dag_get_node(child_p, svn_fs_x__dag_get_fs(parent),
1204 &node_id, result_pool, scratch_pool);
1209 svn_fs_x__dag_copy(dag_node_t *to_node,
1211 dag_node_t *from_node,
1212 svn_boolean_t preserve_history,
1213 svn_revnum_t from_rev,
1214 const char *from_path,
1215 svn_fs_x__txn_id_t txn_id,
1216 apr_pool_t *scratch_pool)
1218 const svn_fs_x__id_t *id;
1220 if (preserve_history)
1222 svn_fs_x__noderev_t *from_noderev, *to_noderev;
1223 svn_fs_x__id_t copy_id;
1224 svn_fs_t *fs = svn_fs_x__dag_get_fs(from_node);
1226 /* Make a copy of the original node revision. */
1227 SVN_ERR(get_node_revision(&from_noderev, from_node));
1228 to_noderev = copy_node_revision(from_noderev, scratch_pool);
1230 /* Reserve a copy ID for this new copy. */
1231 SVN_ERR(svn_fs_x__reserve_copy_id(©_id, fs, txn_id, scratch_pool));
1233 /* Create a successor with its predecessor pointing at the copy
1235 to_noderev->predecessor_id = to_noderev->noderev_id;
1236 to_noderev->predecessor_count++;
1237 to_noderev->created_path =
1238 svn_fspath__join(svn_fs_x__dag_get_created_path(to_node), entry,
1240 to_noderev->copyfrom_path = apr_pstrdup(scratch_pool, from_path);
1241 to_noderev->copyfrom_rev = from_rev;
1243 /* Set the copyroot equal to our own id. */
1244 to_noderev->copyroot_path = NULL;
1246 SVN_ERR(svn_fs_x__create_successor(fs, to_noderev,
1247 ©_id, txn_id, scratch_pool));
1248 id = &to_noderev->noderev_id;
1250 else /* don't preserve history */
1252 id = svn_fs_x__dag_get_id(from_node);
1255 /* Set the entry in to_node to the new id. */
1256 return svn_fs_x__dag_set_entry(to_node, entry, id, from_node->kind,
1257 txn_id, scratch_pool);
1262 /*** Comparison. ***/
1265 svn_fs_x__dag_things_different(svn_boolean_t *props_changed,
1266 svn_boolean_t *contents_changed,
1269 svn_boolean_t strict,
1270 apr_pool_t *scratch_pool)
1272 svn_fs_x__noderev_t *noderev1, *noderev2;
1276 /* If we have no place to store our results, don't bother doing
1278 if (! props_changed && ! contents_changed)
1279 return SVN_NO_ERROR;
1281 fs = svn_fs_x__dag_get_fs(node1);
1283 /* The node revision skels for these two nodes. */
1284 SVN_ERR(get_node_revision(&noderev1, node1));
1285 SVN_ERR(get_node_revision(&noderev2, node2));
1287 /* Compare property keys. */
1288 if (props_changed != NULL)
1290 SVN_ERR(svn_fs_x__prop_rep_equal(&same, fs, noderev1, noderev2,
1291 strict, scratch_pool));
1292 *props_changed = !same;
1295 /* Compare contents keys. */
1296 if (contents_changed != NULL)
1297 *contents_changed = !svn_fs_x__file_text_rep_equal(noderev1->data_rep,
1298 noderev2->data_rep);
1300 return SVN_NO_ERROR;
1304 svn_fs_x__dag_get_copyroot(svn_revnum_t *rev,
1308 svn_fs_x__noderev_t *noderev;
1310 /* Go get a fresh node-revision for NODE. */
1311 SVN_ERR(get_node_revision(&noderev, node));
1313 *rev = noderev->copyroot_rev;
1314 *path = noderev->copyroot_path;
1316 return SVN_NO_ERROR;
1320 svn_fs_x__dag_get_copyfrom_rev(svn_revnum_t *rev,
1323 svn_fs_x__noderev_t *noderev;
1325 /* Go get a fresh node-revision for NODE. */
1326 SVN_ERR(get_node_revision(&noderev, node));
1328 *rev = noderev->copyfrom_rev;
1330 return SVN_NO_ERROR;
1334 svn_fs_x__dag_get_copyfrom_path(const char **path,
1337 svn_fs_x__noderev_t *noderev;
1339 /* Go get a fresh node-revision for NODE. */
1340 SVN_ERR(get_node_revision(&noderev, node));
1342 *path = noderev->copyfrom_path;
1344 return SVN_NO_ERROR;
1348 svn_fs_x__dag_update_ancestry(dag_node_t *target,
1350 apr_pool_t *scratch_pool)
1352 svn_fs_x__noderev_t *source_noderev, *target_noderev;
1354 if (! svn_fs_x__dag_check_mutable(target))
1355 return svn_error_createf
1356 (SVN_ERR_FS_NOT_MUTABLE, NULL,
1357 _("Attempted to update ancestry of non-mutable node"));
1359 SVN_ERR(get_node_revision(&source_noderev, source));
1360 SVN_ERR(get_node_revision(&target_noderev, target));
1362 target_noderev->predecessor_id = source_noderev->noderev_id;
1363 target_noderev->predecessor_count = source_noderev->predecessor_count;
1364 target_noderev->predecessor_count++;
1366 return svn_fs_x__put_node_revision(target->fs, target_noderev,