1 /* dag.c : DAG-like interface filesystem
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"
44 #include "dag_cache.h"
47 /* Initializing a filesystem. */
51 /* The filesystem this dag node came from. */
54 /* The node's NODE-REVISION. */
55 svn_fs_x__noderev_t *node_revision;
57 /* The pool to allocate NODE_REVISION in. */
58 apr_pool_t *node_pool;
60 /* Directory entry lookup hint to speed up consecutive calls to
61 svn_fs_x__rep_contents_dir_entry(). Only used for directory nodes.
62 Any value is legal but should default to APR_SIZE_MAX. */
68 /* Trivial helper/accessor functions. */
70 svn_fs_x__dag_node_kind(dag_node_t *node)
72 return node->node_revision->kind;
75 const svn_fs_x__id_t *
76 svn_fs_x__dag_get_id(const dag_node_t *node)
78 return &node->node_revision->noderev_id;
83 svn_fs_x__dag_get_created_path(dag_node_t *node)
85 return node->node_revision->created_path;
90 svn_fs_x__dag_get_fs(dag_node_t *node)
96 svn_fs_x__dag_set_fs(dag_node_t *node,
103 /* Dup NODEREV and all associated data into RESULT_POOL.
104 Leaves the id and is_fresh_txn_root fields as zero bytes. */
105 static svn_fs_x__noderev_t *
106 copy_node_revision(svn_fs_x__noderev_t *noderev,
107 apr_pool_t *result_pool)
109 svn_fs_x__noderev_t *nr = apr_pmemdup(result_pool, noderev,
112 if (noderev->copyfrom_path)
113 nr->copyfrom_path = apr_pstrdup(result_pool, noderev->copyfrom_path);
115 nr->copyroot_path = apr_pstrdup(result_pool, noderev->copyroot_path);
116 nr->data_rep = svn_fs_x__rep_copy(noderev->data_rep, result_pool);
117 nr->prop_rep = svn_fs_x__rep_copy(noderev->prop_rep, result_pool);
119 if (noderev->created_path)
120 nr->created_path = apr_pstrdup(result_pool, noderev->created_path);
126 const svn_fs_x__id_t *
127 svn_fs_x__dag_get_node_id(dag_node_t *node)
129 return &node->node_revision->node_id;
132 const svn_fs_x__id_t *
133 svn_fs_x__dag_get_copy_id(dag_node_t *node)
135 return &node->node_revision->copy_id;
139 svn_fs_x__dag_related_node(dag_node_t *lhs,
142 return svn_fs_x__id_eq(&lhs->node_revision->node_id,
143 &rhs->node_revision->node_id);
147 svn_fs_x__dag_same_line_of_history(dag_node_t *lhs,
150 svn_fs_x__noderev_t *lhs_noderev = lhs->node_revision;
151 svn_fs_x__noderev_t *rhs_noderev = rhs->node_revision;
153 return svn_fs_x__id_eq(&lhs_noderev->node_id, &rhs_noderev->node_id)
154 && svn_fs_x__id_eq(&lhs_noderev->copy_id, &rhs_noderev->copy_id);
158 svn_fs_x__dag_check_mutable(const dag_node_t *node)
160 return svn_fs_x__is_txn(svn_fs_x__dag_get_id(node)->change_set);
164 svn_fs_x__dag_get_node(dag_node_t **node,
166 const svn_fs_x__id_t *id,
167 apr_pool_t *result_pool,
168 apr_pool_t *scratch_pool)
170 dag_node_t *new_node;
171 svn_fs_x__noderev_t *noderev;
173 /* Construct the node. */
174 new_node = apr_pcalloc(result_pool, sizeof(*new_node));
176 new_node->hint = APR_SIZE_MAX;
178 /* Grab the contents so we can inspect the node's kind and created path. */
179 SVN_ERR(svn_fs_x__get_node_revision(&noderev, fs, id,
180 result_pool, scratch_pool));
181 new_node->node_pool = result_pool;
182 new_node->node_revision = noderev;
184 /* Return a fresh new node */
191 svn_fs_x__dag_get_revision(const dag_node_t *node)
193 svn_fs_x__noderev_t *noderev = node->node_revision;
194 return ( svn_fs_x__is_fresh_txn_root(noderev)
195 ? svn_fs_x__get_revnum(noderev->predecessor_id.change_set)
196 : svn_fs_x__get_revnum(noderev->noderev_id.change_set));
199 const svn_fs_x__id_t *
200 svn_fs_x__dag_get_predecessor_id(dag_node_t *node)
202 return &node->node_revision->predecessor_id;
206 svn_fs_x__dag_get_predecessor_count(dag_node_t *node)
208 return node->node_revision->predecessor_count;
212 svn_fs_x__dag_get_mergeinfo_count(dag_node_t *node)
214 return node->node_revision->mergeinfo_count;
218 svn_fs_x__dag_has_mergeinfo(dag_node_t *node)
220 return node->node_revision->has_mergeinfo;
224 svn_fs_x__dag_has_descendants_with_mergeinfo(dag_node_t *node)
226 svn_fs_x__noderev_t *noderev = node->node_revision;
228 if (noderev->kind != svn_node_dir)
231 if (noderev->mergeinfo_count > 1)
233 else if (noderev->mergeinfo_count == 1 && !noderev->has_mergeinfo)
240 /*** Directory node functions ***/
242 /* Some of these are helpers for functions outside this section. */
244 /* Set *ID_P to the noderev-id for entry NAME in PARENT. If no such
245 entry, set *ID_P to NULL but do not error. */
247 svn_fs_x__dir_entry_id(svn_fs_x__id_t *id_p,
250 apr_pool_t *scratch_pool)
252 svn_fs_x__dirent_t *dirent;
253 svn_fs_x__noderev_t *noderev = parent->node_revision;
255 if (noderev->kind != svn_node_dir)
256 return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
257 _("Can't get entries of non-directory"));
259 /* Make sure that NAME is a single path component. */
260 if (! svn_path_is_single_path_component(name))
261 return svn_error_createf
262 (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
263 "Attempted to open node with an illegal name '%s'", name);
265 /* Get a dirent hash for this directory. */
266 SVN_ERR(svn_fs_x__rep_contents_dir_entry(&dirent, parent->fs, noderev,
268 scratch_pool, scratch_pool));
272 svn_fs_x__id_reset(id_p);
278 /* Add or set in PARENT a directory entry NAME pointing to ID.
279 Temporary allocations are done in SCRATCH_POOL.
282 - PARENT is a mutable directory.
283 - ID does not refer to an ancestor of parent
284 - NAME is a single path component
287 set_entry(dag_node_t *parent,
289 const svn_fs_x__id_t *id,
290 svn_node_kind_t kind,
291 svn_fs_x__txn_id_t txn_id,
292 apr_pool_t *scratch_pool)
294 svn_fs_x__noderev_t *parent_noderev = parent->node_revision;
296 /* Set the new entry. */
297 SVN_ERR(svn_fs_x__set_entry(parent->fs, txn_id, parent_noderev, name, id,
298 kind, parent->node_pool, scratch_pool));
300 /* Update cached data. */
301 svn_fs_x__update_dag_cache(parent);
307 /* Make a new entry named NAME in PARENT. If IS_DIR is true, then the
308 node revision the new entry points to will be a directory, else it
309 will be a file. The new node will be allocated in RESULT_POOL. PARENT
310 must be mutable, and must not have an entry named NAME.
312 Use SCRATCH_POOL for all temporary allocations.
315 make_entry(dag_node_t **child_p,
317 const char *parent_path,
319 svn_boolean_t is_dir,
320 svn_fs_x__txn_id_t txn_id,
321 apr_pool_t *result_pool,
322 apr_pool_t *scratch_pool)
324 svn_fs_x__noderev_t new_noderev;
325 svn_fs_x__noderev_t *parent_noderev = parent->node_revision;
327 /* Make sure that NAME is a single path component. */
328 if (! svn_path_is_single_path_component(name))
329 return svn_error_createf
330 (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
331 _("Attempted to create a node with an illegal name '%s'"), name);
333 /* Make sure that parent is a directory */
334 if (parent_noderev->kind != svn_node_dir)
335 return svn_error_create
336 (SVN_ERR_FS_NOT_DIRECTORY, NULL,
337 _("Attempted to create entry in non-directory parent"));
339 /* Check that the parent is mutable. */
340 if (! svn_fs_x__dag_check_mutable(parent))
341 return svn_error_createf
342 (SVN_ERR_FS_NOT_MUTABLE, NULL,
343 _("Attempted to clone child of non-mutable node"));
345 /* Create the new node's NODE-REVISION */
346 memset(&new_noderev, 0, sizeof(new_noderev));
347 new_noderev.kind = is_dir ? svn_node_dir : svn_node_file;
348 new_noderev.created_path = svn_fspath__join(parent_path, name, result_pool);
350 new_noderev.copyroot_path = apr_pstrdup(result_pool,
351 parent_noderev->copyroot_path);
352 new_noderev.copyroot_rev = parent_noderev->copyroot_rev;
353 new_noderev.copyfrom_rev = SVN_INVALID_REVNUM;
354 new_noderev.copyfrom_path = NULL;
355 svn_fs_x__id_reset(&new_noderev.predecessor_id);
357 SVN_ERR(svn_fs_x__create_node
358 (svn_fs_x__dag_get_fs(parent), &new_noderev,
359 &parent_noderev->copy_id, txn_id, scratch_pool));
361 /* Create a new dag_node_t for our new node */
362 SVN_ERR(svn_fs_x__dag_get_node(child_p, svn_fs_x__dag_get_fs(parent),
363 &new_noderev.noderev_id, result_pool,
366 /* We can safely call set_entry because we already know that
367 PARENT is mutable, and we just created CHILD, so we know it has
368 no ancestors (therefore, PARENT cannot be an ancestor of CHILD) */
369 return set_entry(parent, name, &new_noderev.noderev_id,
370 new_noderev.kind, txn_id, scratch_pool);
375 svn_fs_x__dag_dir_entries(apr_array_header_t **entries,
377 apr_pool_t *result_pool,
378 apr_pool_t *scratch_pool)
380 svn_fs_x__noderev_t *noderev = node->node_revision;
382 if (noderev->kind != svn_node_dir)
383 return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
384 _("Can't get entries of non-directory"));
386 return svn_fs_x__rep_contents_dir(entries, node->fs, noderev, result_pool,
392 svn_fs_x__dag_set_entry(dag_node_t *node,
393 const char *entry_name,
394 const svn_fs_x__id_t *id,
395 svn_node_kind_t kind,
396 svn_fs_x__txn_id_t txn_id,
397 apr_pool_t *scratch_pool)
399 /* Check it's a directory. */
400 if (node->node_revision->kind != svn_node_dir)
401 return svn_error_create
402 (SVN_ERR_FS_NOT_DIRECTORY, NULL,
403 _("Attempted to set entry in non-directory node"));
405 /* Check it's mutable. */
406 if (! svn_fs_x__dag_check_mutable(node))
407 return svn_error_create
408 (SVN_ERR_FS_NOT_MUTABLE, NULL,
409 _("Attempted to set entry in immutable node"));
411 return set_entry(node, entry_name, id, kind, txn_id, scratch_pool);
419 svn_fs_x__dag_get_proplist(apr_hash_t **proplist_p,
421 apr_pool_t *result_pool,
422 apr_pool_t *scratch_pool)
424 SVN_ERR(svn_fs_x__get_proplist(proplist_p, node->fs, node->node_revision,
425 result_pool, scratch_pool));
431 svn_fs_x__dag_set_proplist(dag_node_t *node,
432 apr_hash_t *proplist,
433 apr_pool_t *scratch_pool)
435 /* Sanity check: this node better be mutable! */
436 if (! svn_fs_x__dag_check_mutable(node))
439 = svn_fs_x__id_unparse(&node->node_revision->noderev_id,
441 return svn_error_createf
442 (SVN_ERR_FS_NOT_MUTABLE, NULL,
443 "Can't set proplist on *immutable* node-revision %s",
447 /* Set the new proplist. */
448 SVN_ERR(svn_fs_x__set_proplist(node->fs, node->node_revision, proplist,
450 svn_fs_x__update_dag_cache(node);
455 /* Write NODE's NODEREV element to disk. Update the DAG cache.
456 Use SCRATCH_POOL for temporary allocations. */
458 noderev_changed(dag_node_t *node,
459 apr_pool_t *scratch_pool)
461 SVN_ERR(svn_fs_x__put_node_revision(node->fs, node->node_revision,
463 svn_fs_x__update_dag_cache(node);
469 svn_fs_x__dag_increment_mergeinfo_count(dag_node_t *node,
470 apr_int64_t increment,
471 apr_pool_t *scratch_pool)
473 svn_fs_x__noderev_t *noderev = node->node_revision;
475 /* Sanity check: this node better be mutable! */
476 if (! svn_fs_x__dag_check_mutable(node))
478 svn_string_t *idstr = svn_fs_x__id_unparse(&noderev->noderev_id,
480 return svn_error_createf
481 (SVN_ERR_FS_NOT_MUTABLE, NULL,
482 "Can't increment mergeinfo count on *immutable* node-revision %s",
489 noderev->mergeinfo_count += increment;
490 if (noderev->mergeinfo_count < 0)
492 svn_string_t *idstr = svn_fs_x__id_unparse(&noderev->noderev_id,
494 return svn_error_createf
495 (SVN_ERR_FS_CORRUPT, NULL,
496 apr_psprintf(scratch_pool,
497 _("Can't increment mergeinfo count on node-revision %%s "
498 "to negative value %%%s"),
500 idstr->data, noderev->mergeinfo_count);
502 if (noderev->mergeinfo_count > 1 && noderev->kind == svn_node_file)
504 svn_string_t *idstr = svn_fs_x__id_unparse(&noderev->noderev_id,
506 return svn_error_createf
507 (SVN_ERR_FS_CORRUPT, NULL,
508 apr_psprintf(scratch_pool,
509 _("Can't increment mergeinfo count on *file* "
510 "node-revision %%s to %%%s (> 1)"),
512 idstr->data, noderev->mergeinfo_count);
516 return noderev_changed(node, scratch_pool);
520 svn_fs_x__dag_set_has_mergeinfo(dag_node_t *node,
521 svn_boolean_t has_mergeinfo,
522 apr_pool_t *scratch_pool)
524 /* Sanity check: this node better be mutable! */
525 if (! svn_fs_x__dag_check_mutable(node))
528 = svn_fs_x__id_unparse(&node->node_revision->noderev_id,
530 return svn_error_createf
531 (SVN_ERR_FS_NOT_MUTABLE, NULL,
532 "Can't set mergeinfo flag on *immutable* node-revision %s",
536 node->node_revision->has_mergeinfo = has_mergeinfo;
539 return noderev_changed(node, scratch_pool);
546 svn_fs_x__dag_root(dag_node_t **node_p,
548 svn_fs_x__change_set_t change_set,
549 apr_pool_t *result_pool,
550 apr_pool_t *scratch_pool)
552 svn_fs_x__id_t root_id;
553 root_id.change_set = change_set;
554 root_id.number = SVN_FS_X__ITEM_INDEX_ROOT_NODE;
556 return svn_fs_x__dag_get_node(node_p, fs, &root_id, result_pool,
562 svn_fs_x__dag_clone_child(dag_node_t **child_p,
564 const char *parent_path,
566 const svn_fs_x__id_t *copy_id,
567 svn_fs_x__txn_id_t txn_id,
568 svn_boolean_t is_parent_copyroot,
569 apr_pool_t *result_pool,
570 apr_pool_t *scratch_pool)
572 dag_node_t *cur_entry; /* parent's current entry named NAME */
573 const svn_fs_x__id_t *new_node_id; /* node id we'll put into NEW_NODE */
574 svn_fs_t *fs = svn_fs_x__dag_get_fs(parent);
576 /* First check that the parent is mutable. */
577 if (! svn_fs_x__dag_check_mutable(parent))
578 return svn_error_createf
579 (SVN_ERR_FS_NOT_MUTABLE, NULL,
580 "Attempted to clone child of non-mutable node");
582 /* Make sure that NAME is a single path component. */
583 if (! svn_path_is_single_path_component(name))
584 return svn_error_createf
585 (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
586 "Attempted to make a child clone with an illegal name '%s'", name);
588 /* Find the node named NAME in PARENT's entries list if it exists. */
589 SVN_ERR(svn_fs_x__dag_open(&cur_entry, parent, name, scratch_pool,
592 return svn_error_createf
593 (SVN_ERR_FS_NOT_FOUND, NULL,
594 "Attempted to open non-existent child node '%s'", name);
596 /* Check for mutability in the node we found. If it's mutable, we
597 don't need to clone it. */
598 if (svn_fs_x__dag_check_mutable(cur_entry))
600 /* This has already been cloned */
601 new_node_id = svn_fs_x__dag_get_id(cur_entry);
605 svn_fs_x__noderev_t *noderev = cur_entry->node_revision;
607 if (is_parent_copyroot)
609 svn_fs_x__noderev_t *parent_noderev = parent->node_revision;
610 noderev->copyroot_rev = parent_noderev->copyroot_rev;
611 noderev->copyroot_path = apr_pstrdup(scratch_pool,
612 parent_noderev->copyroot_path);
615 noderev->copyfrom_path = NULL;
616 noderev->copyfrom_rev = SVN_INVALID_REVNUM;
618 noderev->predecessor_id = noderev->noderev_id;
619 noderev->predecessor_count++;
620 noderev->created_path = svn_fspath__join(parent_path, name,
624 copy_id = &noderev->copy_id;
626 SVN_ERR(svn_fs_x__create_successor(fs, noderev, copy_id, txn_id,
628 new_node_id = &noderev->noderev_id;
630 /* Replace the ID in the parent's ENTRY list with the ID which
631 refers to the mutable clone of this child. */
632 SVN_ERR(set_entry(parent, name, new_node_id, noderev->kind, txn_id,
636 /* Initialize the youngster. */
637 return svn_fs_x__dag_get_node(child_p, fs, new_node_id, result_pool,
642 /* Delete all mutable node revisions reachable from node ID, including
643 ID itself, from FS's `nodes' table. Also delete any mutable
644 representations and strings associated with that node revision.
645 ID may refer to a file or directory, which may be mutable or immutable.
647 Use SCRATCH_POOL for temporary allocations.
650 delete_if_mutable(svn_fs_t *fs,
651 const svn_fs_x__id_t *id,
652 apr_pool_t *scratch_pool)
657 SVN_ERR(svn_fs_x__dag_get_node(&node, fs, id, scratch_pool, scratch_pool));
659 /* If immutable, do nothing and return immediately. */
660 if (! svn_fs_x__dag_check_mutable(node))
663 /* Else it's mutable. Recurse on directories... */
664 if (node->node_revision->kind == svn_node_dir)
666 apr_array_header_t *entries;
668 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
670 /* Loop over directory entries */
671 SVN_ERR(svn_fs_x__dag_dir_entries(&entries, node, scratch_pool,
673 for (i = 0; i < entries->nelts; ++i)
675 const svn_fs_x__id_t *noderev_id
676 = &APR_ARRAY_IDX(entries, i, svn_fs_x__dirent_t *)->id;
678 svn_pool_clear(iterpool);
679 SVN_ERR(delete_if_mutable(fs, noderev_id, iterpool));
682 svn_pool_destroy(iterpool);
685 /* ... then delete the node itself, after deleting any mutable
686 representations and strings it points to. */
687 return svn_fs_x__delete_node_revision(fs, id, scratch_pool);
692 svn_fs_x__dag_delete(dag_node_t *parent,
694 svn_fs_x__txn_id_t txn_id,
695 apr_pool_t *scratch_pool)
697 svn_fs_x__noderev_t *parent_noderev = parent->node_revision;
698 svn_fs_t *fs = parent->fs;
699 svn_fs_x__dirent_t *dirent;
702 /* Make sure parent is a directory. */
703 if (parent_noderev->kind != svn_node_dir)
704 return svn_error_createf
705 (SVN_ERR_FS_NOT_DIRECTORY, NULL,
706 "Attempted to delete entry '%s' from *non*-directory node", name);
708 /* Make sure 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 delete entry '%s' from immutable directory node", name);
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 delete a node with an illegal name '%s'", name);
720 /* We allocate a few potentially heavy temporary objects (file buffers
721 and directories). Make sure we don't keep them around for longer
723 subpool = svn_pool_create(scratch_pool);
725 /* Search this directory for a dirent with that NAME. */
726 SVN_ERR(svn_fs_x__rep_contents_dir_entry(&dirent, fs, parent_noderev,
730 /* If we never found ID in ENTRIES (perhaps because there are no
731 ENTRIES, perhaps because ID just isn't in the existing ENTRIES
732 ... it doesn't matter), return an error. */
734 return svn_error_createf
735 (SVN_ERR_FS_NO_SUCH_ENTRY, NULL,
736 "Delete failed--directory has no entry '%s'", name);
738 /* If mutable, remove it and any mutable children from db. */
739 SVN_ERR(delete_if_mutable(parent->fs, &dirent->id, subpool));
741 /* Remove this entry from its parent's entries list. */
742 SVN_ERR(set_entry(parent, name, NULL, svn_node_unknown, txn_id, subpool));
744 svn_pool_destroy(subpool);
750 svn_fs_x__dag_make_file(dag_node_t **child_p,
752 const char *parent_path,
754 svn_fs_x__txn_id_t txn_id,
755 apr_pool_t *result_pool,
756 apr_pool_t *scratch_pool)
758 /* Call our little helper function */
759 return make_entry(child_p, parent, parent_path, name, FALSE, txn_id,
760 result_pool, scratch_pool);
765 svn_fs_x__dag_make_dir(dag_node_t **child_p,
767 const char *parent_path,
769 svn_fs_x__txn_id_t txn_id,
770 apr_pool_t *result_pool,
771 apr_pool_t *scratch_pool)
773 /* Call our little helper function */
774 return make_entry(child_p, parent, parent_path, name, TRUE, txn_id,
775 result_pool, scratch_pool);
780 svn_fs_x__dag_get_contents(svn_stream_t **contents_p,
782 apr_pool_t *result_pool)
784 /* Make sure our node is a file. */
785 if (file->node_revision->kind != svn_node_file)
786 return svn_error_createf
787 (SVN_ERR_FS_NOT_FILE, NULL,
788 "Attempted to get textual contents of a *non*-file node");
790 /* Get a stream to the contents. */
791 SVN_ERR(svn_fs_x__get_contents(contents_p, file->fs,
792 file->node_revision->data_rep, TRUE,
800 svn_fs_x__dag_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
803 apr_pool_t *result_pool,
804 apr_pool_t *scratch_pool)
806 svn_fs_x__noderev_t *src_noderev = source ? source->node_revision : NULL;
807 svn_fs_x__noderev_t *tgt_noderev = target->node_revision;
809 /* Make sure our nodes are files. */
810 if ((source && src_noderev->kind != svn_node_file)
811 || tgt_noderev->kind != svn_node_file)
812 return svn_error_createf
813 (SVN_ERR_FS_NOT_FILE, NULL,
814 "Attempted to get textual contents of a *non*-file node");
816 /* Get the delta stream. */
817 return svn_fs_x__get_file_delta_stream(stream_p, target->fs,
818 src_noderev, tgt_noderev,
819 result_pool, scratch_pool);
824 svn_fs_x__dag_try_process_file_contents(svn_boolean_t *success,
826 svn_fs_process_contents_func_t processor,
828 apr_pool_t *scratch_pool)
830 return svn_fs_x__try_process_file_contents(success, node->fs,
832 processor, baton, scratch_pool);
837 svn_fs_x__dag_file_length(svn_filesize_t *length,
840 /* Make sure our node is a file. */
841 if (file->node_revision->kind != svn_node_file)
842 return svn_error_createf
843 (SVN_ERR_FS_NOT_FILE, NULL,
844 "Attempted to get length of a *non*-file node");
846 return svn_fs_x__file_length(length, file->node_revision);
851 svn_fs_x__dag_file_checksum(svn_checksum_t **checksum,
853 svn_checksum_kind_t kind,
854 apr_pool_t *result_pool)
856 if (file->node_revision->kind != svn_node_file)
857 return svn_error_createf
858 (SVN_ERR_FS_NOT_FILE, NULL,
859 "Attempted to get checksum of a *non*-file node");
861 return svn_fs_x__file_checksum(checksum, file->node_revision, kind,
867 svn_fs_x__dag_get_edit_stream(svn_stream_t **contents,
869 apr_pool_t *result_pool)
871 /* Make sure our node is a file. */
872 if (file->node_revision->kind != svn_node_file)
873 return svn_error_createf
874 (SVN_ERR_FS_NOT_FILE, NULL,
875 "Attempted to set textual contents of a *non*-file node");
877 /* Make sure our node is mutable. */
878 if (! svn_fs_x__dag_check_mutable(file))
879 return svn_error_createf
880 (SVN_ERR_FS_NOT_MUTABLE, NULL,
881 "Attempted to set textual contents of an immutable node");
883 SVN_ERR(svn_fs_x__set_contents(contents, file->fs, file->node_revision,
891 svn_fs_x__dag_finalize_edits(dag_node_t *file,
892 const svn_checksum_t *checksum,
893 apr_pool_t *scratch_pool)
897 svn_checksum_t *file_checksum;
899 SVN_ERR(svn_fs_x__dag_file_checksum(&file_checksum, file,
900 checksum->kind, scratch_pool));
901 if (!svn_checksum_match(checksum, file_checksum))
902 return svn_checksum_mismatch_err(checksum, file_checksum,
904 _("Checksum mismatch for '%s'"),
905 file->node_revision->created_path);
908 svn_fs_x__update_dag_cache(file);
914 svn_fs_x__dag_dup(const dag_node_t *node,
915 apr_pool_t *result_pool)
917 /* Allocate our new node. */
918 dag_node_t *new_node = apr_pmemdup(result_pool, node, sizeof(*new_node));
920 /* Copy sub-structures. */
921 new_node->node_revision = copy_node_revision(node->node_revision,
923 new_node->node_pool = result_pool;
930 svn_fs_x__dag_open(dag_node_t **child_p,
933 apr_pool_t *result_pool,
934 apr_pool_t *scratch_pool)
936 svn_fs_x__id_t node_id;
938 /* Ensure that NAME exists in PARENT's entry list. */
939 SVN_ERR(svn_fs_x__dir_entry_id(&node_id, parent, name, scratch_pool));
940 if (! svn_fs_x__id_used(&node_id))
946 /* Now get the node that was requested. */
947 return svn_fs_x__dag_get_node(child_p, svn_fs_x__dag_get_fs(parent),
948 &node_id, result_pool, scratch_pool);
953 svn_fs_x__dag_copy(dag_node_t *to_node,
955 dag_node_t *from_node,
956 svn_boolean_t preserve_history,
957 svn_revnum_t from_rev,
958 const char *from_path,
959 svn_fs_x__txn_id_t txn_id,
960 apr_pool_t *scratch_pool)
962 const svn_fs_x__id_t *id;
964 if (preserve_history)
966 svn_fs_x__noderev_t *to_noderev;
967 svn_fs_x__id_t copy_id;
968 svn_fs_t *fs = svn_fs_x__dag_get_fs(from_node);
970 /* Make a copy of the original node revision. */
971 to_noderev = copy_node_revision(from_node->node_revision, scratch_pool);
973 /* Reserve a copy ID for this new copy. */
974 SVN_ERR(svn_fs_x__reserve_copy_id(©_id, fs, txn_id, scratch_pool));
976 /* Create a successor with its predecessor pointing at the copy
978 to_noderev->predecessor_id = to_noderev->noderev_id;
979 to_noderev->predecessor_count++;
980 to_noderev->created_path =
981 svn_fspath__join(svn_fs_x__dag_get_created_path(to_node), entry,
983 to_noderev->copyfrom_path = apr_pstrdup(scratch_pool, from_path);
984 to_noderev->copyfrom_rev = from_rev;
986 /* Set the copyroot equal to our own id. */
987 to_noderev->copyroot_path = NULL;
989 SVN_ERR(svn_fs_x__create_successor(fs, to_noderev,
990 ©_id, txn_id, scratch_pool));
991 id = &to_noderev->noderev_id;
993 else /* don't preserve history */
995 id = svn_fs_x__dag_get_id(from_node);
998 /* Set the entry in to_node to the new id. */
999 return svn_fs_x__dag_set_entry(to_node, entry, id,
1000 from_node->node_revision->kind,
1001 txn_id, scratch_pool);
1006 /*** Comparison. ***/
1009 svn_fs_x__dag_things_different(svn_boolean_t *props_changed,
1010 svn_boolean_t *contents_changed,
1013 svn_boolean_t strict,
1014 apr_pool_t *scratch_pool)
1016 svn_fs_x__noderev_t *noderev1 = node1->node_revision;
1017 svn_fs_x__noderev_t *noderev2 = node2->node_revision;
1021 /* If we have no place to store our results, don't bother doing
1023 if (! props_changed && ! contents_changed)
1024 return SVN_NO_ERROR;
1026 fs = svn_fs_x__dag_get_fs(node1);
1028 /* Compare property keys. */
1029 if (props_changed != NULL)
1031 SVN_ERR(svn_fs_x__prop_rep_equal(&same, fs, noderev1, noderev2,
1032 strict, scratch_pool));
1033 *props_changed = !same;
1036 /* Compare contents keys. */
1037 if (contents_changed != NULL)
1038 *contents_changed = !svn_fs_x__file_text_rep_equal(noderev1->data_rep,
1039 noderev2->data_rep);
1041 return SVN_NO_ERROR;
1045 svn_fs_x__dag_get_copyroot(svn_revnum_t *rev,
1049 *rev = node->node_revision->copyroot_rev;
1050 *path = node->node_revision->copyroot_path;
1054 svn_fs_x__dag_get_copyfrom_rev(dag_node_t *node)
1056 return node->node_revision->copyfrom_rev;
1060 svn_fs_x__dag_get_copyfrom_path(dag_node_t *node)
1062 return node->node_revision->copyfrom_path;
1066 svn_fs_x__dag_update_ancestry(dag_node_t *target,
1068 apr_pool_t *scratch_pool)
1070 svn_fs_x__noderev_t *source_noderev = source->node_revision;
1071 svn_fs_x__noderev_t *target_noderev = target->node_revision;
1073 if (! svn_fs_x__dag_check_mutable(target))
1074 return svn_error_createf
1075 (SVN_ERR_FS_NOT_MUTABLE, NULL,
1076 _("Attempted to update ancestry of non-mutable node"));
1078 target_noderev->predecessor_id = source_noderev->noderev_id;
1079 target_noderev->predecessor_count = source_noderev->predecessor_count;
1080 target_noderev->predecessor_count++;
1082 return noderev_changed(target, scratch_pool);