1 /* tree.c : tree-like filesystem, built on DAG 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 * ====================================================================
24 /* The job of this layer is to take a filesystem with lots of node
25 sharing going on --- the real DAG filesystem as it appears in the
26 database --- and make it look and act like an ordinary tree
27 filesystem, with no sharing.
29 We do just-in-time cloning: you can walk from some unfinished
30 transaction's root down into directories and files shared with
31 committed revisions; as soon as you try to change something, the
32 appropriate nodes get cloned (and parent directory entries updated)
33 invisibly, behind your back. Any other references you have to
34 nodes that have been cloned by other changes, even made by other
35 processes, are automatically updated to point to the right clones. */
41 #include <apr_pools.h>
45 #include "svn_private_config.h"
46 #include "svn_pools.h"
47 #include "svn_error.h"
49 #include "svn_mergeinfo.h"
51 #include "svn_props.h"
52 #include "svn_sorts.h"
55 #include "cached_data.h"
62 #include "temp_serializer.h"
63 #include "transaction.h"
66 #include "private/svn_mergeinfo_private.h"
67 #include "private/svn_subr_private.h"
68 #include "private/svn_fs_util.h"
69 #include "private/svn_fspath.h"
70 #include "../libsvn_fs/fs-loader.h"
74 /* The root structures.
76 Why do they contain different data? Well, transactions are mutable
77 enough that it isn't safe to cache the DAG node for the root
78 directory or the hash of copyfrom data: somebody else might modify
79 them concurrently on disk! (Why is the DAG node cache safer than
80 the root DAG node? When cloning transaction DAG nodes in and out
81 of the cache, all of the possibly-mutable data from the
82 node_revision_t inside the dag_node_t is dropped.) Additionally,
83 revisions are immutable enough that their DAG node cache can be
84 kept in the FS object and shared among multiple revision root
87 typedef dag_node_t fs_rev_root_data_t;
89 typedef struct fs_txn_root_data_t
91 /* TXN_ID value from the main struct but as a struct instead of a string */
92 svn_fs_fs__id_part_t txn_id;
94 /* Cache of txn DAG nodes (without their nested noderevs, because
95 * it's mutable). Same keys/values as ffd->rev_node_cache. */
96 svn_cache__t *txn_node_cache;
99 /* Declared here to resolve the circular dependencies. */
100 static svn_error_t * get_dag(dag_node_t **dag_node_p,
105 static svn_fs_root_t *make_revision_root(svn_fs_t *fs, svn_revnum_t rev,
106 dag_node_t *root_dir,
109 static svn_error_t *make_txn_root(svn_fs_root_t **root_p,
111 const svn_fs_fs__id_part_t *txn,
112 svn_revnum_t base_rev,
116 static svn_error_t *fs_closest_copy(svn_fs_root_t **root_p,
123 /*** Node Caching ***/
125 /* 1st level cache */
127 /* An entry in the first-level cache. REVISION and PATH form the key that
128 will ultimately be matched.
130 typedef struct cache_entry_t
132 /* hash value derived from PATH, REVISION.
133 Used to short-circuit failed lookups. */
134 apr_uint32_t hash_value;
136 /* revision to which the NODE belongs */
137 svn_revnum_t revision;
139 /* path of the NODE */
142 /* cached value of strlen(PATH). */
145 /* the node allocated in the cache's pool. NULL for empty entries. */
149 /* Number of entries in the cache. Keep this low to keep pressure on the
150 CPU caches low as well. A binary value is most efficient. If we walk
151 a directory tree, we want enough entries to store nodes for all files
152 without overwriting the nodes for the parent folder. That way, there
153 will be no unnecessary misses (except for a few random ones caused by
156 The actual number of instances may be higher but entries that got
157 overwritten are no longer visible.
159 enum { BUCKET_COUNT = 256 };
161 /* The actual cache structure. All nodes will be allocated in POOL.
162 When the number of INSERTIONS (i.e. objects created form that pool)
163 exceeds a certain threshold, the pool will be cleared and the cache
166 struct fs_fs_dag_cache_t
168 /* fixed number of (possibly empty) cache entries */
169 cache_entry_t buckets[BUCKET_COUNT];
171 /* pool used for all node allocation */
174 /* number of entries created from POOL since the last cleanup */
175 apr_size_t insertions;
177 /* Property lookups etc. have a very high locality (75% re-hit).
178 Thus, remember the last hit location for optimistic lookup. */
181 /* Position of the last bucket hit that actually had a DAG node in it.
182 LAST_HIT may refer to a bucket that matches path@rev but has not
183 its NODE element set, yet.
184 This value is a mere hint for optimistic lookup and any value is
185 valid (as long as it is < BUCKET_COUNT). */
186 apr_size_t last_non_empty;
190 svn_fs_fs__create_dag_cache(apr_pool_t *pool)
192 fs_fs_dag_cache_t *result = apr_pcalloc(pool, sizeof(*result));
193 result->pool = svn_pool_create(pool);
198 /* Clears the CACHE at regular intervals (destroying all cached nodes)
201 auto_clear_dag_cache(fs_fs_dag_cache_t* cache)
203 if (cache->insertions > BUCKET_COUNT)
205 svn_pool_clear(cache->pool);
207 memset(cache->buckets, 0, sizeof(cache->buckets));
208 cache->insertions = 0;
212 /* For the given REVISION and PATH, return the respective entry in CACHE.
213 If the entry is empty, its NODE member will be NULL and the caller
214 may then set it to the corresponding DAG node allocated in CACHE->POOL.
216 static cache_entry_t *
217 cache_lookup( fs_fs_dag_cache_t *cache
218 , svn_revnum_t revision
221 apr_size_t i, bucket_index;
222 apr_size_t path_len = strlen(path);
223 apr_uint32_t hash_value = (apr_uint32_t)revision;
225 #if SVN_UNALIGNED_ACCESS_IS_OK
226 /* "randomizing" / distributing factor used in our hash function */
227 const apr_uint32_t factor = 0xd1f3da69;
230 /* optimistic lookup: hit the same bucket again? */
231 cache_entry_t *result = &cache->buckets[cache->last_hit];
232 if ( (result->revision == revision)
233 && (result->path_len == path_len)
234 && !memcmp(result->path, path, path_len))
236 /* Remember the position of the last node we found in this cache. */
238 cache->last_non_empty = cache->last_hit;
243 /* need to do a full lookup. Calculate the hash value
244 (HASH_VALUE has been initialized to REVISION).
246 Note that the actual hash function is arbitrary as long as its result
247 in HASH_VALUE only depends on REVISION and *PATH. However, we try to
248 make as much of *PATH influence the result as possible to get an "even"
249 spread across the hash buckets (maximizes our cache retention rate and
252 When chunked access is possible (independent of the PATH pointer's
253 value!), we read 4 bytes at once and multiply the hash value with a
254 FACTOR that mirror / pattern / shift all 4 input bytes to various bits
255 of the result. The final result will be taken from the MSBs.
257 When chunked access is not possible (not supported by CPU or odd bytes
258 at the end of *PATH), we use the simple traditional "* 33" hash
259 function that works very well with texts / paths and that e.g. APR uses.
261 Please note that the bytewise and the chunked calculation are *NOT*
262 interchangeable as they will yield different results for the same input.
263 For any given machine and *PATH, we must use a fixed combination of the
267 #if SVN_UNALIGNED_ACCESS_IS_OK
268 /* We relax the dependency chain between iterations by processing
269 two chunks from the input per hash_value self-multiplication.
270 The HASH_VALUE update latency is now 1 MUL latency + 1 ADD latency
271 per 2 chunks instead of 1 chunk.
273 for (; i + 8 <= path_len; i += 8)
274 hash_value = hash_value * factor * factor
275 + ( *(const apr_uint32_t*)(path + i) * factor
276 + *(const apr_uint32_t*)(path + i + 4));
279 for (; i < path_len; ++i)
280 /* Help GCC to minimize the HASH_VALUE update latency by splitting the
281 MUL 33 of the naive implementation: h = h * 33 + path[i]. This
282 shortens the dependency chain from 1 shift + 2 ADDs to 1 shift + 1 ADD.
284 hash_value = hash_value * 32 + (hash_value + (unsigned char)path[i]);
286 bucket_index = hash_value + (hash_value >> 16);
287 bucket_index = (bucket_index + (bucket_index >> 8)) % BUCKET_COUNT;
289 /* access the corresponding bucket and remember its location */
290 result = &cache->buckets[bucket_index];
291 cache->last_hit = bucket_index;
293 /* if it is *NOT* a match, clear the bucket, expect the caller to fill
294 in the node and count it as an insertion */
295 if ( (result->hash_value != hash_value)
296 || (result->revision != revision)
297 || (result->path_len != path_len)
298 || memcmp(result->path, path, path_len))
300 result->hash_value = hash_value;
301 result->revision = revision;
302 if (result->path_len < path_len)
303 result->path = apr_palloc(cache->pool, path_len + 1);
304 result->path_len = path_len;
305 memcpy(result->path, path, path_len + 1);
311 else if (result->node)
313 /* This bucket is valid & has a suitable DAG node in it.
314 Remember its location. */
315 cache->last_non_empty = bucket_index;
321 /* Optimistic lookup using the last seen non-empty location in CACHE.
322 Return the node of that entry, if it is still in use and matches PATH.
323 Return NULL otherwise. Since the caller usually already knows the path
324 length, provide it in PATH_LEN. */
326 cache_lookup_last_path(fs_fs_dag_cache_t *cache,
330 cache_entry_t *result = &cache->buckets[cache->last_non_empty];
331 assert(strlen(path) == path_len);
334 && (result->path_len == path_len)
335 && !memcmp(result->path, path, path_len))
343 /* 2nd level cache */
345 /* Find and return the DAG node cache for ROOT and the key that
346 should be used for PATH.
348 Pool will only be used for allocating a new keys if necessary */
350 locate_cache(svn_cache__t **cache,
356 if (root->is_txn_root)
358 fs_txn_root_data_t *frd = root->fsap_data;
361 *cache = frd->txn_node_cache;
367 fs_fs_data_t *ffd = root->fs->fsap_data;
370 *cache = ffd->rev_node_cache;
372 *key = svn_fs_fs__combine_number_and_string(root->rev, path, pool);
376 /* In *NODE_P, return the DAG node for PATH from ROOT's node cache, or NULL
377 if the node isn't cached. *NODE_P is allocated in POOL. */
379 dag_node_cache_get(dag_node_t **node_p,
385 dag_node_t *node = NULL;
389 SVN_ERR_ASSERT(*path == '/');
391 if (!root->is_txn_root)
393 /* immutable DAG node. use the global caches for it */
395 fs_fs_data_t *ffd = root->fs->fsap_data;
396 cache_entry_t *bucket;
398 auto_clear_dag_cache(ffd->dag_node_cache);
399 bucket = cache_lookup(ffd->dag_node_cache, root->rev, path);
400 if (bucket->node == NULL)
402 locate_cache(&cache, &key, root, path, pool);
403 SVN_ERR(svn_cache__get((void **)&node, &found, cache, key, pool));
406 /* Patch up the FS, since this might have come from an old FS
408 svn_fs_fs__dag_set_fs(node, root->fs);
410 /* Retain the DAG node in L1 cache. */
411 bucket->node = svn_fs_fs__dag_dup(node,
412 ffd->dag_node_cache->pool);
417 /* Copy the node from L1 cache into the passed-in POOL. */
418 node = svn_fs_fs__dag_dup(bucket->node, pool);
423 /* DAG is mutable / may become invalid. Use the TXN-local cache */
425 locate_cache(&cache, &key, root, path, pool);
427 SVN_ERR(svn_cache__get((void **) &node, &found, cache, key, pool));
430 /* Patch up the FS, since this might have come from an old FS
432 svn_fs_fs__dag_set_fs(node, root->fs);
442 /* Add the NODE for PATH to ROOT's node cache. */
444 dag_node_cache_set(svn_fs_root_t *root,
452 SVN_ERR_ASSERT(*path == '/');
454 locate_cache(&cache, &key, root, path, pool);
455 return svn_cache__set(cache, key, node, pool);
459 /* Baton for find_descendants_in_cache. */
462 apr_array_header_t *list;
466 /* If the given item is a descendant of BATON->PATH, push
467 * it onto BATON->LIST (copying into BATON->POOL). Implements
468 * the svn_iter_apr_hash_cb_t prototype. */
470 find_descendants_in_cache(void *baton,
476 struct fdic_baton *b = baton;
477 const char *item_path = key;
479 if (svn_fspath__skip_ancestor(b->path, item_path))
480 APR_ARRAY_PUSH(b->list, const char *) = apr_pstrdup(b->pool, item_path);
485 /* Invalidate cache entries for PATH and any of its children. This
486 should *only* be called on a transaction root! */
488 dag_node_cache_invalidate(svn_fs_root_t *root,
494 apr_pool_t *iterpool;
498 b.pool = svn_pool_create(pool);
499 b.list = apr_array_make(b.pool, 1, sizeof(const char *));
501 SVN_ERR_ASSERT(root->is_txn_root);
502 locate_cache(&cache, NULL, root, NULL, b.pool);
505 SVN_ERR(svn_cache__iter(NULL, cache, find_descendants_in_cache,
508 iterpool = svn_pool_create(b.pool);
510 for (i = 0; i < b.list->nelts; i++)
512 const char *descendant = APR_ARRAY_IDX(b.list, i, const char *);
513 svn_pool_clear(iterpool);
514 SVN_ERR(svn_cache__set(cache, descendant, NULL, iterpool));
517 svn_pool_destroy(iterpool);
518 svn_pool_destroy(b.pool);
524 /* Creating transaction and revision root nodes. */
527 svn_fs_fs__txn_root(svn_fs_root_t **root_p,
531 apr_uint32_t flags = 0;
532 apr_hash_t *txnprops;
534 /* Look for the temporary txn props representing 'flags'. */
535 SVN_ERR(svn_fs_fs__txn_proplist(&txnprops, txn, pool));
538 if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD))
539 flags |= SVN_FS_TXN_CHECK_OOD;
541 if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS))
542 flags |= SVN_FS_TXN_CHECK_LOCKS;
545 return make_txn_root(root_p, txn->fs, svn_fs_fs__txn_get_id(txn),
546 txn->base_rev, flags, pool);
551 svn_fs_fs__revision_root(svn_fs_root_t **root_p,
556 dag_node_t *root_dir;
558 SVN_ERR(svn_fs__check_fs(fs, TRUE));
560 SVN_ERR(svn_fs_fs__dag_revision_root(&root_dir, fs, rev, pool));
562 *root_p = make_revision_root(fs, rev, root_dir, pool);
569 /* Getting dag nodes for roots. */
571 /* Return the transaction ID to a given transaction ROOT. */
572 static const svn_fs_fs__id_part_t *
573 root_txn_id(svn_fs_root_t *root)
575 fs_txn_root_data_t *frd = root->fsap_data;
576 assert(root->is_txn_root);
581 /* Set *NODE_P to a freshly opened dag node referring to the root
582 directory of ROOT, allocating from POOL. */
584 root_node(dag_node_t **node_p,
588 if (root->is_txn_root)
590 /* It's a transaction root. Open a fresh copy. */
591 return svn_fs_fs__dag_txn_root(node_p, root->fs, root_txn_id(root),
596 /* It's a revision root, so we already have its root directory
598 dag_node_t *root_dir = root->fsap_data;
599 *node_p = svn_fs_fs__dag_dup(root_dir, pool);
605 /* Set *NODE_P to a mutable root directory for ROOT, cloning if
606 necessary, allocating in POOL. ROOT must be a transaction root.
607 Use ERROR_PATH in error messages. */
609 mutable_root_node(dag_node_t **node_p,
611 const char *error_path,
614 if (root->is_txn_root)
616 /* It's a transaction root. Open a fresh copy. */
617 return svn_fs_fs__dag_clone_root(node_p, root->fs, root_txn_id(root),
621 /* If it's not a transaction root, we can't change its contents. */
622 return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path);
627 /* Traversing directory paths. */
629 typedef enum copy_id_inherit_t
631 copy_id_inherit_unknown = 0,
632 copy_id_inherit_self,
633 copy_id_inherit_parent,
638 /* A linked list representing the path from a node up to a root
639 directory. We use this for cloning, and for operations that need
640 to deal with both a node and its parent directory. For example, a
641 `delete' operation needs to know that the node actually exists, but
642 also needs to change the parent directory. */
643 typedef struct parent_path_t
646 /* A node along the path. This could be the final node, one of its
647 parents, or the root. Every parent path ends with an element for
648 the root directory. */
651 /* The name NODE has in its parent directory. This is zero for the
652 root directory, which (obviously) has no name in its parent. */
655 /* The parent of NODE, or zero if NODE is the root directory. */
656 struct parent_path_t *parent;
658 /* The copy ID inheritance style. */
659 copy_id_inherit_t copy_inherit;
661 /* If copy ID inheritance style is copy_id_inherit_new, this is the
662 path which should be implicitly copied; otherwise, this is NULL. */
663 const char *copy_src_path;
667 /* Return a text string describing the absolute path of parent_path
668 PARENT_PATH. It will be allocated in POOL. */
670 parent_path_path(parent_path_t *parent_path,
673 const char *path_so_far = "/";
674 if (parent_path->parent)
675 path_so_far = parent_path_path(parent_path->parent, pool);
676 return parent_path->entry
677 ? svn_fspath__join(path_so_far, parent_path->entry, pool)
682 /* Return the FS path for the parent path chain object CHILD relative
683 to its ANCESTOR in the same chain, allocated in POOL. */
685 parent_path_relpath(parent_path_t *child,
686 parent_path_t *ancestor,
689 const char *path_so_far = "";
690 parent_path_t *this_node = child;
691 while (this_node != ancestor)
693 assert(this_node != NULL);
694 path_so_far = svn_relpath_join(this_node->entry, path_so_far, pool);
695 this_node = this_node->parent;
702 /* Choose a copy ID inheritance method *INHERIT_P to be used in the
703 event that immutable node CHILD in FS needs to be made mutable. If
704 the inheritance method is copy_id_inherit_new, also return a
705 *COPY_SRC_PATH on which to base the new copy ID (else return NULL
706 for that path). CHILD must have a parent (it cannot be the root
707 node). Allocations are taken from POOL. */
709 get_copy_inheritance(copy_id_inherit_t *inherit_p,
710 const char **copy_src_path,
712 parent_path_t *child,
715 const svn_fs_id_t *child_id, *parent_id, *copyroot_id;
716 const svn_fs_fs__id_part_t *child_copy_id, *parent_copy_id;
717 const char *id_path = NULL;
718 svn_fs_root_t *copyroot_root;
719 dag_node_t *copyroot_node;
720 svn_revnum_t copyroot_rev;
721 const char *copyroot_path;
723 SVN_ERR_ASSERT(child && child->parent);
725 /* Initialize some convenience variables. */
726 child_id = svn_fs_fs__dag_get_id(child->node);
727 parent_id = svn_fs_fs__dag_get_id(child->parent->node);
728 child_copy_id = svn_fs_fs__id_copy_id(child_id);
729 parent_copy_id = svn_fs_fs__id_copy_id(parent_id);
731 /* If this child is already mutable, we have nothing to do. */
732 if (svn_fs_fs__id_is_txn(child_id))
734 *inherit_p = copy_id_inherit_self;
735 *copy_src_path = NULL;
739 /* From this point on, we'll assume that the child will just take
740 its copy ID from its parent. */
741 *inherit_p = copy_id_inherit_parent;
742 *copy_src_path = NULL;
744 /* Special case: if the child's copy ID is '0', use the parent's
746 if (svn_fs_fs__id_part_is_root(child_copy_id))
749 /* Compare the copy IDs of the child and its parent. If they are
750 the same, then the child is already on the same branch as the
751 parent, and should use the same mutability copy ID that the
753 if (svn_fs_fs__id_part_eq(child_copy_id, parent_copy_id))
756 /* If the child is on the same branch that the parent is on, the
757 child should just use the same copy ID that the parent would use.
758 Else, the child needs to generate a new copy ID to use should it
759 need to be made mutable. We will claim that child is on the same
760 branch as its parent if the child itself is not a branch point,
761 or if it is a branch point that we are accessing via its original
762 copy destination path. */
763 SVN_ERR(svn_fs_fs__dag_get_copyroot(©root_rev, ©root_path,
765 SVN_ERR(svn_fs_fs__revision_root(©root_root, fs, copyroot_rev, pool));
766 SVN_ERR(get_dag(©root_node, copyroot_root, copyroot_path, pool));
767 copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
769 if (svn_fs_fs__id_compare(copyroot_id, child_id) == svn_fs_node_unrelated)
772 /* Determine if we are looking at the child via its original path or
773 as a subtree item of a copied tree. */
774 id_path = svn_fs_fs__dag_get_created_path(child->node);
775 if (strcmp(id_path, parent_path_path(child, pool)) == 0)
777 *inherit_p = copy_id_inherit_self;
781 /* We are pretty sure that the child node is an unedited nested
782 branched node. When it needs to be made mutable, it should claim
784 *inherit_p = copy_id_inherit_new;
785 *copy_src_path = id_path;
789 /* Allocate a new parent_path_t node from POOL, referring to NODE,
790 ENTRY, PARENT, and COPY_ID. */
791 static parent_path_t *
792 make_parent_path(dag_node_t *node,
794 parent_path_t *parent,
797 parent_path_t *parent_path = apr_pcalloc(pool, sizeof(*parent_path));
798 parent_path->node = node;
799 parent_path->entry = entry;
800 parent_path->parent = parent;
801 parent_path->copy_inherit = copy_id_inherit_unknown;
802 parent_path->copy_src_path = NULL;
807 /* Flags for open_path. */
808 typedef enum open_path_flags_t {
810 /* The last component of the PATH need not exist. (All parent
811 directories must exist, as usual.) If the last component doesn't
812 exist, simply leave the `node' member of the bottom parent_path
814 open_path_last_optional = 1,
816 /* When this flag is set, don't bother to lookup the DAG node in
817 our caches because we already tried this. Ignoring this flag
818 has no functional impact. */
819 open_path_uncached = 2,
821 /* The caller does not care about the parent node chain but only
822 the final DAG node. */
823 open_path_node_only = 4,
825 /* The caller wants a NULL path object instead of an error if the
826 path cannot be found. */
827 open_path_allow_null = 8
830 /* Try a short-cut for the open_path() function using the last node accessed.
831 * If that ROOT is that nodes's "created rev" and PATH of PATH_LEN chars is
832 * its "created path", return the node in *NODE_P. Set it to NULL otherwise.
834 * This function is used to support ra_serf-style access patterns where we
835 * are first asked for path@rev and then for path@c_rev of the same node.
836 * The shortcut works by ignoring the "rev" part of the cache key and then
837 * checking whether we got lucky. Lookup and verification are both quick
838 * plus there are many early outs for common types of mismatch.
841 try_match_last_node(dag_node_t **node_p,
845 apr_pool_t *scratch_pool)
847 fs_fs_data_t *ffd = root->fs->fsap_data;
849 /* Optimistic lookup: if the last node returned from the cache applied to
850 the same PATH, return it in NODE. */
852 = cache_lookup_last_path(ffd->dag_node_cache, path, path_len);
854 /* Did we get a bucket with a committed node? */
855 if (node && !svn_fs_fs__dag_check_mutable(node))
857 /* Get the path&rev pair at which this node was created.
858 This is repository location for which this node is _known_ to be
859 the right lookup result irrespective of how we found it. */
860 const char *created_path
861 = svn_fs_fs__dag_get_created_path(node);
862 svn_revnum_t revision;
863 SVN_ERR(svn_fs_fs__dag_get_revision(&revision, node, scratch_pool));
865 /* Is it an exact match? */
866 if (revision == root->rev && strcmp(created_path, path) == 0)
868 /* Cache it under its full path@rev access path. */
869 SVN_ERR(dag_node_cache_set(root, path, node, scratch_pool));
881 /* Open the node identified by PATH in ROOT, allocating in POOL. Set
882 *PARENT_PATH_P to a path from the node up to ROOT. The resulting
883 **PARENT_PATH_P value is guaranteed to contain at least one
884 *element, for the root directory. PATH must be in canonical form.
886 If resulting *PARENT_PATH_P will eventually be made mutable and
887 modified, or if copy ID inheritance information is otherwise needed,
888 IS_TXN_PATH must be set. If IS_TXN_PATH is FALSE, no copy ID
889 inheritance information will be calculated for the *PARENT_PATH_P chain.
891 If FLAGS & open_path_last_optional is zero, return the error
892 SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist. If
893 non-zero, require all the parent directories to exist as normal,
894 but if the final path component doesn't exist, simply return a path
895 whose bottom `node' member is zero. This option is useful for
896 callers that create new nodes --- we find the parent directory for
897 them, and tell them whether the entry exists already.
899 The remaining bits in FLAGS are hints that allow this function
900 to take shortcuts based on knowledge that the caller provides,
901 such as the caller is not actually being interested in PARENT_PATH_P,
902 but only in (*PARENT_PATH_P)->NODE.
904 NOTE: Public interfaces which only *read* from the filesystem
905 should not call this function directly, but should instead use
909 open_path(parent_path_t **parent_path_p,
913 svn_boolean_t is_txn_path,
916 svn_fs_t *fs = root->fs;
917 dag_node_t *here = NULL; /* The directory we're currently looking at. */
918 parent_path_t *parent_path; /* The path from HERE up to the root. */
919 const char *rest = NULL; /* The portion of PATH we haven't traversed yet. */
920 apr_pool_t *iterpool = svn_pool_create(pool);
922 /* path to the currently processed entry without trailing '/'.
923 We will reuse this across iterations by simply putting a NUL terminator
924 at the respective position and replacing that with a '/' in the next
925 iteration. This is correct as we assert() PATH to be canonical. */
926 svn_stringbuf_t *path_so_far = svn_stringbuf_create(path, pool);
927 apr_size_t path_len = path_so_far->len;
929 /* Callers often traverse the DAG in some path-based order or along the
930 history segments. That allows us to try a few guesses about where to
931 find the next item. This is only useful if the caller didn't request
932 the full parent chain. */
933 assert(svn_fs__is_canonical_abspath(path));
934 path_so_far->len = 0; /* "" */
935 if (flags & open_path_node_only)
937 const char *directory;
939 /* First attempt: Assume that we access the DAG for the same path as
940 in the last lookup but for a different revision that happens to be
941 the last revision that touched the respective node. This is a
942 common pattern when e.g. checking out over ra_serf. Note that this
943 will only work for committed data as the revision info for nodes in
946 This shortcut is quick and will exit this function upon success.
948 if (!root->is_txn_root)
951 SVN_ERR(try_match_last_node(&node, root, path, path_len, iterpool));
953 /* Did the shortcut work? */
956 /* Construct and return the result. */
957 svn_pool_destroy(iterpool);
959 parent_path = make_parent_path(node, 0, 0, pool);
960 parent_path->copy_inherit = copy_id_inherit_self;
961 *parent_path_p = parent_path;
967 /* Second attempt: Try starting the lookup immediately at the parent
968 node. We will often have recently accessed either a sibling or
969 said parent DIRECTORY itself for the same revision. */
970 directory = svn_dirent_dirname(path, pool);
971 if (directory[1] != 0) /* root nodes are covered anyway */
973 SVN_ERR(dag_node_cache_get(&here, root, directory, pool));
975 /* Did the shortcut work? */
978 apr_size_t dirname_len = strlen(directory);
979 path_so_far->len = dirname_len;
980 rest = path + dirname_len + 1;
985 /* did the shortcut work? */
988 /* Make a parent_path item for the root node, using its own current
990 SVN_ERR(root_node(&here, root, pool));
991 rest = path + 1; /* skip the leading '/', it saves in iteration */
994 path_so_far->data[path_so_far->len] = '\0';
995 parent_path = make_parent_path(here, 0, 0, pool);
996 parent_path->copy_inherit = copy_id_inherit_self;
998 /* Whenever we are at the top of this loop:
999 - HERE is our current directory,
1000 - ID is the node revision ID of HERE,
1001 - REST is the path we're going to find in HERE, and
1002 - PARENT_PATH includes HERE and all its parents. */
1009 svn_pool_clear(iterpool);
1011 /* Parse out the next entry from the path. */
1012 entry = svn_fs__next_entry_name(&next, rest, pool);
1014 /* Update the path traversed thus far. */
1015 path_so_far->data[path_so_far->len] = '/';
1016 path_so_far->len += strlen(entry) + 1;
1017 path_so_far->data[path_so_far->len] = '\0';
1021 /* Given the behavior of svn_fs__next_entry_name(), this
1022 happens when the path either starts or ends with a slash.
1023 In either case, we stay put: the current directory stays
1024 the same, and we add nothing to the parent path. */
1029 copy_id_inherit_t inherit;
1030 const char *copy_path = NULL;
1031 dag_node_t *cached_node = NULL;
1033 /* If we found a directory entry, follow it. First, we
1034 check our node cache, and, failing that, we hit the DAG
1035 layer. Don't bother to contact the cache for the last
1036 element if we already know the lookup to fail for the
1038 if (next || !(flags & open_path_uncached))
1039 SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far->data,
1042 child = cached_node;
1044 SVN_ERR(svn_fs_fs__dag_open(&child, here, entry, pool, iterpool));
1046 /* "file not found" requires special handling. */
1049 /* If this was the last path component, and the caller
1050 said it was optional, then don't return an error;
1051 just put a NULL node pointer in the path. */
1053 if ((flags & open_path_last_optional)
1054 && (! next || *next == '\0'))
1056 parent_path = make_parent_path(NULL, entry, parent_path,
1060 else if (flags & open_path_allow_null)
1067 /* Build a better error message than svn_fs_fs__dag_open
1068 can provide, giving the root and full path name. */
1069 return SVN_FS__NOT_FOUND(root, path);
1073 if (flags & open_path_node_only)
1075 /* Shortcut: the caller only wants the final DAG node. */
1076 parent_path->node = child;
1080 /* Now, make a parent_path item for CHILD. */
1081 parent_path = make_parent_path(child, entry, parent_path, pool);
1084 SVN_ERR(get_copy_inheritance(&inherit, ©_path, fs,
1085 parent_path, iterpool));
1086 parent_path->copy_inherit = inherit;
1087 parent_path->copy_src_path = apr_pstrdup(pool, copy_path);
1091 /* Cache the node we found (if it wasn't already cached). */
1093 SVN_ERR(dag_node_cache_set(root, path_so_far->data, child,
1097 /* Are we finished traversing the path? */
1101 /* The path isn't finished yet; we'd better be in a directory. */
1102 if (svn_fs_fs__dag_node_kind(child) != svn_node_dir)
1103 SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far->data),
1104 apr_psprintf(iterpool, _("Failure opening '%s'"), path));
1110 svn_pool_destroy(iterpool);
1111 *parent_path_p = parent_path;
1112 return SVN_NO_ERROR;
1116 /* Make the node referred to by PARENT_PATH mutable, if it isn't
1117 already, allocating from POOL. ROOT must be the root from which
1118 PARENT_PATH descends. Clone any parent directories as needed.
1119 Adjust the dag nodes in PARENT_PATH to refer to the clones. Use
1120 ERROR_PATH in error messages. */
1121 static svn_error_t *
1122 make_path_mutable(svn_fs_root_t *root,
1123 parent_path_t *parent_path,
1124 const char *error_path,
1128 const svn_fs_fs__id_part_t *txn_id = root_txn_id(root);
1130 /* Is the node mutable already? */
1131 if (svn_fs_fs__dag_check_mutable(parent_path->node))
1132 return SVN_NO_ERROR;
1134 /* Are we trying to clone the root, or somebody's child node? */
1135 if (parent_path->parent)
1137 const svn_fs_id_t *parent_id, *child_id, *copyroot_id;
1138 svn_fs_fs__id_part_t copy_id = { SVN_INVALID_REVNUM, 0 };
1139 svn_fs_fs__id_part_t *copy_id_ptr = ©_id;
1140 copy_id_inherit_t inherit = parent_path->copy_inherit;
1141 const char *clone_path, *copyroot_path;
1142 svn_revnum_t copyroot_rev;
1143 svn_boolean_t is_parent_copyroot = FALSE;
1144 svn_fs_root_t *copyroot_root;
1145 dag_node_t *copyroot_node;
1147 /* We're trying to clone somebody's child. Make sure our parent
1149 SVN_ERR(make_path_mutable(root, parent_path->parent,
1154 case copy_id_inherit_parent:
1155 parent_id = svn_fs_fs__dag_get_id(parent_path->parent->node);
1156 copy_id = *svn_fs_fs__id_copy_id(parent_id);
1159 case copy_id_inherit_new:
1160 SVN_ERR(svn_fs_fs__reserve_copy_id(©_id, root->fs, txn_id,
1164 case copy_id_inherit_self:
1168 case copy_id_inherit_unknown:
1170 SVN_ERR_MALFUNCTION(); /* uh-oh -- somebody didn't calculate copy-ID
1171 inheritance data. */
1174 /* Determine what copyroot our new child node should use. */
1175 SVN_ERR(svn_fs_fs__dag_get_copyroot(©root_rev, ©root_path,
1176 parent_path->node));
1177 SVN_ERR(svn_fs_fs__revision_root(©root_root, root->fs,
1178 copyroot_rev, pool));
1179 SVN_ERR(get_dag(©root_node, copyroot_root, copyroot_path, pool));
1181 child_id = svn_fs_fs__dag_get_id(parent_path->node);
1182 copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
1183 if (!svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(child_id),
1184 svn_fs_fs__id_node_id(copyroot_id)))
1185 is_parent_copyroot = TRUE;
1187 /* Now make this node mutable. */
1188 clone_path = parent_path_path(parent_path->parent, pool);
1189 SVN_ERR(svn_fs_fs__dag_clone_child(&clone,
1190 parent_path->parent->node,
1193 copy_id_ptr, txn_id,
1197 /* Update the path cache. */
1198 SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool),
1203 /* We're trying to clone the root directory. */
1204 SVN_ERR(mutable_root_node(&clone, root, error_path, pool));
1207 /* Update the PARENT_PATH link to refer to the clone. */
1208 parent_path->node = clone;
1210 return SVN_NO_ERROR;
1214 /* Open the node identified by PATH in ROOT. Set DAG_NODE_P to the
1215 node we find, allocated in POOL. Return the error
1216 SVN_ERR_FS_NOT_FOUND if this node doesn't exist. */
1217 static svn_error_t *
1218 get_dag(dag_node_t **dag_node_p,
1219 svn_fs_root_t *root,
1223 parent_path_t *parent_path;
1224 dag_node_t *node = NULL;
1226 /* First we look for the DAG in our cache
1227 (if the path may be canonical). */
1229 SVN_ERR(dag_node_cache_get(&node, root, path, pool));
1233 /* Canonicalize the input PATH. As it turns out, >95% of all paths
1234 * seen here during e.g. svnadmin verify are non-canonical, i.e.
1235 * miss the leading '/'. Unconditional canonicalization has a net
1236 * performance benefit over previously checking path for being
1238 path = svn_fs__canonicalize_abspath(path, pool);
1239 SVN_ERR(dag_node_cache_get(&node, root, path, pool));
1243 /* Call open_path with no flags, as we want this to return an
1244 * error if the node for which we are searching doesn't exist. */
1245 SVN_ERR(open_path(&parent_path, root, path,
1246 open_path_uncached | open_path_node_only,
1248 node = parent_path->node;
1250 /* No need to cache our find -- open_path() will do that for us. */
1255 return SVN_NO_ERROR;
1260 /* Populating the `changes' table. */
1262 /* Add a change to the changes table in FS, keyed on transaction id
1263 TXN_ID, and indicated that a change of kind CHANGE_KIND occurred on
1264 PATH (whose node revision id is--or was, in the case of a
1265 deletion--NODEREV_ID), and optionally that TEXT_MODs, PROP_MODs or
1266 MERGEINFO_MODs occurred. If the change resulted from a copy,
1267 COPYFROM_REV and COPYFROM_PATH specify under which revision and path
1268 the node was copied from. If this was not part of a copy, COPYFROM_REV
1269 should be SVN_INVALID_REVNUM. Do all this as part of POOL. */
1270 static svn_error_t *
1271 add_change(svn_fs_t *fs,
1272 const svn_fs_fs__id_part_t *txn_id,
1274 const svn_fs_id_t *noderev_id,
1275 svn_fs_path_change_kind_t change_kind,
1276 svn_boolean_t text_mod,
1277 svn_boolean_t prop_mod,
1278 svn_boolean_t mergeinfo_mod,
1279 svn_node_kind_t node_kind,
1280 svn_revnum_t copyfrom_rev,
1281 const char *copyfrom_path,
1284 return svn_fs_fs__add_change(fs, txn_id,
1285 svn_fs__canonicalize_abspath(path, pool),
1286 noderev_id, change_kind,
1287 text_mod, prop_mod, mergeinfo_mod,
1288 node_kind, copyfrom_rev, copyfrom_path,
1294 /* Generic node operations. */
1296 /* Get the id of a node referenced by path PATH in ROOT. Return the
1297 id in *ID_P allocated in POOL. */
1299 svn_fs_fs__node_id(const svn_fs_id_t **id_p,
1300 svn_fs_root_t *root,
1304 if ((! root->is_txn_root)
1305 && (path[0] == '\0' || ((path[0] == '/') && (path[1] == '\0'))))
1307 /* Optimize the case where we don't need any db access at all.
1308 The root directory ("" or "/") node is stored in the
1309 svn_fs_root_t object, and never changes when it's a revision
1310 root, so we can just reach in and grab it directly. */
1311 dag_node_t *root_dir = root->fsap_data;
1312 *id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(root_dir), pool);
1318 SVN_ERR(get_dag(&node, root, path, pool));
1319 *id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(node), pool);
1321 return SVN_NO_ERROR;
1324 static svn_error_t *
1325 fs_node_relation(svn_fs_node_relation_t *relation,
1326 svn_fs_root_t *root_a, const char *path_a,
1327 svn_fs_root_t *root_b, const char *path_b,
1331 const svn_fs_id_t *id_a, *id_b;
1332 svn_fs_fs__id_part_t node_id_a, node_id_b;
1334 /* Root paths are a common special case. */
1335 svn_boolean_t a_is_root_dir
1336 = (path_a[0] == '\0') || ((path_a[0] == '/') && (path_a[1] == '\0'));
1337 svn_boolean_t b_is_root_dir
1338 = (path_b[0] == '\0') || ((path_b[0] == '/') && (path_b[1] == '\0'));
1340 /* Another useful thing to know: Both are txns but not the same txn. */
1341 svn_boolean_t different_txn
1342 = root_a->is_txn_root && root_b->is_txn_root
1343 && strcmp(root_a->txn, root_b->txn);
1345 /* Path from different repository are always unrelated. */
1346 if (root_a->fs != root_b->fs)
1348 *relation = svn_fs_node_unrelated;
1349 return SVN_NO_ERROR;
1352 /* Are both (!) root paths? Then, they are related and we only test how
1353 * direct the relation is. */
1354 if (a_is_root_dir && b_is_root_dir)
1356 /* For txn roots, root->REV is the base revision of that TXN. */
1357 *relation = ( (root_a->rev == root_b->rev)
1358 && (root_a->is_txn_root == root_b->is_txn_root)
1360 ? svn_fs_node_unchanged
1361 : svn_fs_node_common_ancestor;
1362 return SVN_NO_ERROR;
1365 /* We checked for all separations between ID spaces (repos, txn).
1366 * Now, we can simply test for the ID values themselves. */
1367 SVN_ERR(get_dag(&node, root_a, path_a, pool));
1368 id_a = svn_fs_fs__dag_get_id(node);
1369 node_id_a = *svn_fs_fs__id_node_id(id_a);
1371 SVN_ERR(get_dag(&node, root_b, path_b, pool));
1372 id_b = svn_fs_fs__dag_get_id(node);
1373 node_id_b = *svn_fs_fs__id_node_id(id_b);
1375 /* Noderevs from different nodes are unrelated. */
1376 if (!svn_fs_fs__id_part_eq(&node_id_a, &node_id_b))
1378 *relation = svn_fs_node_unrelated;
1379 return SVN_NO_ERROR;
1382 /* Noderevs have the same node-ID now. So, they *seem* to be related.
1384 * Special case: Different txns may create the same (txn-local) node ID.
1385 * Only when they are committed can they actually be related to others. */
1386 if (different_txn && node_id_a.revision == SVN_INVALID_REVNUM)
1388 *relation = svn_fs_node_unrelated;
1389 return SVN_NO_ERROR;
1392 /* The noderevs are actually related. Are they the same? */
1393 if (svn_fs_fs__id_eq(id_a, id_b))
1394 *relation = svn_fs_node_unchanged;
1396 *relation = svn_fs_node_common_ancestor;
1398 return SVN_NO_ERROR;
1402 svn_fs_fs__node_created_rev(svn_revnum_t *revision,
1403 svn_fs_root_t *root,
1409 SVN_ERR(get_dag(&node, root, path, pool));
1410 return svn_fs_fs__dag_get_revision(revision, node, pool);
1414 /* Set *CREATED_PATH to the path at which PATH under ROOT was created.
1415 Return a string allocated in POOL. */
1416 static svn_error_t *
1417 fs_node_created_path(const char **created_path,
1418 svn_fs_root_t *root,
1424 SVN_ERR(get_dag(&node, root, path, pool));
1425 *created_path = svn_fs_fs__dag_get_created_path(node);
1427 return SVN_NO_ERROR;
1431 /* Set *KIND_P to the type of node located at PATH under ROOT.
1432 Perform temporary allocations in POOL. */
1433 static svn_error_t *
1434 node_kind(svn_node_kind_t *kind_p,
1435 svn_fs_root_t *root,
1439 const svn_fs_id_t *node_id;
1442 /* Get the node id. */
1443 SVN_ERR(svn_fs_fs__node_id(&node_id, root, path, pool));
1445 /* Use the node id to get the real kind. */
1446 SVN_ERR(svn_fs_fs__dag_get_node(&node, root->fs, node_id, pool));
1447 *kind_p = svn_fs_fs__dag_node_kind(node);
1449 return SVN_NO_ERROR;
1453 /* Set *KIND_P to the type of node present at PATH under ROOT. If
1454 PATH does not exist under ROOT, set *KIND_P to svn_node_none. Use
1455 POOL for temporary allocation. */
1457 svn_fs_fs__check_path(svn_node_kind_t *kind_p,
1458 svn_fs_root_t *root,
1462 svn_error_t *err = node_kind(kind_p, root, path, pool);
1464 ((err->apr_err == SVN_ERR_FS_NOT_FOUND)
1465 || (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY)))
1467 svn_error_clear(err);
1469 *kind_p = svn_node_none;
1472 return svn_error_trace(err);
1475 /* Set *VALUE_P to the value of the property named PROPNAME of PATH in
1476 ROOT. If the node has no property by that name, set *VALUE_P to
1477 zero. Allocate the result in POOL. */
1478 static svn_error_t *
1479 fs_node_prop(svn_string_t **value_p,
1480 svn_fs_root_t *root,
1482 const char *propname,
1486 apr_hash_t *proplist;
1488 SVN_ERR(get_dag(&node, root, path, pool));
1489 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, node, pool));
1492 *value_p = svn_hash_gets(proplist, propname);
1494 return SVN_NO_ERROR;
1498 /* Set *TABLE_P to the entire property list of PATH under ROOT, as an
1499 APR hash table allocated in POOL. The resulting property table
1500 maps property names to pointers to svn_string_t objects containing
1501 the property value. */
1502 static svn_error_t *
1503 fs_node_proplist(apr_hash_t **table_p,
1504 svn_fs_root_t *root,
1511 SVN_ERR(get_dag(&node, root, path, pool));
1512 SVN_ERR(svn_fs_fs__dag_get_proplist(&table, node, pool));
1513 *table_p = table ? table : apr_hash_make(pool);
1515 return SVN_NO_ERROR;
1518 static svn_error_t *
1519 fs_node_has_props(svn_boolean_t *has_props,
1520 svn_fs_root_t *root,
1522 apr_pool_t *scratch_pool)
1526 SVN_ERR(get_dag(&node, root, path, scratch_pool));
1528 return svn_error_trace(svn_fs_fs__dag_has_props(has_props, node,
1532 static svn_error_t *
1533 increment_mergeinfo_up_tree(parent_path_t *pp,
1534 apr_int64_t increment,
1537 for (; pp; pp = pp->parent)
1538 SVN_ERR(svn_fs_fs__dag_increment_mergeinfo_count(pp->node,
1542 return SVN_NO_ERROR;
1545 /* Change, add, or delete a node's property value. The affected node
1546 is PATH under ROOT, the property value to modify is NAME, and VALUE
1547 points to either a string value to set the new contents to, or NULL
1548 if the property should be deleted. Perform temporary allocations
1550 static svn_error_t *
1551 fs_change_node_prop(svn_fs_root_t *root,
1554 const svn_string_t *value,
1557 parent_path_t *parent_path;
1558 apr_hash_t *proplist;
1559 const svn_fs_fs__id_part_t *txn_id;
1560 svn_boolean_t mergeinfo_mod = FALSE;
1562 if (! root->is_txn_root)
1563 return SVN_FS__NOT_TXN(root);
1564 txn_id = root_txn_id(root);
1566 path = svn_fs__canonicalize_abspath(path, pool);
1567 SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, pool));
1569 /* Check (non-recursively) to see if path is locked; if so, check
1570 that we can use it. */
1571 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
1572 SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, FALSE, FALSE,
1575 SVN_ERR(make_path_mutable(root, parent_path, path, pool));
1576 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, parent_path->node, pool));
1578 /* If there's no proplist, but we're just deleting a property, exit now. */
1579 if ((! proplist) && (! value))
1580 return SVN_NO_ERROR;
1582 /* Now, if there's no proplist, we know we need to make one. */
1584 proplist = apr_hash_make(pool);
1586 if (svn_fs_fs__fs_supports_mergeinfo(root->fs)
1587 && strcmp (name, SVN_PROP_MERGEINFO) == 0)
1589 apr_int64_t increment = 0;
1590 svn_boolean_t had_mergeinfo;
1591 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&had_mergeinfo, parent_path->node));
1593 if (value && !had_mergeinfo)
1595 else if (!value && had_mergeinfo)
1600 SVN_ERR(increment_mergeinfo_up_tree(parent_path, increment, pool));
1601 SVN_ERR(svn_fs_fs__dag_set_has_mergeinfo(parent_path->node,
1602 (value != NULL), pool));
1605 mergeinfo_mod = TRUE;
1608 /* Set the property. */
1609 svn_hash_sets(proplist, name, value);
1611 /* Overwrite the node's proplist. */
1612 SVN_ERR(svn_fs_fs__dag_set_proplist(parent_path->node, proplist,
1615 /* Make a record of this modification in the changes table. */
1616 return add_change(root->fs, txn_id, path,
1617 svn_fs_fs__dag_get_id(parent_path->node),
1618 svn_fs_path_change_modify, FALSE, TRUE, mergeinfo_mod,
1619 svn_fs_fs__dag_node_kind(parent_path->node),
1620 SVN_INVALID_REVNUM, NULL, pool);
1624 /* Determine if the properties of two path/root combinations are
1625 different. Set *CHANGED_P to TRUE if the properties at PATH1 under
1626 ROOT1 differ from those at PATH2 under ROOT2, or FALSE otherwise.
1627 Both roots must be in the same filesystem. */
1628 static svn_error_t *
1629 fs_props_changed(svn_boolean_t *changed_p,
1630 svn_fs_root_t *root1,
1632 svn_fs_root_t *root2,
1634 svn_boolean_t strict,
1637 dag_node_t *node1, *node2;
1639 /* Check that roots are in the same fs. */
1640 if (root1->fs != root2->fs)
1641 return svn_error_create
1642 (SVN_ERR_FS_GENERAL, NULL,
1643 _("Cannot compare property value between two different filesystems"));
1645 SVN_ERR(get_dag(&node1, root1, path1, pool));
1646 SVN_ERR(get_dag(&node2, root2, path2, pool));
1647 return svn_fs_fs__dag_things_different(changed_p, NULL,
1648 node1, node2, strict, pool);
1653 /* Merges and commits. */
1655 /* Set *NODE to the root node of ROOT. */
1656 static svn_error_t *
1657 get_root(dag_node_t **node, svn_fs_root_t *root, apr_pool_t *pool)
1659 return get_dag(node, root, "/", pool);
1663 /* Set the contents of CONFLICT_PATH to PATH, and return an
1664 SVN_ERR_FS_CONFLICT error that indicates that there was a conflict
1665 at PATH. Perform all allocations in POOL (except the allocation of
1666 CONFLICT_PATH, which should be handled outside this function). */
1667 static svn_error_t *
1668 conflict_err(svn_stringbuf_t *conflict_path,
1671 svn_stringbuf_set(conflict_path, path);
1672 return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL,
1673 _("Conflict at '%s'"), path);
1676 /* Compare the directory representations at nodes LHS and RHS and set
1677 * *CHANGED to TRUE, if at least one entry has been added or removed them.
1678 * Use POOL for temporary allocations.
1680 static svn_error_t *
1681 compare_dir_structure(svn_boolean_t *changed,
1686 apr_array_header_t *lhs_entries;
1687 apr_array_header_t *rhs_entries;
1690 SVN_ERR(svn_fs_fs__dag_dir_entries(&lhs_entries, lhs, pool));
1691 SVN_ERR(svn_fs_fs__dag_dir_entries(&rhs_entries, rhs, pool));
1693 /* different number of entries -> some addition / removal */
1694 if (lhs_entries->nelts != rhs_entries->nelts)
1697 return SVN_NO_ERROR;
1700 /* Since directories are sorted by name, we can simply compare their
1701 entries one-by-one without binary lookup etc. */
1702 for (i = 0; i < lhs_entries->nelts; ++i)
1704 svn_fs_dirent_t *lhs_entry
1705 = APR_ARRAY_IDX(lhs_entries, i, svn_fs_dirent_t *);
1706 svn_fs_dirent_t *rhs_entry
1707 = APR_ARRAY_IDX(rhs_entries, i, svn_fs_dirent_t *);
1709 if (strcmp(lhs_entry->name, rhs_entry->name)
1710 || !svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(lhs_entry->id),
1711 svn_fs_fs__id_node_id(rhs_entry->id))
1712 || !svn_fs_fs__id_part_eq(svn_fs_fs__id_copy_id(lhs_entry->id),
1713 svn_fs_fs__id_copy_id(rhs_entry->id)))
1716 return SVN_NO_ERROR;
1721 return SVN_NO_ERROR;
1724 /* Merge changes between ANCESTOR and SOURCE into TARGET. ANCESTOR
1725 * and TARGET must be distinct node revisions. TARGET_PATH should
1726 * correspond to TARGET's full path in its filesystem, and is used for
1727 * reporting conflict location.
1729 * SOURCE, TARGET, and ANCESTOR are generally directories; this
1730 * function recursively merges the directories' contents. If any are
1731 * files, this function simply returns an error whenever SOURCE,
1732 * TARGET, and ANCESTOR are all distinct node revisions.
1734 * If there are differences between ANCESTOR and SOURCE that conflict
1735 * with changes between ANCESTOR and TARGET, this function returns an
1736 * SVN_ERR_FS_CONFLICT error, and updates CONFLICT_P to the name of the
1737 * conflicting node in TARGET, with TARGET_PATH prepended as a path.
1739 * If there are no conflicting differences, CONFLICT_P is updated to
1742 * CONFLICT_P must point to a valid svn_stringbuf_t.
1744 * Do any necessary temporary allocation in POOL.
1746 static svn_error_t *
1747 merge(svn_stringbuf_t *conflict_p,
1748 const char *target_path,
1751 dag_node_t *ancestor,
1752 const svn_fs_fs__id_part_t *txn_id,
1753 apr_int64_t *mergeinfo_increment_out,
1756 const svn_fs_id_t *source_id, *target_id, *ancestor_id;
1757 apr_array_header_t *s_entries, *t_entries, *a_entries;
1758 int i, s_idx = -1, t_idx = -1;
1760 apr_pool_t *iterpool;
1761 apr_int64_t mergeinfo_increment = 0;
1762 svn_boolean_t fs_supports_mergeinfo;
1764 /* Make sure everyone comes from the same filesystem. */
1765 fs = svn_fs_fs__dag_get_fs(ancestor);
1766 if ((fs != svn_fs_fs__dag_get_fs(source))
1767 || (fs != svn_fs_fs__dag_get_fs(target)))
1769 return svn_error_create
1770 (SVN_ERR_FS_CORRUPT, NULL,
1771 _("Bad merge; ancestor, source, and target not all in same fs"));
1774 /* We have the same fs, now check it. */
1775 SVN_ERR(svn_fs__check_fs(fs, TRUE));
1777 source_id = svn_fs_fs__dag_get_id(source);
1778 target_id = svn_fs_fs__dag_get_id(target);
1779 ancestor_id = svn_fs_fs__dag_get_id(ancestor);
1781 /* It's improper to call this function with ancestor == target. */
1782 if (svn_fs_fs__id_eq(ancestor_id, target_id))
1784 svn_string_t *id_str = svn_fs_fs__id_unparse(target_id, pool);
1785 return svn_error_createf
1786 (SVN_ERR_FS_GENERAL, NULL,
1787 _("Bad merge; target '%s' has id '%s', same as ancestor"),
1788 target_path, id_str->data);
1791 svn_stringbuf_setempty(conflict_p);
1794 * Either no change made in source, or same change as made in target.
1795 * Both mean nothing to merge here.
1797 if (svn_fs_fs__id_eq(ancestor_id, source_id)
1798 || (svn_fs_fs__id_eq(source_id, target_id)))
1799 return SVN_NO_ERROR;
1801 /* Else proceed, knowing all three are distinct node revisions.
1803 * How to merge from this point:
1805 * if (not all 3 are directories)
1807 * early exit with conflict;
1810 * // Property changes may only be made to up-to-date
1811 * // directories, because once the client commits the prop
1812 * // change, it bumps the directory's revision, and therefore
1813 * // must be able to depend on there being no other changes to
1814 * // that directory in the repository.
1815 * if (target's property list differs from ancestor's)
1818 * For each entry NAME in the directory ANCESTOR:
1820 * Let ANCESTOR-ENTRY, SOURCE-ENTRY, and TARGET-ENTRY be the IDs of
1821 * the name within ANCESTOR, SOURCE, and TARGET respectively.
1822 * (Possibly null if NAME does not exist in SOURCE or TARGET.)
1824 * If ANCESTOR-ENTRY == SOURCE-ENTRY, then:
1825 * No changes were made to this entry while the transaction was in
1826 * progress, so do nothing to the target.
1828 * Else if ANCESTOR-ENTRY == TARGET-ENTRY, then:
1829 * A change was made to this entry while the transaction was in
1830 * process, but the transaction did not touch this entry. Replace
1831 * TARGET-ENTRY with SOURCE-ENTRY.
1834 * Changes were made to this entry both within the transaction and
1835 * to the repository while the transaction was in progress. They
1836 * must be merged or declared to be in conflict.
1838 * If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
1839 * double delete; flag a conflict.
1841 * If any of the three entries is of type file, declare a conflict.
1843 * If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
1844 * modification of ANCESTOR-ENTRY (determine by comparing the
1845 * node-id fields), declare a conflict. A replacement is
1846 * incompatible with a modification or other replacement--even
1847 * an identical replacement.
1849 * Direct modifications were made to the directory ANCESTOR-ENTRY
1850 * in both SOURCE and TARGET. Recursively merge these
1853 * For each leftover entry NAME in the directory SOURCE:
1855 * If NAME exists in TARGET, declare a conflict. Even if SOURCE and
1856 * TARGET are adding exactly the same thing, two additions are not
1857 * auto-mergeable with each other.
1859 * Add NAME to TARGET with the entry from SOURCE.
1861 * Now that we are done merging the changes from SOURCE into the
1862 * directory TARGET, update TARGET's predecessor to be SOURCE.
1865 if ((svn_fs_fs__dag_node_kind(source) != svn_node_dir)
1866 || (svn_fs_fs__dag_node_kind(target) != svn_node_dir)
1867 || (svn_fs_fs__dag_node_kind(ancestor) != svn_node_dir))
1869 return conflict_err(conflict_p, target_path);
1873 /* Possible early merge failure: if target and ancestor have
1874 different property lists, then the merge should fail.
1875 Propchanges can *only* be committed on an up-to-date directory.
1876 ### TODO: see issue #418 about the inelegance of this.
1878 Another possible, similar, early merge failure: if source and
1879 ancestor have different property lists (meaning someone else
1880 changed directory properties while our commit transaction was
1881 happening), the merge should fail. See issue #2751.
1884 node_revision_t *tgt_nr, *anc_nr, *src_nr;
1886 apr_pool_t *scratch_pool;
1888 /* Get node revisions for our id's. */
1889 scratch_pool = svn_pool_create(pool);
1890 SVN_ERR(svn_fs_fs__get_node_revision(&tgt_nr, fs, target_id, pool,
1892 svn_pool_clear(scratch_pool);
1893 SVN_ERR(svn_fs_fs__get_node_revision(&anc_nr, fs, ancestor_id, pool,
1895 svn_pool_clear(scratch_pool);
1896 SVN_ERR(svn_fs_fs__get_node_revision(&src_nr, fs, source_id, pool,
1898 svn_pool_destroy(scratch_pool);
1900 /* Now compare the prop-keys of the skels. Note that just because
1901 the keys are different -doesn't- mean the proplists have
1902 different contents. */
1903 SVN_ERR(svn_fs_fs__prop_rep_equal(&same, fs, src_nr, anc_nr, TRUE, pool));
1905 return conflict_err(conflict_p, target_path);
1907 /* The directory entries got changed in the repository but the directory
1908 properties did not. */
1909 SVN_ERR(svn_fs_fs__prop_rep_equal(&same, fs, tgt_nr, anc_nr, TRUE, pool));
1912 /* There is an incoming prop change for this directory.
1913 We will accept it only if the directory changes were mere updates
1914 to its entries, i.e. there were no additions or removals.
1915 Those could cause update problems to the working copy. */
1916 svn_boolean_t changed;
1917 SVN_ERR(compare_dir_structure(&changed, source, ancestor, pool));
1920 return conflict_err(conflict_p, target_path);
1924 /* ### todo: it would be more efficient to simply check for a NULL
1925 entries hash where necessary below than to allocate an empty hash
1926 here, but another day, another day... */
1927 SVN_ERR(svn_fs_fs__dag_dir_entries(&s_entries, source, pool));
1928 SVN_ERR(svn_fs_fs__dag_dir_entries(&t_entries, target, pool));
1929 SVN_ERR(svn_fs_fs__dag_dir_entries(&a_entries, ancestor, pool));
1931 fs_supports_mergeinfo = svn_fs_fs__fs_supports_mergeinfo(fs);
1933 /* for each entry E in a_entries... */
1934 iterpool = svn_pool_create(pool);
1935 for (i = 0; i < a_entries->nelts; ++i)
1937 svn_fs_dirent_t *s_entry, *t_entry, *a_entry;
1938 svn_pool_clear(iterpool);
1940 a_entry = APR_ARRAY_IDX(a_entries, i, svn_fs_dirent_t *);
1941 s_entry = svn_fs_fs__find_dir_entry(s_entries, a_entry->name, &s_idx);
1942 t_entry = svn_fs_fs__find_dir_entry(t_entries, a_entry->name, &t_idx);
1944 /* No changes were made to this entry while the transaction was
1945 in progress, so do nothing to the target. */
1946 if (s_entry && svn_fs_fs__id_eq(a_entry->id, s_entry->id))
1949 /* A change was made to this entry while the transaction was in
1950 process, but the transaction did not touch this entry. */
1951 else if (t_entry && svn_fs_fs__id_eq(a_entry->id, t_entry->id))
1953 dag_node_t *t_ent_node;
1954 SVN_ERR(svn_fs_fs__dag_get_node(&t_ent_node, fs,
1955 t_entry->id, iterpool));
1956 if (fs_supports_mergeinfo)
1958 apr_int64_t mergeinfo_start;
1959 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start,
1961 mergeinfo_increment -= mergeinfo_start;
1966 dag_node_t *s_ent_node;
1967 SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
1968 s_entry->id, iterpool));
1970 if (fs_supports_mergeinfo)
1972 apr_int64_t mergeinfo_end;
1973 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end,
1975 mergeinfo_increment += mergeinfo_end;
1978 SVN_ERR(svn_fs_fs__dag_set_entry(target, a_entry->name,
1986 SVN_ERR(svn_fs_fs__dag_delete(target, a_entry->name, txn_id,
1991 /* Changes were made to this entry both within the transaction
1992 and to the repository while the transaction was in progress.
1993 They must be merged or declared to be in conflict. */
1996 dag_node_t *s_ent_node, *t_ent_node, *a_ent_node;
1997 const char *new_tpath;
1998 apr_int64_t sub_mergeinfo_increment;
2000 /* If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
2001 double delete; if one of them is null, that's a delete versus
2002 a modification. In any of these cases, flag a conflict. */
2003 if (s_entry == NULL || t_entry == NULL)
2004 return conflict_err(conflict_p,
2005 svn_fspath__join(target_path,
2009 /* If any of the three entries is of type file, flag a conflict. */
2010 if (s_entry->kind == svn_node_file
2011 || t_entry->kind == svn_node_file
2012 || a_entry->kind == svn_node_file)
2013 return conflict_err(conflict_p,
2014 svn_fspath__join(target_path,
2018 /* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
2019 modification of ANCESTOR-ENTRY, declare a conflict. */
2020 if (!svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(s_entry->id),
2021 svn_fs_fs__id_node_id(a_entry->id))
2022 || !svn_fs_fs__id_part_eq(svn_fs_fs__id_copy_id(s_entry->id),
2023 svn_fs_fs__id_copy_id(a_entry->id))
2024 || !svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(t_entry->id),
2025 svn_fs_fs__id_node_id(a_entry->id))
2026 || !svn_fs_fs__id_part_eq(svn_fs_fs__id_copy_id(t_entry->id),
2027 svn_fs_fs__id_copy_id(a_entry->id)))
2028 return conflict_err(conflict_p,
2029 svn_fspath__join(target_path,
2033 /* Direct modifications were made to the directory
2034 ANCESTOR-ENTRY in both SOURCE and TARGET. Recursively
2035 merge these modifications. */
2036 SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
2037 s_entry->id, iterpool));
2038 SVN_ERR(svn_fs_fs__dag_get_node(&t_ent_node, fs,
2039 t_entry->id, iterpool));
2040 SVN_ERR(svn_fs_fs__dag_get_node(&a_ent_node, fs,
2041 a_entry->id, iterpool));
2042 new_tpath = svn_fspath__join(target_path, t_entry->name, iterpool);
2043 SVN_ERR(merge(conflict_p, new_tpath,
2044 t_ent_node, s_ent_node, a_ent_node,
2046 &sub_mergeinfo_increment,
2048 if (fs_supports_mergeinfo)
2049 mergeinfo_increment += sub_mergeinfo_increment;
2053 /* For each entry E in source but not in ancestor */
2054 for (i = 0; i < s_entries->nelts; ++i)
2056 svn_fs_dirent_t *a_entry, *s_entry, *t_entry;
2057 dag_node_t *s_ent_node;
2059 svn_pool_clear(iterpool);
2061 s_entry = APR_ARRAY_IDX(s_entries, i, svn_fs_dirent_t *);
2062 a_entry = svn_fs_fs__find_dir_entry(a_entries, s_entry->name, &s_idx);
2063 t_entry = svn_fs_fs__find_dir_entry(t_entries, s_entry->name, &t_idx);
2065 /* Process only entries in source that are NOT in ancestor. */
2069 /* If NAME exists in TARGET, declare a conflict. */
2071 return conflict_err(conflict_p,
2072 svn_fspath__join(target_path,
2076 SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
2077 s_entry->id, iterpool));
2078 if (fs_supports_mergeinfo)
2080 apr_int64_t mergeinfo_s;
2081 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_s,
2083 mergeinfo_increment += mergeinfo_s;
2086 SVN_ERR(svn_fs_fs__dag_set_entry
2087 (target, s_entry->name, s_entry->id, s_entry->kind,
2090 svn_pool_destroy(iterpool);
2092 SVN_ERR(svn_fs_fs__dag_update_ancestry(target, source, pool));
2094 if (fs_supports_mergeinfo)
2095 SVN_ERR(svn_fs_fs__dag_increment_mergeinfo_count(target,
2096 mergeinfo_increment,
2099 if (mergeinfo_increment_out)
2100 *mergeinfo_increment_out = mergeinfo_increment;
2102 return SVN_NO_ERROR;
2105 /* Merge changes between an ancestor and SOURCE_NODE into
2106 TXN. The ancestor is either ANCESTOR_NODE, or if
2107 that is null, TXN's base node.
2109 If the merge is successful, TXN's base will become
2110 SOURCE_NODE, and its root node will have a new ID, a
2111 successor of SOURCE_NODE.
2113 If a conflict results, update *CONFLICT to the path in the txn that
2114 conflicted; see the CONFLICT_P parameter of merge() for details. */
2115 static svn_error_t *
2116 merge_changes(dag_node_t *ancestor_node,
2117 dag_node_t *source_node,
2119 svn_stringbuf_t *conflict,
2122 dag_node_t *txn_root_node;
2123 svn_fs_t *fs = txn->fs;
2124 const svn_fs_fs__id_part_t *txn_id = svn_fs_fs__txn_get_id(txn);
2126 SVN_ERR(svn_fs_fs__dag_txn_root(&txn_root_node, fs, txn_id, pool));
2128 if (ancestor_node == NULL)
2130 SVN_ERR(svn_fs_fs__dag_txn_base_root(&ancestor_node, fs,
2134 if (svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(ancestor_node),
2135 svn_fs_fs__dag_get_id(txn_root_node)))
2137 /* If no changes have been made in TXN since its current base,
2138 then it can't conflict with any changes since that base.
2139 The caller isn't supposed to call us in that case. */
2140 SVN_ERR_MALFUNCTION();
2143 SVN_ERR(merge(conflict, "/", txn_root_node,
2144 source_node, ancestor_node, txn_id, NULL, pool));
2146 return SVN_NO_ERROR;
2151 svn_fs_fs__commit_txn(const char **conflict_p,
2152 svn_revnum_t *new_rev,
2156 /* How do commits work in Subversion?
2158 * When you're ready to commit, here's what you have:
2160 * 1. A transaction, with a mutable tree hanging off it.
2161 * 2. A base revision, against which TXN_TREE was made.
2162 * 3. A latest revision, which may be newer than the base rev.
2164 * The problem is that if latest != base, then one can't simply
2165 * attach the txn root as the root of the new revision, because that
2166 * would lose all the changes between base and latest. It is also
2167 * not acceptable to insist that base == latest; in a busy
2168 * repository, commits happen too fast to insist that everyone keep
2169 * their entire tree up-to-date at all times. Non-overlapping
2170 * changes should not interfere with each other.
2172 * The solution is to merge the changes between base and latest into
2173 * the txn tree [see the function merge()]. The txn tree is the
2174 * only one of the three trees that is mutable, so it has to be the
2177 * You might have to adjust it more than once, if a new latest
2178 * revision gets committed while you were merging in the previous
2181 * 1. Jane starts txn T, based at revision 6.
2182 * 2. Someone commits (or already committed) revision 7.
2183 * 3. Jane's starts merging the changes between 6 and 7 into T.
2184 * 4. Meanwhile, someone commits revision 8.
2185 * 5. Jane finishes the 6-->7 merge. T could now be committed
2186 * against a latest revision of 7, if only that were still the
2187 * latest. Unfortunately, 8 is now the latest, so...
2188 * 6. Jane starts merging the changes between 7 and 8 into T.
2189 * 7. Meanwhile, no one commits any new revisions. Whew.
2190 * 8. Jane commits T, creating revision 9, whose tree is exactly
2191 * T's tree, except immutable now.
2193 * Lather, rinse, repeat.
2196 svn_error_t *err = SVN_NO_ERROR;
2197 svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool);
2198 svn_fs_t *fs = txn->fs;
2199 fs_fs_data_t *ffd = fs->fsap_data;
2201 /* Limit memory usage when the repository has a high commit rate and
2202 needs to run the following while loop multiple times. The memory
2203 growth without an iteration pool is very noticeable when the
2204 transaction modifies a node that has 20,000 sibling nodes. */
2205 apr_pool_t *iterpool = svn_pool_create(pool);
2207 /* Initialize output params. */
2208 *new_rev = SVN_INVALID_REVNUM;
2214 svn_revnum_t youngish_rev;
2215 svn_fs_root_t *youngish_root;
2216 dag_node_t *youngish_root_node;
2218 svn_pool_clear(iterpool);
2220 /* Get the *current* youngest revision. We call it "youngish"
2221 because new revisions might get committed after we've
2224 SVN_ERR(svn_fs_fs__youngest_rev(&youngish_rev, fs, iterpool));
2225 SVN_ERR(svn_fs_fs__revision_root(&youngish_root, fs, youngish_rev,
2228 /* Get the dag node for the youngest revision. Later we'll use
2229 it as the SOURCE argument to a merge, and if the merge
2230 succeeds, this youngest root node will become the new base
2231 root for the svn txn that was the target of the merge (but
2232 note that the youngest rev may have changed by then -- that's
2233 why we're careful to get this root in its own bdb txn
2235 SVN_ERR(get_root(&youngish_root_node, youngish_root, iterpool));
2237 /* Try to merge. If the merge succeeds, the base root node of
2238 TARGET's txn will become the same as youngish_root_node, so
2239 any future merges will only be between that node and whatever
2240 the root node of the youngest rev is by then. */
2241 err = merge_changes(NULL, youngish_root_node, txn, conflict, iterpool);
2244 if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2245 *conflict_p = conflict->data;
2248 txn->base_rev = youngish_rev;
2250 /* Try to commit. */
2251 err = svn_fs_fs__commit(new_rev, fs, txn, iterpool);
2252 if (err && (err->apr_err == SVN_ERR_FS_TXN_OUT_OF_DATE))
2254 /* Did someone else finish committing a new revision while we
2255 were in mid-merge or mid-commit? If so, we'll need to
2256 loop again to merge the new changes in, then try to
2257 commit again. Or if that's not what happened, then just
2258 return the error. */
2259 svn_revnum_t youngest_rev;
2260 SVN_ERR(svn_fs_fs__youngest_rev(&youngest_rev, fs, iterpool));
2261 if (youngest_rev == youngish_rev)
2264 svn_error_clear(err);
2279 svn_fs_fs__reset_txn_caches(fs);
2281 svn_pool_destroy(iterpool);
2285 if (ffd->pack_after_commit)
2287 SVN_ERR(svn_fs_fs__pack(fs, NULL, NULL, NULL, NULL, pool));
2290 return SVN_NO_ERROR;
2294 /* Merge changes between two nodes into a third node. Given nodes
2295 SOURCE_PATH under SOURCE_ROOT, TARGET_PATH under TARGET_ROOT and
2296 ANCESTOR_PATH under ANCESTOR_ROOT, modify target to contain all the
2297 changes between the ancestor and source. If there are conflicts,
2298 return SVN_ERR_FS_CONFLICT and set *CONFLICT_P to a textual
2299 description of the offending changes. Perform any temporary
2300 allocations in POOL. */
2301 static svn_error_t *
2302 fs_merge(const char **conflict_p,
2303 svn_fs_root_t *source_root,
2304 const char *source_path,
2305 svn_fs_root_t *target_root,
2306 const char *target_path,
2307 svn_fs_root_t *ancestor_root,
2308 const char *ancestor_path,
2311 dag_node_t *source, *ancestor;
2314 svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool);
2316 if (! target_root->is_txn_root)
2317 return SVN_FS__NOT_TXN(target_root);
2320 if ((source_root->fs != ancestor_root->fs)
2321 || (target_root->fs != ancestor_root->fs))
2323 return svn_error_create
2324 (SVN_ERR_FS_CORRUPT, NULL,
2325 _("Bad merge; ancestor, source, and target not all in same fs"));
2328 /* ### kff todo: is there any compelling reason to get the nodes in
2329 one db transaction? Right now we don't; txn_body_get_root() gets
2330 one node at a time. This will probably need to change:
2332 Jim Blandy <jimb@zwingli.cygnus.com> writes:
2333 > svn_fs_merge needs to be a single transaction, to protect it against
2334 > people deleting parents of nodes it's working on, etc.
2337 /* Get the ancestor node. */
2338 SVN_ERR(get_root(&ancestor, ancestor_root, pool));
2340 /* Get the source node. */
2341 SVN_ERR(get_root(&source, source_root, pool));
2343 /* Open a txn for the txn root into which we're merging. */
2344 SVN_ERR(svn_fs_fs__open_txn(&txn, ancestor_root->fs, target_root->txn,
2347 /* Merge changes between ANCESTOR and SOURCE into TXN. */
2348 err = merge_changes(ancestor, source, txn, conflict, pool);
2351 if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2352 *conflict_p = conflict->data;
2353 return svn_error_trace(err);
2356 return SVN_NO_ERROR;
2360 svn_fs_fs__deltify(svn_fs_t *fs,
2361 svn_revnum_t revision,
2364 /* Deltify is a no-op for fs_fs. */
2366 return SVN_NO_ERROR;
2373 /* Set *TABLE_P to a newly allocated APR hash table containing the
2374 entries of the directory at PATH in ROOT. The keys of the table
2375 are entry names, as byte strings, excluding the final null
2376 character; the table's values are pointers to svn_fs_dirent_t
2377 structures. Allocate the table and its contents in POOL. */
2378 static svn_error_t *
2379 fs_dir_entries(apr_hash_t **table_p,
2380 svn_fs_root_t *root,
2385 apr_hash_t *hash = svn_hash__make(pool);
2386 apr_array_header_t *table;
2389 /* Get the entries for this path in the caller's pool. */
2390 SVN_ERR(get_dag(&node, root, path, pool));
2391 SVN_ERR(svn_fs_fs__dag_dir_entries(&table, node, pool));
2393 /* Convert directory array to hash. */
2394 for (i = 0; i < table->nelts; ++i)
2396 svn_fs_dirent_t *entry = APR_ARRAY_IDX(table, i, svn_fs_dirent_t *);
2397 svn_hash_sets(hash, entry->name, entry);
2401 return SVN_NO_ERROR;
2404 static svn_error_t *
2405 fs_dir_optimal_order(apr_array_header_t **ordered_p,
2406 svn_fs_root_t *root,
2407 apr_hash_t *entries,
2408 apr_pool_t *result_pool,
2409 apr_pool_t *scratch_pool)
2411 *ordered_p = svn_fs_fs__order_dir_entries(root->fs, entries, result_pool,
2414 return SVN_NO_ERROR;
2417 /* Raise an error if PATH contains a newline because FSFS cannot handle
2418 * such paths. See issue #4340. */
2419 static svn_error_t *
2420 check_newline(const char *path, apr_pool_t *pool)
2422 char *c = strchr(path, '\n');
2425 return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
2426 _("Invalid control character '0x%02x' in path '%s'"),
2427 (unsigned char)*c, svn_path_illegal_path_escape(path, pool));
2429 return SVN_NO_ERROR;
2432 /* Create a new directory named PATH in ROOT. The new directory has
2433 no entries, and no properties. ROOT must be the root of a
2434 transaction, not a revision. Do any necessary temporary allocation
2436 static svn_error_t *
2437 fs_make_dir(svn_fs_root_t *root,
2441 parent_path_t *parent_path;
2442 dag_node_t *sub_dir;
2443 const svn_fs_fs__id_part_t *txn_id = root_txn_id(root);
2445 SVN_ERR(check_newline(path, pool));
2447 path = svn_fs__canonicalize_abspath(path, pool);
2448 SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
2451 /* Check (recursively) to see if some lock is 'reserving' a path at
2452 that location, or even some child-path; if so, check that we can
2454 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2455 SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, TRUE, FALSE,
2458 /* If there's already a sub-directory by that name, complain. This
2459 also catches the case of trying to make a subdirectory named `/'. */
2460 if (parent_path->node)
2461 return SVN_FS__ALREADY_EXISTS(root, path);
2463 /* Create the subdirectory. */
2464 SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
2465 SVN_ERR(svn_fs_fs__dag_make_dir(&sub_dir,
2466 parent_path->parent->node,
2467 parent_path_path(parent_path->parent,
2473 /* Add this directory to the path cache. */
2474 SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool),
2477 /* Make a record of this modification in the changes table. */
2478 return add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(sub_dir),
2479 svn_fs_path_change_add, FALSE, FALSE, FALSE,
2480 svn_node_dir, SVN_INVALID_REVNUM, NULL, pool);
2484 /* Delete the node at PATH under ROOT. ROOT must be a transaction
2485 root. Perform temporary allocations in POOL. */
2486 static svn_error_t *
2487 fs_delete_node(svn_fs_root_t *root,
2491 parent_path_t *parent_path;
2492 const svn_fs_fs__id_part_t *txn_id;
2493 apr_int64_t mergeinfo_count = 0;
2494 svn_node_kind_t kind;
2496 if (! root->is_txn_root)
2497 return SVN_FS__NOT_TXN(root);
2499 txn_id = root_txn_id(root);
2500 path = svn_fs__canonicalize_abspath(path, pool);
2501 SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, pool));
2502 kind = svn_fs_fs__dag_node_kind(parent_path->node);
2504 /* We can't remove the root of the filesystem. */
2505 if (! parent_path->parent)
2506 return svn_error_create(SVN_ERR_FS_ROOT_DIR, NULL,
2507 _("The root directory cannot be deleted"));
2509 /* Check to see if path (or any child thereof) is locked; if so,
2510 check that we can use the existing lock(s). */
2511 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2512 SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, TRUE, FALSE,
2515 /* Make the parent directory mutable, and do the deletion. */
2516 SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
2517 if (svn_fs_fs__fs_supports_mergeinfo(root->fs))
2518 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_count,
2519 parent_path->node));
2520 SVN_ERR(svn_fs_fs__dag_delete(parent_path->parent->node,
2524 /* Remove this node and any children from the path cache. */
2525 SVN_ERR(dag_node_cache_invalidate(root, parent_path_path(parent_path, pool),
2528 /* Update mergeinfo counts for parents */
2529 if (mergeinfo_count > 0)
2530 SVN_ERR(increment_mergeinfo_up_tree(parent_path->parent,
2534 /* Make a record of this modification in the changes table. */
2535 return add_change(root->fs, txn_id, path,
2536 svn_fs_fs__dag_get_id(parent_path->node),
2537 svn_fs_path_change_delete, FALSE, FALSE, FALSE, kind,
2538 SVN_INVALID_REVNUM, NULL, pool);
2542 /* Set *SAME_P to TRUE if FS1 and FS2 have the same UUID, else set to FALSE.
2543 Use POOL for temporary allocation only.
2544 Note: this code is duplicated between libsvn_fs_fs and libsvn_fs_base. */
2545 static svn_error_t *
2546 fs_same_p(svn_boolean_t *same_p,
2551 *same_p = ! strcmp(fs1->uuid, fs2->uuid);
2552 return SVN_NO_ERROR;
2555 /* Copy the node at FROM_PATH under FROM_ROOT to TO_PATH under
2556 TO_ROOT. If PRESERVE_HISTORY is set, then the copy is recorded in
2557 the copies table. Perform temporary allocations in POOL. */
2558 static svn_error_t *
2559 copy_helper(svn_fs_root_t *from_root,
2560 const char *from_path,
2561 svn_fs_root_t *to_root,
2562 const char *to_path,
2563 svn_boolean_t preserve_history,
2566 dag_node_t *from_node;
2567 parent_path_t *to_parent_path;
2568 const svn_fs_fs__id_part_t *txn_id = root_txn_id(to_root);
2569 svn_boolean_t same_p;
2571 /* Use an error check, not an assert, because even the caller cannot
2572 guarantee that a filesystem's UUID has not changed "on the fly". */
2573 SVN_ERR(fs_same_p(&same_p, from_root->fs, to_root->fs, pool));
2575 return svn_error_createf
2576 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2577 _("Cannot copy between two different filesystems ('%s' and '%s')"),
2578 from_root->fs->path, to_root->fs->path);
2580 /* more things that we can't do ATM */
2581 if (from_root->is_txn_root)
2582 return svn_error_create
2583 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2584 _("Copy from mutable tree not currently supported"));
2586 if (! to_root->is_txn_root)
2587 return svn_error_create
2588 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2589 _("Copy immutable tree not supported"));
2591 /* Get the NODE for FROM_PATH in FROM_ROOT.*/
2592 SVN_ERR(get_dag(&from_node, from_root, from_path, pool));
2594 /* Build up the parent path from TO_PATH in TO_ROOT. If the last
2595 component does not exist, it's not that big a deal. We'll just
2597 SVN_ERR(open_path(&to_parent_path, to_root, to_path,
2598 open_path_last_optional, TRUE, pool));
2600 /* Check to see if path (or any child thereof) is locked; if so,
2601 check that we can use the existing lock(s). */
2602 if (to_root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2603 SVN_ERR(svn_fs_fs__allow_locked_operation(to_path, to_root->fs,
2604 TRUE, FALSE, pool));
2606 /* If the destination node already exists as the same node as the
2607 source (in other words, this operation would result in nothing
2608 happening at all), just do nothing an return successfully,
2609 proud that you saved yourself from a tiresome task. */
2610 if (to_parent_path->node &&
2611 svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(from_node),
2612 svn_fs_fs__dag_get_id(to_parent_path->node)))
2613 return SVN_NO_ERROR;
2615 if (! from_root->is_txn_root)
2617 svn_fs_path_change_kind_t kind;
2618 dag_node_t *new_node;
2619 const char *from_canonpath;
2620 apr_int64_t mergeinfo_start;
2621 apr_int64_t mergeinfo_end;
2623 /* If TO_PATH already existed prior to the copy, note that this
2624 operation is a replacement, not an addition. */
2625 if (to_parent_path->node)
2627 kind = svn_fs_path_change_replace;
2628 if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs))
2629 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start,
2630 to_parent_path->node));
2634 kind = svn_fs_path_change_add;
2635 mergeinfo_start = 0;
2638 if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs))
2639 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end,
2642 /* Make sure the target node's parents are mutable. */
2643 SVN_ERR(make_path_mutable(to_root, to_parent_path->parent,
2646 /* Canonicalize the copyfrom path. */
2647 from_canonpath = svn_fs__canonicalize_abspath(from_path, pool);
2649 SVN_ERR(svn_fs_fs__dag_copy(to_parent_path->parent->node,
2650 to_parent_path->entry,
2657 if (kind != svn_fs_path_change_add)
2658 SVN_ERR(dag_node_cache_invalidate(to_root,
2659 parent_path_path(to_parent_path,
2662 if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs)
2663 && mergeinfo_start != mergeinfo_end)
2664 SVN_ERR(increment_mergeinfo_up_tree(to_parent_path->parent,
2665 mergeinfo_end - mergeinfo_start,
2668 /* Make a record of this modification in the changes table. */
2669 SVN_ERR(get_dag(&new_node, to_root, to_path, pool));
2670 SVN_ERR(add_change(to_root->fs, txn_id, to_path,
2671 svn_fs_fs__dag_get_id(new_node), kind, FALSE,
2672 FALSE, FALSE, svn_fs_fs__dag_node_kind(from_node),
2673 from_root->rev, from_canonpath, pool));
2677 /* See IZ Issue #436 */
2678 /* Copying from transaction roots not currently available.
2680 ### cmpilato todo someday: make this not so. :-) Note that
2681 when copying from mutable trees, you have to make sure that
2682 you aren't creating a cyclic graph filesystem, and a simple
2683 referencing operation won't cut it. Currently, we should not
2684 be able to reach this clause, and the interface reports that
2685 this only works from immutable trees anyway, but JimB has
2686 stated that this requirement need not be necessary in the
2689 SVN_ERR_MALFUNCTION();
2692 return SVN_NO_ERROR;
2696 /* Create a copy of FROM_PATH in FROM_ROOT named TO_PATH in TO_ROOT.
2697 If FROM_PATH is a directory, copy it recursively. Temporary
2698 allocations are from POOL.*/
2699 static svn_error_t *
2700 fs_copy(svn_fs_root_t *from_root,
2701 const char *from_path,
2702 svn_fs_root_t *to_root,
2703 const char *to_path,
2706 SVN_ERR(check_newline(to_path, pool));
2708 return svn_error_trace(copy_helper(from_root,
2709 svn_fs__canonicalize_abspath(from_path,
2712 svn_fs__canonicalize_abspath(to_path,
2718 /* Create a copy of FROM_PATH in FROM_ROOT named TO_PATH in TO_ROOT.
2719 If FROM_PATH is a directory, copy it recursively. No history is
2720 preserved. Temporary allocations are from POOL. */
2721 static svn_error_t *
2722 fs_revision_link(svn_fs_root_t *from_root,
2723 svn_fs_root_t *to_root,
2727 if (! to_root->is_txn_root)
2728 return SVN_FS__NOT_TXN(to_root);
2730 path = svn_fs__canonicalize_abspath(path, pool);
2731 return svn_error_trace(copy_helper(from_root, path, to_root, path,
2736 /* Discover the copy ancestry of PATH under ROOT. Return a relevant
2737 ancestor/revision combination in *PATH_P and *REV_P. Temporary
2738 allocations are in POOL. */
2739 static svn_error_t *
2740 fs_copied_from(svn_revnum_t *rev_p,
2741 const char **path_p,
2742 svn_fs_root_t *root,
2748 /* There is no cached entry, look it up the old-fashioned
2750 SVN_ERR(get_dag(&node, root, path, pool));
2751 SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(rev_p, node));
2752 SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(path_p, node));
2754 return SVN_NO_ERROR;
2761 /* Create the empty file PATH under ROOT. Temporary allocations are
2763 static svn_error_t *
2764 fs_make_file(svn_fs_root_t *root,
2768 parent_path_t *parent_path;
2770 const svn_fs_fs__id_part_t *txn_id = root_txn_id(root);
2772 SVN_ERR(check_newline(path, pool));
2774 path = svn_fs__canonicalize_abspath(path, pool);
2775 SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
2778 /* If there's already a file by that name, complain.
2779 This also catches the case of trying to make a file named `/'. */
2780 if (parent_path->node)
2781 return SVN_FS__ALREADY_EXISTS(root, path);
2783 /* Check (non-recursively) to see if path is locked; if so, check
2784 that we can use it. */
2785 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2786 SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, FALSE, FALSE,
2789 /* Create the file. */
2790 SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
2791 SVN_ERR(svn_fs_fs__dag_make_file(&child,
2792 parent_path->parent->node,
2793 parent_path_path(parent_path->parent,
2799 /* Add this file to the path cache. */
2800 SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool), child,
2803 /* Make a record of this modification in the changes table. */
2804 return add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(child),
2805 svn_fs_path_change_add, TRUE, FALSE, FALSE,
2806 svn_node_file, SVN_INVALID_REVNUM, NULL, pool);
2810 /* Set *LENGTH_P to the size of the file PATH under ROOT. Temporary
2811 allocations are in POOL. */
2812 static svn_error_t *
2813 fs_file_length(svn_filesize_t *length_p,
2814 svn_fs_root_t *root,
2820 /* First create a dag_node_t from the root/path pair. */
2821 SVN_ERR(get_dag(&file, root, path, pool));
2823 /* Now fetch its length */
2824 return svn_fs_fs__dag_file_length(length_p, file, pool);
2828 /* Set *CHECKSUM to the checksum of type KIND for PATH under ROOT, or
2829 NULL if that information isn't available. Temporary allocations
2831 static svn_error_t *
2832 fs_file_checksum(svn_checksum_t **checksum,
2833 svn_checksum_kind_t kind,
2834 svn_fs_root_t *root,
2840 SVN_ERR(get_dag(&file, root, path, pool));
2841 return svn_fs_fs__dag_file_checksum(checksum, file, kind, pool);
2845 /* --- Machinery for svn_fs_file_contents() --- */
2847 /* Set *CONTENTS to a readable stream that will return the contents of
2848 PATH under ROOT. The stream is allocated in POOL. */
2849 static svn_error_t *
2850 fs_file_contents(svn_stream_t **contents,
2851 svn_fs_root_t *root,
2856 svn_stream_t *file_stream;
2858 /* First create a dag_node_t from the root/path pair. */
2859 SVN_ERR(get_dag(&node, root, path, pool));
2861 /* Then create a readable stream from the dag_node_t. */
2862 SVN_ERR(svn_fs_fs__dag_get_contents(&file_stream, node, pool));
2864 *contents = file_stream;
2865 return SVN_NO_ERROR;
2868 /* --- End machinery for svn_fs_file_contents() --- */
2871 /* --- Machinery for svn_fs_try_process_file_contents() --- */
2873 static svn_error_t *
2874 fs_try_process_file_contents(svn_boolean_t *success,
2875 svn_fs_root_t *root,
2877 svn_fs_process_contents_func_t processor,
2882 SVN_ERR(get_dag(&node, root, path, pool));
2884 return svn_fs_fs__dag_try_process_file_contents(success, node,
2885 processor, baton, pool);
2888 /* --- End machinery for svn_fs_try_process_file_contents() --- */
2891 /* --- Machinery for svn_fs_apply_textdelta() --- */
2894 /* Local baton type for all the helper functions below. */
2895 typedef struct txdelta_baton_t
2897 /* This is the custom-built window consumer given to us by the delta
2898 library; it uniquely knows how to read data from our designated
2899 "source" stream, interpret the window, and write data to our
2900 designated "target" stream (in this case, our repos file.) */
2901 svn_txdelta_window_handler_t interpreter;
2902 void *interpreter_baton;
2904 /* The original file info */
2905 svn_fs_root_t *root;
2908 /* Derived from the file info */
2911 svn_stream_t *source_stream;
2912 svn_stream_t *target_stream;
2914 /* MD5 digest for the base text against which a delta is to be
2915 applied, and for the resultant fulltext, respectively. Either or
2916 both may be null, in which case ignored. */
2917 svn_checksum_t *base_checksum;
2918 svn_checksum_t *result_checksum;
2920 /* Pool used by db txns */
2926 /* The main window handler returned by svn_fs_apply_textdelta. */
2927 static svn_error_t *
2928 window_consumer(svn_txdelta_window_t *window, void *baton)
2930 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
2932 /* Send the window right through to the custom window interpreter.
2933 In theory, the interpreter will then write more data to
2934 cb->target_string. */
2935 SVN_ERR(tb->interpreter(window, tb->interpreter_baton));
2937 /* Is the window NULL? If so, we're done. The stream has already been
2938 closed by the interpreter. */
2940 SVN_ERR(svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum,
2943 return SVN_NO_ERROR;
2946 /* Helper function for fs_apply_textdelta. BATON is of type
2948 static svn_error_t *
2949 apply_textdelta(void *baton, apr_pool_t *pool)
2951 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
2952 parent_path_t *parent_path;
2953 const svn_fs_fs__id_part_t *txn_id = root_txn_id(tb->root);
2955 /* Call open_path with no flags, as we want this to return an error
2956 if the node for which we are searching doesn't exist. */
2957 SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, TRUE, pool));
2959 /* Check (non-recursively) to see if path is locked; if so, check
2960 that we can use it. */
2961 if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2962 SVN_ERR(svn_fs_fs__allow_locked_operation(tb->path, tb->root->fs,
2963 FALSE, FALSE, pool));
2965 /* Now, make sure this path is mutable. */
2966 SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, pool));
2967 tb->node = parent_path->node;
2969 if (tb->base_checksum)
2971 svn_checksum_t *checksum;
2973 /* Until we finalize the node, its data_key points to the old
2974 contents, in other words, the base text. */
2975 SVN_ERR(svn_fs_fs__dag_file_checksum(&checksum, tb->node,
2976 tb->base_checksum->kind, pool));
2977 if (!svn_checksum_match(tb->base_checksum, checksum))
2978 return svn_checksum_mismatch_err(tb->base_checksum, checksum, pool,
2979 _("Base checksum mismatch on '%s'"),
2983 /* Make a readable "source" stream out of the current contents of
2984 ROOT/PATH; obviously, this must done in the context of a db_txn.
2985 The stream is returned in tb->source_stream. */
2986 SVN_ERR(svn_fs_fs__dag_get_contents(&(tb->source_stream),
2987 tb->node, tb->pool));
2989 /* Make a writable "target" stream */
2990 SVN_ERR(svn_fs_fs__dag_get_edit_stream(&(tb->target_stream), tb->node,
2993 /* Now, create a custom window handler that uses our two streams. */
2994 svn_txdelta_apply(tb->source_stream,
3000 &(tb->interpreter_baton));
3002 /* Make a record of this modification in the changes table. */
3003 return add_change(tb->root->fs, txn_id, tb->path,
3004 svn_fs_fs__dag_get_id(tb->node),
3005 svn_fs_path_change_modify, TRUE, FALSE, FALSE,
3006 svn_node_file, SVN_INVALID_REVNUM, NULL, pool);
3010 /* Set *CONTENTS_P and *CONTENTS_BATON_P to a window handler and baton
3011 that will accept text delta windows to modify the contents of PATH
3012 under ROOT. Allocations are in POOL. */
3013 static svn_error_t *
3014 fs_apply_textdelta(svn_txdelta_window_handler_t *contents_p,
3015 void **contents_baton_p,
3016 svn_fs_root_t *root,
3018 svn_checksum_t *base_checksum,
3019 svn_checksum_t *result_checksum,
3022 txdelta_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
3025 tb->path = svn_fs__canonicalize_abspath(path, pool);
3027 tb->base_checksum = svn_checksum_dup(base_checksum, pool);
3028 tb->result_checksum = svn_checksum_dup(result_checksum, pool);
3030 SVN_ERR(apply_textdelta(tb, pool));
3032 *contents_p = window_consumer;
3033 *contents_baton_p = tb;
3034 return SVN_NO_ERROR;
3037 /* --- End machinery for svn_fs_apply_textdelta() --- */
3039 /* --- Machinery for svn_fs_apply_text() --- */
3041 /* Baton for svn_fs_apply_text(). */
3044 /* The original file info */
3045 svn_fs_root_t *root;
3048 /* Derived from the file info */
3051 /* The returned stream that will accept the file's new contents. */
3052 svn_stream_t *stream;
3054 /* The actual fs stream that the returned stream will write to. */
3055 svn_stream_t *file_stream;
3057 /* MD5 digest for the final fulltext written to the file. May
3058 be null, in which case ignored. */
3059 svn_checksum_t *result_checksum;
3061 /* Pool used by db txns */
3066 /* A wrapper around svn_fs_fs__dag_finalize_edits, but for
3067 * fulltext data, not text deltas. Closes BATON->file_stream.
3069 * Note: If you're confused about how this function relates to another
3070 * of similar name, think of it this way:
3072 * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits()
3073 * svn_fs_apply_text() ==> ... ==> txn_body_fulltext_finalize_edits()
3076 /* Write function for the publically returned stream. */
3077 static svn_error_t *
3078 text_stream_writer(void *baton,
3082 struct text_baton_t *tb = baton;
3084 /* Psst, here's some data. Pass it on to the -real- file stream. */
3085 return svn_stream_write(tb->file_stream, data, len);
3088 /* Close function for the publically returned stream. */
3089 static svn_error_t *
3090 text_stream_closer(void *baton)
3092 struct text_baton_t *tb = baton;
3094 /* Close the internal-use stream. ### This used to be inside of
3095 txn_body_fulltext_finalize_edits(), but that invoked a nested
3096 Berkeley DB transaction -- scandalous! */
3097 SVN_ERR(svn_stream_close(tb->file_stream));
3099 /* Need to tell fs that we're done sending text */
3100 return svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum,
3105 /* Helper function for fs_apply_text. BATON is of type
3107 static svn_error_t *
3108 apply_text(void *baton, apr_pool_t *pool)
3110 struct text_baton_t *tb = baton;
3111 parent_path_t *parent_path;
3112 const svn_fs_fs__id_part_t *txn_id = root_txn_id(tb->root);
3114 /* Call open_path with no flags, as we want this to return an error
3115 if the node for which we are searching doesn't exist. */
3116 SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, TRUE, pool));
3118 /* Check (non-recursively) to see if path is locked; if so, check
3119 that we can use it. */
3120 if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3121 SVN_ERR(svn_fs_fs__allow_locked_operation(tb->path, tb->root->fs,
3122 FALSE, FALSE, pool));
3124 /* Now, make sure this path is mutable. */
3125 SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, pool));
3126 tb->node = parent_path->node;
3128 /* Make a writable stream for replacing the file's text. */
3129 SVN_ERR(svn_fs_fs__dag_get_edit_stream(&(tb->file_stream), tb->node,
3132 /* Create a 'returnable' stream which writes to the file_stream. */
3133 tb->stream = svn_stream_create(tb, tb->pool);
3134 svn_stream_set_write(tb->stream, text_stream_writer);
3135 svn_stream_set_close(tb->stream, text_stream_closer);
3137 /* Make a record of this modification in the changes table. */
3138 return add_change(tb->root->fs, txn_id, tb->path,
3139 svn_fs_fs__dag_get_id(tb->node),
3140 svn_fs_path_change_modify, TRUE, FALSE, FALSE,
3141 svn_node_file, SVN_INVALID_REVNUM, NULL, pool);
3145 /* Return a writable stream that will set the contents of PATH under
3146 ROOT. RESULT_CHECKSUM is the MD5 checksum of the final result.
3147 Temporary allocations are in POOL. */
3148 static svn_error_t *
3149 fs_apply_text(svn_stream_t **contents_p,
3150 svn_fs_root_t *root,
3152 svn_checksum_t *result_checksum,
3155 struct text_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
3158 tb->path = svn_fs__canonicalize_abspath(path, pool);
3160 tb->result_checksum = svn_checksum_dup(result_checksum, pool);
3162 SVN_ERR(apply_text(tb, pool));
3164 *contents_p = tb->stream;
3165 return SVN_NO_ERROR;
3168 /* --- End machinery for svn_fs_apply_text() --- */
3171 /* Check if the contents of PATH1 under ROOT1 are different from the
3172 contents of PATH2 under ROOT2. If they are different set
3173 *CHANGED_P to TRUE, otherwise set it to FALSE. */
3174 static svn_error_t *
3175 fs_contents_changed(svn_boolean_t *changed_p,
3176 svn_fs_root_t *root1,
3178 svn_fs_root_t *root2,
3180 svn_boolean_t strict,
3183 dag_node_t *node1, *node2;
3185 /* Check that roots are in the same fs. */
3186 if (root1->fs != root2->fs)
3187 return svn_error_create
3188 (SVN_ERR_FS_GENERAL, NULL,
3189 _("Cannot compare file contents between two different filesystems"));
3191 /* Check that both paths are files. */
3193 svn_node_kind_t kind;
3195 SVN_ERR(svn_fs_fs__check_path(&kind, root1, path1, pool));
3196 if (kind != svn_node_file)
3197 return svn_error_createf
3198 (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path1);
3200 SVN_ERR(svn_fs_fs__check_path(&kind, root2, path2, pool));
3201 if (kind != svn_node_file)
3202 return svn_error_createf
3203 (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path2);
3206 SVN_ERR(get_dag(&node1, root1, path1, pool));
3207 SVN_ERR(get_dag(&node2, root2, path2, pool));
3208 return svn_fs_fs__dag_things_different(NULL, changed_p,
3209 node1, node2, strict, pool);
3214 /* Public interface to computing file text deltas. */
3216 static svn_error_t *
3217 fs_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
3218 svn_fs_root_t *source_root,
3219 const char *source_path,
3220 svn_fs_root_t *target_root,
3221 const char *target_path,
3224 dag_node_t *source_node, *target_node;
3226 if (source_root && source_path)
3227 SVN_ERR(get_dag(&source_node, source_root, source_path, pool));
3230 SVN_ERR(get_dag(&target_node, target_root, target_path, pool));
3232 /* Create a delta stream that turns the source into the target. */
3233 return svn_fs_fs__dag_get_file_delta_stream(stream_p, source_node,
3239 /* Finding Changes */
3241 /* Set *CHANGED_PATHS_P to a newly allocated hash containing
3242 descriptions of the paths changed under ROOT. The hash is keyed
3243 with const char * paths and has svn_fs_path_change2_t * values. Use
3244 POOL for all allocations. */
3245 static svn_error_t *
3246 fs_paths_changed(apr_hash_t **changed_paths_p,
3247 svn_fs_root_t *root,
3250 if (root->is_txn_root)
3251 return svn_fs_fs__txn_changes_fetch(changed_paths_p, root->fs,
3252 root_txn_id(root), pool);
3254 return svn_fs_fs__paths_changed(changed_paths_p, root->fs, root->rev,
3260 /* Our coolio opaque history object. */
3261 typedef struct fs_history_data_t
3263 /* filesystem object */
3266 /* path and revision of historical location */
3268 svn_revnum_t revision;
3270 /* internal-use hints about where to resume the history search. */
3271 const char *path_hint;
3272 svn_revnum_t rev_hint;
3274 /* FALSE until the first call to svn_fs_history_prev(). */
3275 svn_boolean_t is_interesting;
3276 } fs_history_data_t;
3278 static svn_fs_history_t *
3279 assemble_history(svn_fs_t *fs,
3281 svn_revnum_t revision,
3282 svn_boolean_t is_interesting,
3283 const char *path_hint,
3284 svn_revnum_t rev_hint,
3288 /* Set *HISTORY_P to an opaque node history object which represents
3289 PATH under ROOT. ROOT must be a revision root. Use POOL for all
3291 static svn_error_t *
3292 fs_node_history(svn_fs_history_t **history_p,
3293 svn_fs_root_t *root,
3295 apr_pool_t *result_pool,
3296 apr_pool_t *scratch_pool)
3298 svn_node_kind_t kind;
3300 /* We require a revision root. */
3301 if (root->is_txn_root)
3302 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
3304 /* And we require that the path exist in the root. */
3305 SVN_ERR(svn_fs_fs__check_path(&kind, root, path, scratch_pool));
3306 if (kind == svn_node_none)
3307 return SVN_FS__NOT_FOUND(root, path);
3309 /* Okay, all seems well. Build our history object and return it. */
3310 *history_p = assemble_history(root->fs, path, root->rev, FALSE, NULL,
3311 SVN_INVALID_REVNUM, result_pool);
3312 return SVN_NO_ERROR;
3315 /* Find the youngest copyroot for path PARENT_PATH or its parents in
3316 filesystem FS, and store the copyroot in *REV_P and *PATH_P.
3317 Perform all allocations in POOL. */
3318 static svn_error_t *
3319 find_youngest_copyroot(svn_revnum_t *rev_p,
3320 const char **path_p,
3322 parent_path_t *parent_path,
3325 svn_revnum_t rev_mine;
3326 svn_revnum_t rev_parent = SVN_INVALID_REVNUM;
3327 const char *path_mine;
3328 const char *path_parent = NULL;
3330 /* First find our parent's youngest copyroot. */
3331 if (parent_path->parent)
3332 SVN_ERR(find_youngest_copyroot(&rev_parent, &path_parent, fs,
3333 parent_path->parent, pool));
3335 /* Find our copyroot. */
3336 SVN_ERR(svn_fs_fs__dag_get_copyroot(&rev_mine, &path_mine,
3337 parent_path->node));
3339 /* If a parent and child were copied to in the same revision, prefer
3340 the child copy target, since it is the copy relevant to the
3341 history of the child. */
3342 if (rev_mine >= rev_parent)
3345 *path_p = path_mine;
3349 *rev_p = rev_parent;
3350 *path_p = path_parent;
3353 return SVN_NO_ERROR;
3357 static svn_error_t *fs_closest_copy(svn_fs_root_t **root_p,
3358 const char **path_p,
3359 svn_fs_root_t *root,
3363 svn_fs_t *fs = root->fs;
3364 parent_path_t *parent_path, *copy_dst_parent_path;
3365 svn_revnum_t copy_dst_rev, created_rev;
3366 const char *copy_dst_path;
3367 svn_fs_root_t *copy_dst_root;
3368 dag_node_t *copy_dst_node;
3370 /* Initialize return values. */
3374 path = svn_fs__canonicalize_abspath(path, pool);
3375 SVN_ERR(open_path(&parent_path, root, path, 0, FALSE, pool));
3377 /* Find the youngest copyroot in the path of this node-rev, which
3378 will indicate the target of the innermost copy affecting the
3380 SVN_ERR(find_youngest_copyroot(©_dst_rev, ©_dst_path,
3381 fs, parent_path, pool));
3382 if (copy_dst_rev == 0) /* There are no copies affecting this node-rev. */
3383 return SVN_NO_ERROR;
3385 /* It is possible that this node was created from scratch at some
3386 revision between COPY_DST_REV and REV. Make sure that PATH
3387 exists as of COPY_DST_REV and is related to this node-rev. */
3388 SVN_ERR(svn_fs_fs__revision_root(©_dst_root, fs, copy_dst_rev, pool));
3389 SVN_ERR(open_path(©_dst_parent_path, copy_dst_root, path,
3390 open_path_node_only | open_path_allow_null, FALSE, pool));
3391 if (copy_dst_parent_path == NULL)
3392 return SVN_NO_ERROR;
3394 copy_dst_node = copy_dst_parent_path->node;
3395 if (! svn_fs_fs__id_check_related(svn_fs_fs__dag_get_id(copy_dst_node),
3396 svn_fs_fs__dag_get_id(parent_path->node)))
3397 return SVN_NO_ERROR;
3399 /* One final check must be done here. If you copy a directory and
3400 create a new entity somewhere beneath that directory in the same
3401 txn, then we can't claim that the copy affected the new entity.
3402 For example, if you do:
3405 create dir2/new-thing
3408 then dir2/new-thing was not affected by the copy of dir1 to dir2.
3409 We detect this situation by asking if PATH@COPY_DST_REV's
3410 created-rev is COPY_DST_REV, and that node-revision has no
3411 predecessors, then there is no relevant closest copy.
3413 SVN_ERR(svn_fs_fs__dag_get_revision(&created_rev, copy_dst_node, pool));
3414 if (created_rev == copy_dst_rev)
3416 const svn_fs_id_t *pred;
3417 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred, copy_dst_node));
3419 return SVN_NO_ERROR;
3422 /* The copy destination checks out. Return it. */
3423 *root_p = copy_dst_root;
3424 *path_p = copy_dst_path;
3425 return SVN_NO_ERROR;
3429 /* Set *PREV_PATH and *PREV_REV to the path and revision which
3430 represent the location at which PATH in FS was located immediately
3431 prior to REVISION iff there was a copy operation (to PATH or one of
3432 its parent directories) between that previous location and
3435 If there was no such copy operation in that portion of PATH's
3436 history, set *PREV_PATH to NULL and *PREV_REV to SVN_INVALID_REVNUM. */
3437 static svn_error_t *
3438 prev_location(const char **prev_path,
3439 svn_revnum_t *prev_rev,
3441 svn_fs_root_t *root,
3445 const char *copy_path, *copy_src_path, *remainder_path;
3446 svn_fs_root_t *copy_root;
3447 svn_revnum_t copy_src_rev;
3449 /* Ask about the most recent copy which affected PATH@REVISION. If
3450 there was no such copy, we're done. */
3451 SVN_ERR(fs_closest_copy(©_root, ©_path, root, path, pool));
3454 *prev_rev = SVN_INVALID_REVNUM;
3456 return SVN_NO_ERROR;
3459 /* Ultimately, it's not the path of the closest copy's source that
3460 we care about -- it's our own path's location in the copy source
3461 revision. So we'll tack the relative path that expresses the
3462 difference between the copy destination and our path in the copy
3463 revision onto the copy source path to determine this information.
3465 In other words, if our path is "/branches/my-branch/foo/bar", and
3466 we know that the closest relevant copy was a copy of "/trunk" to
3467 "/branches/my-branch", then that relative path under the copy
3468 destination is "/foo/bar". Tacking that onto the copy source
3469 path tells us that our path was located at "/trunk/foo/bar"
3472 SVN_ERR(fs_copied_from(©_src_rev, ©_src_path,
3473 copy_root, copy_path, pool));
3474 remainder_path = svn_fspath__skip_ancestor(copy_path, path);
3475 *prev_path = svn_fspath__join(copy_src_path, remainder_path, pool);
3476 *prev_rev = copy_src_rev;
3477 return SVN_NO_ERROR;
3481 static svn_error_t *
3482 fs_node_origin_rev(svn_revnum_t *revision,
3483 svn_fs_root_t *root,
3487 svn_fs_t *fs = root->fs;
3488 const svn_fs_id_t *given_noderev_id, *cached_origin_id;
3489 const svn_fs_fs__id_part_t *node_id;
3491 path = svn_fs__canonicalize_abspath(path, pool);
3493 /* Check the cache first. */
3494 SVN_ERR(svn_fs_fs__node_id(&given_noderev_id, root, path, pool));
3495 node_id = svn_fs_fs__id_node_id(given_noderev_id);
3497 /* Is it a brand new uncommitted node or a new-style node ID?
3498 * (committed old-style nodes will have a 0 revision value;
3499 * rev 0, number 0 is rev 0 root node). Note that != 0 includes
3500 * SVN_INVALID_REVNUM for uncommitted nodes. */
3501 if (node_id->revision != 0 || node_id->number == 0)
3503 *revision = node_id->revision;
3504 return SVN_NO_ERROR;
3507 /* OK, it's an old-style ID? Maybe it's cached. */
3508 SVN_ERR(svn_fs_fs__get_node_origin(&cached_origin_id,
3512 if (cached_origin_id != NULL)
3514 *revision = svn_fs_fs__id_rev(cached_origin_id);
3515 return SVN_NO_ERROR;
3519 /* Ah well, the answer isn't in the ID itself or in the cache.
3520 Let's actually calculate it, then. */
3521 svn_fs_root_t *curroot = root;
3522 apr_pool_t *subpool = svn_pool_create(pool);
3523 apr_pool_t *predidpool = svn_pool_create(pool);
3524 svn_stringbuf_t *lastpath = svn_stringbuf_create(path, pool);
3525 svn_revnum_t lastrev = SVN_INVALID_REVNUM;
3527 const svn_fs_id_t *pred_id;
3529 /* Walk the closest-copy chain back to the first copy in our history.
3531 NOTE: We merely *assume* that this is faster than walking the
3532 predecessor chain, because we *assume* that copies of parent
3533 directories happen less often than modifications to a given item. */
3536 svn_revnum_t currev;
3537 const char *curpath = lastpath->data;
3539 svn_pool_clear(subpool);
3541 /* Get a root pointing to LASTREV. (The first time around,
3542 LASTREV is invalid, but that's cool because CURROOT is
3543 already initialized.) */
3544 if (SVN_IS_VALID_REVNUM(lastrev))
3545 SVN_ERR(svn_fs_fs__revision_root(&curroot, fs, lastrev, subpool));
3547 /* Find the previous location using the closest-copy shortcut. */
3548 SVN_ERR(prev_location(&curpath, &currev, fs, curroot, curpath,
3553 /* Update our LASTPATH and LASTREV variables (which survive
3555 svn_stringbuf_set(lastpath, curpath);
3559 /* Walk the predecessor links back to origin. */
3560 SVN_ERR(svn_fs_fs__node_id(&pred_id, curroot, lastpath->data, predidpool));
3563 svn_pool_clear(subpool);
3564 SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, subpool));
3566 /* Why not just fetch the predecessor ID in PREDIDPOOL?
3567 Because svn_fs_fs__dag_get_predecessor_id() doesn't
3568 necessarily honor the passed-in pool, and might return a
3569 value cached in the node (which is allocated in
3570 SUBPOOL... maybe). */
3571 svn_pool_clear(predidpool);
3572 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
3573 pred_id = pred_id ? svn_fs_fs__id_copy(pred_id, predidpool) : NULL;
3577 /* When we get here, NODE should be the first node-revision in our
3579 SVN_ERR(svn_fs_fs__dag_get_revision(revision, node, pool));
3581 /* Wow, I don't want to have to do all that again. Let's cache
3583 if (node_id->revision != SVN_INVALID_REVNUM)
3584 SVN_ERR(svn_fs_fs__set_node_origin(fs, node_id,
3585 svn_fs_fs__dag_get_id(node), pool));
3587 svn_pool_destroy(subpool);
3588 svn_pool_destroy(predidpool);
3589 return SVN_NO_ERROR;
3594 static svn_error_t *
3595 history_prev(svn_fs_history_t **prev_history,
3596 svn_fs_history_t *history,
3597 svn_boolean_t cross_copies,
3598 apr_pool_t *result_pool,
3599 apr_pool_t *scratch_pool)
3601 fs_history_data_t *fhd = history->fsap_data;
3602 const char *commit_path, *src_path, *path = fhd->path;
3603 svn_revnum_t commit_rev, src_rev, dst_rev;
3604 svn_revnum_t revision = fhd->revision;
3605 svn_fs_t *fs = fhd->fs;
3606 parent_path_t *parent_path;
3608 svn_fs_root_t *root;
3609 svn_boolean_t reported = fhd->is_interesting;
3610 svn_revnum_t copyroot_rev;
3611 const char *copyroot_path;
3613 /* Initialize our return value. */
3614 *prev_history = NULL;
3616 /* If our last history report left us hints about where to pickup
3617 the chase, then our last report was on the destination of a
3618 copy. If we are crossing copies, start from those locations,
3619 otherwise, we're all done here. */
3620 if (fhd->path_hint && SVN_IS_VALID_REVNUM(fhd->rev_hint))
3624 return SVN_NO_ERROR;
3625 path = fhd->path_hint;
3626 revision = fhd->rev_hint;
3629 /* Construct a ROOT for the current revision. */
3630 SVN_ERR(svn_fs_fs__revision_root(&root, fs, revision, scratch_pool));
3632 /* Open PATH/REVISION, and get its node and a bunch of other
3634 SVN_ERR(open_path(&parent_path, root, path, 0, FALSE, scratch_pool));
3635 node = parent_path->node;
3636 commit_path = svn_fs_fs__dag_get_created_path(node);
3637 SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, scratch_pool));
3639 /* The Subversion filesystem is written in such a way that a given
3640 line of history may have at most one interesting history point
3641 per filesystem revision. Either that node was edited (and
3642 possibly copied), or it was copied but not edited. And a copy
3643 source cannot be from the same revision as its destination. So,
3644 if our history revision matches its node's commit revision, we
3646 if (revision == commit_rev)
3650 /* ... we either have not yet reported on this revision (and
3651 need now to do so) ... */
3652 *prev_history = assemble_history(fs, commit_path,
3653 commit_rev, TRUE, NULL,
3654 SVN_INVALID_REVNUM, result_pool);
3655 return SVN_NO_ERROR;
3659 /* ... or we *have* reported on this revision, and must now
3660 progress toward this node's predecessor (unless there is
3661 no predecessor, in which case we're all done!). */
3662 const svn_fs_id_t *pred_id;
3664 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
3666 return SVN_NO_ERROR;
3668 /* Replace NODE and friends with the information from its
3670 SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, scratch_pool));
3671 commit_path = svn_fs_fs__dag_get_created_path(node);
3672 SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, scratch_pool));
3676 /* Find the youngest copyroot in the path of this node, including
3678 SVN_ERR(find_youngest_copyroot(©root_rev, ©root_path, fs,
3679 parent_path, scratch_pool));
3681 /* Initialize some state variables. */
3683 src_rev = SVN_INVALID_REVNUM;
3684 dst_rev = SVN_INVALID_REVNUM;
3686 if (copyroot_rev > commit_rev)
3688 const char *remainder_path;
3689 const char *copy_dst, *copy_src;
3690 svn_fs_root_t *copyroot_root;
3692 SVN_ERR(svn_fs_fs__revision_root(©root_root, fs, copyroot_rev,
3694 SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, scratch_pool));
3695 copy_dst = svn_fs_fs__dag_get_created_path(node);
3697 /* If our current path was the very destination of the copy,
3698 then our new current path will be the copy source. If our
3699 current path was instead the *child* of the destination of
3700 the copy, then figure out its previous location by taking its
3701 path relative to the copy destination and appending that to
3702 the copy source. Finally, if our current path doesn't meet
3703 one of these other criteria ... ### for now just fallback to
3704 the old copy hunt algorithm. */
3705 remainder_path = svn_fspath__skip_ancestor(copy_dst, path);
3709 /* If we get here, then our current path is the destination
3710 of, or the child of the destination of, a copy. Fill
3711 in the return values and get outta here. */
3712 SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(&src_rev, node));
3713 SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(©_src, node));
3715 dst_rev = copyroot_rev;
3716 src_path = svn_fspath__join(copy_src, remainder_path, scratch_pool);
3720 /* If we calculated a copy source path and revision, we'll make a
3721 'copy-style' history object. */
3722 if (src_path && SVN_IS_VALID_REVNUM(src_rev))
3724 svn_boolean_t retry = FALSE;
3726 /* It's possible for us to find a copy location that is the same
3727 as the history point we've just reported. If that happens,
3728 we simply need to take another trip through this history
3730 if ((dst_rev == revision) && reported)
3733 *prev_history = assemble_history(fs, path, dst_rev, ! retry,
3734 src_path, src_rev, result_pool);
3738 *prev_history = assemble_history(fs, commit_path, commit_rev, TRUE,
3739 NULL, SVN_INVALID_REVNUM, result_pool);
3742 return SVN_NO_ERROR;
3746 /* Implement svn_fs_history_prev, set *PREV_HISTORY_P to a new
3747 svn_fs_history_t object that represents the predecessory of
3748 HISTORY. If CROSS_COPIES is true, *PREV_HISTORY_P may be related
3749 only through a copy operation. Perform all allocations in POOL. */
3750 static svn_error_t *
3751 fs_history_prev(svn_fs_history_t **prev_history_p,
3752 svn_fs_history_t *history,
3753 svn_boolean_t cross_copies,
3754 apr_pool_t *result_pool,
3755 apr_pool_t *scratch_pool)
3757 svn_fs_history_t *prev_history = NULL;
3758 fs_history_data_t *fhd = history->fsap_data;
3759 svn_fs_t *fs = fhd->fs;
3761 /* Special case: the root directory changes in every single
3762 revision, no exceptions. And, the root can't be the target (or
3763 child of a target -- duh) of a copy. So, if that's our path,
3764 then we need only decrement our revision by 1, and there you go. */
3765 if (strcmp(fhd->path, "/") == 0)
3767 if (! fhd->is_interesting)
3768 prev_history = assemble_history(fs, "/", fhd->revision,
3769 1, NULL, SVN_INVALID_REVNUM,
3771 else if (fhd->revision > 0)
3772 prev_history = assemble_history(fs, "/", fhd->revision - 1,
3773 1, NULL, SVN_INVALID_REVNUM,
3778 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3779 prev_history = history;
3783 svn_pool_clear(iterpool);
3784 SVN_ERR(history_prev(&prev_history, prev_history, cross_copies,
3785 result_pool, iterpool));
3789 fhd = prev_history->fsap_data;
3790 if (fhd->is_interesting)
3794 svn_pool_destroy(iterpool);
3797 *prev_history_p = prev_history;
3798 return SVN_NO_ERROR;
3802 /* Set *PATH and *REVISION to the path and revision for the HISTORY
3803 object. Use POOL for all allocations. */
3804 static svn_error_t *
3805 fs_history_location(const char **path,
3806 svn_revnum_t *revision,
3807 svn_fs_history_t *history,
3810 fs_history_data_t *fhd = history->fsap_data;
3812 *path = apr_pstrdup(pool, fhd->path);
3813 *revision = fhd->revision;
3814 return SVN_NO_ERROR;
3817 static history_vtable_t history_vtable = {
3822 /* Return a new history object (marked as "interesting") for PATH and
3823 REVISION, allocated in POOL, and with its members set to the values
3824 of the parameters provided. Note that PATH and PATH_HINT get
3825 normalized and duplicated in POOL. */
3826 static svn_fs_history_t *
3827 assemble_history(svn_fs_t *fs,
3829 svn_revnum_t revision,
3830 svn_boolean_t is_interesting,
3831 const char *path_hint,
3832 svn_revnum_t rev_hint,
3835 svn_fs_history_t *history = apr_pcalloc(pool, sizeof(*history));
3836 fs_history_data_t *fhd = apr_pcalloc(pool, sizeof(*fhd));
3837 fhd->path = svn_fs__canonicalize_abspath(path, pool);
3838 fhd->revision = revision;
3839 fhd->is_interesting = is_interesting;
3840 fhd->path_hint = path_hint ? svn_fs__canonicalize_abspath(path_hint, pool)
3842 fhd->rev_hint = rev_hint;
3845 history->vtable = &history_vtable;
3846 history->fsap_data = fhd;
3851 /* mergeinfo queries */
3854 /* DIR_DAG is a directory DAG node which has mergeinfo in its
3855 descendants. This function iterates over its children. For each
3856 child with immediate mergeinfo, it adds its mergeinfo to
3857 RESULT_CATALOG. appropriate arguments. For each child with
3858 descendants with mergeinfo, it recurses. Note that it does *not*
3859 call the action on the path for DIR_DAG itself.
3861 POOL is used for temporary allocations, including the mergeinfo
3862 hashes passed to actions; RESULT_POOL is used for the mergeinfo added
3865 static svn_error_t *
3866 crawl_directory_dag_for_mergeinfo(svn_fs_root_t *root,
3867 const char *this_path,
3868 dag_node_t *dir_dag,
3869 svn_mergeinfo_catalog_t result_catalog,
3870 apr_pool_t *result_pool,
3871 apr_pool_t *scratch_pool)
3873 apr_array_header_t *entries;
3875 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3877 SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, dir_dag, scratch_pool));
3878 for (i = 0; i < entries->nelts; ++i)
3880 svn_fs_dirent_t *dirent = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *);
3881 const char *kid_path;
3882 dag_node_t *kid_dag;
3883 svn_boolean_t has_mergeinfo, go_down;
3885 svn_pool_clear(iterpool);
3887 kid_path = svn_fspath__join(this_path, dirent->name, iterpool);
3888 SVN_ERR(get_dag(&kid_dag, root, kid_path, iterpool));
3890 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, kid_dag));
3891 SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down, kid_dag));
3895 /* Save this particular node's mergeinfo. */
3896 apr_hash_t *proplist;
3897 svn_mergeinfo_t kid_mergeinfo;
3898 svn_string_t *mergeinfo_string;
3901 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, kid_dag, iterpool));
3902 mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
3903 if (!mergeinfo_string)
3905 svn_string_t *idstr = svn_fs_fs__id_unparse(dirent->id, iterpool);
3906 return svn_error_createf
3907 (SVN_ERR_FS_CORRUPT, NULL,
3908 _("Node-revision #'%s' claims to have mergeinfo but doesn't"),
3912 /* Issue #3896: If a node has syntactically invalid mergeinfo, then
3913 treat it as if no mergeinfo is present rather than raising a parse
3915 err = svn_mergeinfo_parse(&kid_mergeinfo,
3916 mergeinfo_string->data,
3920 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
3921 svn_error_clear(err);
3923 return svn_error_trace(err);
3927 svn_hash_sets(result_catalog, apr_pstrdup(result_pool, kid_path),
3933 SVN_ERR(crawl_directory_dag_for_mergeinfo(root,
3941 svn_pool_destroy(iterpool);
3942 return SVN_NO_ERROR;
3945 /* Return the cache key as a combination of REV_ROOT->REV, the inheritance
3946 flags INHERIT and ADJUST_INHERITED_MERGEINFO, and the PATH. The result
3947 will be allocated in POOL..
3950 mergeinfo_cache_key(const char *path,
3951 svn_fs_root_t *rev_root,
3952 svn_mergeinfo_inheritance_t inherit,
3953 svn_boolean_t adjust_inherited_mergeinfo,
3956 apr_int64_t number = rev_root->rev;
3958 + (inherit == svn_mergeinfo_nearest_ancestor ? 2 : 0)
3959 + (adjust_inherited_mergeinfo ? 1 : 0);
3961 return svn_fs_fs__combine_number_and_string(number, path, pool);
3964 /* Calculates the mergeinfo for PATH under REV_ROOT using inheritance
3965 type INHERIT. Returns it in *MERGEINFO, or NULL if there is none.
3966 The result is allocated in RESULT_POOL; SCRATCH_POOL is
3967 used for temporary allocations.
3969 static svn_error_t *
3970 get_mergeinfo_for_path_internal(svn_mergeinfo_t *mergeinfo,
3971 svn_fs_root_t *rev_root,
3973 svn_mergeinfo_inheritance_t inherit,
3974 svn_boolean_t adjust_inherited_mergeinfo,
3975 apr_pool_t *result_pool,
3976 apr_pool_t *scratch_pool)
3978 parent_path_t *parent_path, *nearest_ancestor;
3979 apr_hash_t *proplist;
3980 svn_string_t *mergeinfo_string;
3982 path = svn_fs__canonicalize_abspath(path, scratch_pool);
3984 SVN_ERR(open_path(&parent_path, rev_root, path, 0, FALSE, scratch_pool));
3986 if (inherit == svn_mergeinfo_nearest_ancestor && ! parent_path->parent)
3987 return SVN_NO_ERROR;
3989 if (inherit == svn_mergeinfo_nearest_ancestor)
3990 nearest_ancestor = parent_path->parent;
3992 nearest_ancestor = parent_path;
3996 svn_boolean_t has_mergeinfo;
3998 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo,
3999 nearest_ancestor->node));
4003 /* No need to loop if we're looking for explicit mergeinfo. */
4004 if (inherit == svn_mergeinfo_explicit)
4006 return SVN_NO_ERROR;
4009 nearest_ancestor = nearest_ancestor->parent;
4011 /* Run out? There's no mergeinfo. */
4012 if (!nearest_ancestor)
4014 return SVN_NO_ERROR;
4018 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, nearest_ancestor->node,
4020 mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
4021 if (!mergeinfo_string)
4022 return svn_error_createf
4023 (SVN_ERR_FS_CORRUPT, NULL,
4024 _("Node-revision '%s@%ld' claims to have mergeinfo but doesn't"),
4025 parent_path_path(nearest_ancestor, scratch_pool), rev_root->rev);
4027 /* Parse the mergeinfo; store the result in *MERGEINFO. */
4029 /* Issue #3896: If a node has syntactically invalid mergeinfo, then
4030 treat it as if no mergeinfo is present rather than raising a parse
4032 svn_error_t *err = svn_mergeinfo_parse(mergeinfo,
4033 mergeinfo_string->data,
4037 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
4039 svn_error_clear(err);
4043 return svn_error_trace(err);
4047 /* If our nearest ancestor is the very path we inquired about, we
4048 can return the mergeinfo results directly. Otherwise, we're
4049 inheriting the mergeinfo, so we need to a) remove non-inheritable
4050 ranges and b) telescope the merged-from paths. */
4051 if (adjust_inherited_mergeinfo && (nearest_ancestor != parent_path))
4053 svn_mergeinfo_t tmp_mergeinfo;
4055 SVN_ERR(svn_mergeinfo_inheritable2(&tmp_mergeinfo, *mergeinfo,
4056 NULL, SVN_INVALID_REVNUM,
4057 SVN_INVALID_REVNUM, TRUE,
4058 scratch_pool, scratch_pool));
4059 SVN_ERR(svn_fs__append_to_merged_froms(mergeinfo, tmp_mergeinfo,
4060 parent_path_relpath(
4061 parent_path, nearest_ancestor,
4066 return SVN_NO_ERROR;
4069 /* Caching wrapper around get_mergeinfo_for_path_internal().
4071 static svn_error_t *
4072 get_mergeinfo_for_path(svn_mergeinfo_t *mergeinfo,
4073 svn_fs_root_t *rev_root,
4075 svn_mergeinfo_inheritance_t inherit,
4076 svn_boolean_t adjust_inherited_mergeinfo,
4077 apr_pool_t *result_pool,
4078 apr_pool_t *scratch_pool)
4080 fs_fs_data_t *ffd = rev_root->fs->fsap_data;
4081 const char *cache_key;
4082 svn_boolean_t found = FALSE;
4083 svn_stringbuf_t *mergeinfo_exists;
4087 cache_key = mergeinfo_cache_key(path, rev_root, inherit,
4088 adjust_inherited_mergeinfo, scratch_pool);
4089 if (ffd->mergeinfo_existence_cache)
4091 SVN_ERR(svn_cache__get((void **)&mergeinfo_exists, &found,
4092 ffd->mergeinfo_existence_cache,
4093 cache_key, result_pool));
4094 if (found && mergeinfo_exists->data[0] == '1')
4095 SVN_ERR(svn_cache__get((void **)mergeinfo, &found,
4096 ffd->mergeinfo_cache,
4097 cache_key, result_pool));
4102 SVN_ERR(get_mergeinfo_for_path_internal(mergeinfo, rev_root, path,
4104 adjust_inherited_mergeinfo,
4105 result_pool, scratch_pool));
4106 if (ffd->mergeinfo_existence_cache)
4108 mergeinfo_exists = svn_stringbuf_create(*mergeinfo ? "1" : "0",
4110 SVN_ERR(svn_cache__set(ffd->mergeinfo_existence_cache,
4111 cache_key, mergeinfo_exists, scratch_pool));
4113 SVN_ERR(svn_cache__set(ffd->mergeinfo_cache,
4114 cache_key, *mergeinfo, scratch_pool));
4118 return SVN_NO_ERROR;
4121 /* Adds mergeinfo for each descendant of PATH (but not PATH itself)
4122 under ROOT to RESULT_CATALOG. Returned values are allocated in
4123 RESULT_POOL; temporary values in POOL. */
4124 static svn_error_t *
4125 add_descendant_mergeinfo(svn_mergeinfo_catalog_t result_catalog,
4126 svn_fs_root_t *root,
4128 apr_pool_t *result_pool,
4129 apr_pool_t *scratch_pool)
4131 dag_node_t *this_dag;
4132 svn_boolean_t go_down;
4134 SVN_ERR(get_dag(&this_dag, root, path, scratch_pool));
4135 SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down,
4138 SVN_ERR(crawl_directory_dag_for_mergeinfo(root,
4144 return SVN_NO_ERROR;
4148 /* Get the mergeinfo for a set of paths, returned in
4149 *MERGEINFO_CATALOG. Returned values are allocated in
4150 POOL, while temporary values are allocated in a sub-pool. */
4151 static svn_error_t *
4152 get_mergeinfos_for_paths(svn_fs_root_t *root,
4153 svn_mergeinfo_catalog_t *mergeinfo_catalog,
4154 const apr_array_header_t *paths,
4155 svn_mergeinfo_inheritance_t inherit,
4156 svn_boolean_t include_descendants,
4157 svn_boolean_t adjust_inherited_mergeinfo,
4158 apr_pool_t *result_pool,
4159 apr_pool_t *scratch_pool)
4161 svn_mergeinfo_catalog_t result_catalog = svn_hash__make(result_pool);
4162 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4165 for (i = 0; i < paths->nelts; i++)
4168 svn_mergeinfo_t path_mergeinfo;
4169 const char *path = APR_ARRAY_IDX(paths, i, const char *);
4171 svn_pool_clear(iterpool);
4173 err = get_mergeinfo_for_path(&path_mergeinfo, root, path,
4174 inherit, adjust_inherited_mergeinfo,
4175 result_pool, iterpool);
4178 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
4180 svn_error_clear(err);
4182 path_mergeinfo = NULL;
4186 return svn_error_trace(err);
4191 svn_hash_sets(result_catalog, path, path_mergeinfo);
4192 if (include_descendants)
4193 SVN_ERR(add_descendant_mergeinfo(result_catalog, root, path,
4194 result_pool, scratch_pool));
4196 svn_pool_destroy(iterpool);
4198 *mergeinfo_catalog = result_catalog;
4199 return SVN_NO_ERROR;
4203 /* Implements svn_fs_get_mergeinfo. */
4204 static svn_error_t *
4205 fs_get_mergeinfo(svn_mergeinfo_catalog_t *catalog,
4206 svn_fs_root_t *root,
4207 const apr_array_header_t *paths,
4208 svn_mergeinfo_inheritance_t inherit,
4209 svn_boolean_t include_descendants,
4210 svn_boolean_t adjust_inherited_mergeinfo,
4211 apr_pool_t *result_pool,
4212 apr_pool_t *scratch_pool)
4214 fs_fs_data_t *ffd = root->fs->fsap_data;
4216 /* We require a revision root. */
4217 if (root->is_txn_root)
4218 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
4220 /* We have to actually be able to find the mergeinfo metadata! */
4221 if (! svn_fs_fs__fs_supports_mergeinfo(root->fs))
4222 return svn_error_createf
4223 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
4224 _("Querying mergeinfo requires version %d of the FSFS filesystem "
4225 "schema; filesystem '%s' uses only version %d"),
4226 SVN_FS_FS__MIN_MERGEINFO_FORMAT, root->fs->path, ffd->format);
4228 /* Retrieve a path -> mergeinfo hash mapping. */
4229 return get_mergeinfos_for_paths(root, catalog, paths,
4231 include_descendants,
4232 adjust_inherited_mergeinfo,
4233 result_pool, scratch_pool);
4237 /* The vtable associated with root objects. */
4238 static root_vtable_t root_vtable = {
4240 svn_fs_fs__check_path,
4244 svn_fs_fs__node_created_rev,
4246 fs_node_created_path,
4255 fs_change_node_prop,
4258 fs_dir_optimal_order,
4263 fs_try_process_file_contents,
4267 fs_contents_changed,
4268 fs_get_file_delta_stream,
4273 /* Construct a new root object in FS, allocated from POOL. */
4274 static svn_fs_root_t *
4275 make_root(svn_fs_t *fs,
4278 svn_fs_root_t *root = apr_pcalloc(pool, sizeof(*root));
4282 root->vtable = &root_vtable;
4288 /* Construct a root object referring to the root of REVISION in FS,
4289 whose root directory is ROOT_DIR. Create the new root in POOL. */
4290 static svn_fs_root_t *
4291 make_revision_root(svn_fs_t *fs,
4293 dag_node_t *root_dir,
4296 svn_fs_root_t *root = make_root(fs, pool);
4298 root->is_txn_root = FALSE;
4300 root->fsap_data = root_dir;
4306 /* Construct a root object referring to the root of the transaction
4307 named TXN and based on revision BASE_REV in FS, with FLAGS to
4308 describe transaction's behavior. Create the new root in POOL. */
4309 static svn_error_t *
4310 make_txn_root(svn_fs_root_t **root_p,
4312 const svn_fs_fs__id_part_t *txn,
4313 svn_revnum_t base_rev,
4317 svn_fs_root_t *root = make_root(fs, pool);
4318 fs_txn_root_data_t *frd = apr_pcalloc(root->pool, sizeof(*frd));
4321 root->is_txn_root = TRUE;
4322 root->txn = svn_fs_fs__id_txn_unparse(txn, root->pool);
4323 root->txn_flags = flags;
4324 root->rev = base_rev;
4326 /* Because this cache actually tries to invalidate elements, keep
4327 the number of elements per page down.
4329 Note that since dag_node_cache_invalidate uses svn_cache__iter,
4330 this *cannot* be a memcache-based cache. */
4331 SVN_ERR(svn_cache__create_inprocess(&(frd->txn_node_cache),
4332 svn_fs_fs__dag_serialize,
4333 svn_fs_fs__dag_deserialize,
4334 APR_HASH_KEY_STRING,
4336 apr_pstrcat(pool, txn, ":TXN",
4340 /* Initialize transaction-local caches in FS.
4342 Note that we cannot put those caches in frd because that content
4343 fs root object is not available where we would need it. */
4344 SVN_ERR(svn_fs_fs__initialize_txn_caches(fs, root->txn, root->pool));
4346 root->fsap_data = frd;
4349 return SVN_NO_ERROR;
4356 stringify_node(dag_node_t *node,
4359 /* ### TODO: print some PATH@REV to it, too. */
4360 return svn_fs_fs__id_unparse(svn_fs_fs__dag_get_id(node), pool)->data;
4363 /* Check metadata sanity on NODE, and on its children. Manually verify
4364 information for DAG nodes in revision REV, and trust the metadata
4365 accuracy for nodes belonging to older revisions. To detect cycles,
4366 provide all parent dag_node_t * in PARENT_NODES. */
4367 static svn_error_t *
4368 verify_node(dag_node_t *node,
4370 apr_array_header_t *parent_nodes,
4373 svn_boolean_t has_mergeinfo;
4374 apr_int64_t mergeinfo_count;
4375 const svn_fs_id_t *pred_id;
4376 svn_fs_t *fs = svn_fs_fs__dag_get_fs(node);
4378 svn_node_kind_t kind;
4379 apr_pool_t *iterpool = svn_pool_create(pool);
4382 /* Detect (non-)DAG cycles. */
4383 for (i = 0; i < parent_nodes->nelts; ++i)
4385 dag_node_t *parent = APR_ARRAY_IDX(parent_nodes, i, dag_node_t *);
4386 if (svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(parent),
4387 svn_fs_fs__dag_get_id(node)))
4388 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4389 "Node is its own direct or indirect parent '%s'",
4390 stringify_node(node, iterpool));
4393 /* Fetch some data. */
4394 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, node));
4395 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_count, node));
4396 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
4397 SVN_ERR(svn_fs_fs__dag_get_predecessor_count(&pred_count, node));
4398 kind = svn_fs_fs__dag_node_kind(node);
4401 if (mergeinfo_count < 0)
4402 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4403 "Negative mergeinfo-count %" APR_INT64_T_FMT
4405 mergeinfo_count, stringify_node(node, iterpool));
4407 /* Issue #4129. (This check will explicitly catch non-root instances too.) */
4411 int pred_pred_count;
4412 SVN_ERR(svn_fs_fs__dag_get_node(&pred, fs, pred_id, iterpool));
4413 SVN_ERR(svn_fs_fs__dag_get_predecessor_count(&pred_pred_count, pred));
4414 if (pred_pred_count+1 != pred_count)
4415 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4416 "Predecessor count mismatch: "
4417 "%s has %d, but %s has %d",
4418 stringify_node(node, iterpool), pred_count,
4419 stringify_node(pred, iterpool),
4423 /* Kind-dependent verifications. */
4424 if (kind == svn_node_none)
4426 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4427 "Node '%s' has kind 'none'",
4428 stringify_node(node, iterpool));
4430 if (kind == svn_node_file)
4432 if (has_mergeinfo != mergeinfo_count) /* comparing int to bool */
4433 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4434 "File node '%s' has inconsistent mergeinfo: "
4435 "has_mergeinfo=%d, "
4436 "mergeinfo_count=%" APR_INT64_T_FMT,
4437 stringify_node(node, iterpool),
4438 has_mergeinfo, mergeinfo_count);
4440 if (kind == svn_node_dir)
4442 apr_array_header_t *entries;
4443 apr_int64_t children_mergeinfo = 0;
4444 APR_ARRAY_PUSH(parent_nodes, dag_node_t*) = node;
4446 SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool));
4448 /* Compute CHILDREN_MERGEINFO. */
4449 for (i = 0; i < entries->nelts; ++i)
4451 svn_fs_dirent_t *dirent
4452 = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *);
4454 apr_int64_t child_mergeinfo;
4456 svn_pool_clear(iterpool);
4458 /* Compute CHILD_REV. */
4459 if (svn_fs_fs__id_rev(dirent->id) == rev)
4461 SVN_ERR(svn_fs_fs__dag_get_node(&child, fs, dirent->id,
4463 SVN_ERR(verify_node(child, rev, parent_nodes, iterpool));
4464 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&child_mergeinfo,
4469 /* access mergeinfo counter with minimal overhead */
4470 node_revision_t *noderev;
4471 SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, dirent->id,
4472 iterpool, iterpool));
4473 child_mergeinfo = noderev->mergeinfo_count;
4476 children_mergeinfo += child_mergeinfo;
4479 /* Side-effect of issue #4129. */
4480 if (children_mergeinfo+has_mergeinfo != mergeinfo_count)
4481 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4482 "Mergeinfo-count discrepancy on '%s': "
4483 "expected %" APR_INT64_T_FMT "+%d, "
4484 "counted %" APR_INT64_T_FMT,
4485 stringify_node(node, iterpool),
4486 mergeinfo_count, has_mergeinfo,
4487 children_mergeinfo);
4489 /* If we don't make it here, there was an error / corruption.
4490 * In that case, nobody will need PARENT_NODES anymore. */
4491 apr_array_pop(parent_nodes);
4494 svn_pool_destroy(iterpool);
4495 return SVN_NO_ERROR;
4499 svn_fs_fs__verify_root(svn_fs_root_t *root,
4502 svn_fs_t *fs = root->fs;
4503 dag_node_t *root_dir;
4504 apr_array_header_t *parent_nodes;
4506 /* Issue #4129: bogus pred-counts and minfo-cnt's on the root node-rev
4507 (and elsewhere). This code makes more thorough checks than the
4508 commit-time checks in validate_root_noderev(). */
4510 /* Callers should disable caches by setting SVN_FS_CONFIG_FSFS_CACHE_NS;
4513 When this code is called in the library, we want to ensure we
4514 use the on-disk data --- rather than some data that was read
4515 in the possibly-distance past and cached since. */
4517 if (root->is_txn_root)
4519 fs_txn_root_data_t *frd = root->fsap_data;
4520 SVN_ERR(svn_fs_fs__dag_txn_root(&root_dir, fs, &frd->txn_id, pool));
4524 root_dir = root->fsap_data;
4527 /* Recursively verify ROOT_DIR. */
4528 parent_nodes = apr_array_make(pool, 16, sizeof(dag_node_t *));
4529 SVN_ERR(verify_node(root_dir, root->rev, parent_nodes, pool));
4531 /* Verify explicitly the predecessor of the root. */
4533 const svn_fs_id_t *pred_id;
4535 /* Only r0 should have no predecessor. */
4536 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, root_dir));
4537 if (! root->is_txn_root && !!pred_id != !!root->rev)
4538 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4539 "r%ld's root node's predecessor is "
4540 "unexpectedly '%s'",
4543 ? svn_fs_fs__id_unparse(pred_id, pool)->data
4545 if (root->is_txn_root && !pred_id)
4546 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4547 "Transaction '%s''s root node's predecessor is "
4548 "unexpectedly NULL",
4551 /* Check the predecessor's revision. */
4554 svn_revnum_t pred_rev = svn_fs_fs__id_rev(pred_id);
4555 if (! root->is_txn_root && pred_rev+1 != root->rev)
4557 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4558 "r%ld's root node's predecessor is r%ld"
4559 " but should be r%ld",
4560 root->rev, pred_rev, root->rev - 1);
4561 if (root->is_txn_root && pred_rev != root->rev)
4562 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4563 "Transaction '%s''s root node's predecessor"
4565 " but should be r%ld",
4566 root->txn, pred_rev, root->rev);
4570 return SVN_NO_ERROR;