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 /* Returns a 32 bit hash value for the given REVISION and PATH of exactly
216 hash_func(svn_revnum_t revision,
221 apr_uint32_t hash_value = (apr_uint32_t)revision;
223 #if SVN_UNALIGNED_ACCESS_IS_OK
224 /* "randomizing" / distributing factor used in our hash function */
225 const apr_uint32_t factor = 0xd1f3da69;
228 /* Calculate the hash value
229 (HASH_VALUE has been initialized to REVISION).
231 Note that the actual hash function is arbitrary as long as its result
232 in HASH_VALUE only depends on REVISION and *PATH. However, we try to
233 make as much of *PATH influence the result as possible to get an "even"
234 spread across the hash buckets (maximizes our cache retention rate and
237 When chunked access is possible (independent of the PATH pointer's
238 value!), we read 4 bytes at once and multiply the hash value with a
239 FACTOR that mirror / pattern / shift all 4 input bytes to various bits
240 of the result. The final result will be taken from the MSBs.
242 When chunked access is not possible (not supported by CPU or odd bytes
243 at the end of *PATH), we use the simple traditional "* 33" hash
244 function that works very well with texts / paths and that e.g. APR uses.
246 Please note that the bytewise and the chunked calculation are *NOT*
247 interchangeable as they will yield different results for the same input.
248 For any given machine and *PATH, we must use a fixed combination of the
252 #if SVN_UNALIGNED_ACCESS_IS_OK
253 /* We relax the dependency chain between iterations by processing
254 two chunks from the input per hash_value self-multiplication.
255 The HASH_VALUE update latency is now 1 MUL latency + 1 ADD latency
256 per 2 chunks instead of 1 chunk.
258 for (; i + 8 <= path_len; i += 8)
259 hash_value = hash_value * factor * factor
260 + ( *(const apr_uint32_t*)(path + i) * factor
261 + *(const apr_uint32_t*)(path + i + 4));
264 for (; i < path_len; ++i)
265 /* Help GCC to minimize the HASH_VALUE update latency by splitting the
266 MUL 33 of the naive implementation: h = h * 33 + path[i]. This
267 shortens the dependency chain from 1 shift + 2 ADDs to 1 shift + 1 ADD.
269 hash_value = hash_value * 32 + (hash_value + (unsigned char)path[i]);
274 /* For the given REVISION and PATH, return the respective node found in
275 * CACHE. If there is none, return NULL.
278 cache_lookup( fs_fs_dag_cache_t *cache
279 , svn_revnum_t revision
282 apr_size_t bucket_index;
283 apr_size_t path_len = strlen(path);
284 apr_uint32_t hash_value;
286 /* optimistic lookup: hit the same bucket again? */
287 cache_entry_t *result = &cache->buckets[cache->last_hit];
288 if ( (result->revision == revision)
289 && (result->path_len == path_len)
290 && !memcmp(result->path, path, path_len))
292 /* Remember the position of the last node we found in this cache. */
294 cache->last_non_empty = cache->last_hit;
299 /* need to do a full lookup. */
300 hash_value = hash_func(revision, path, path_len);
302 bucket_index = hash_value + (hash_value >> 16);
303 bucket_index = (bucket_index + (bucket_index >> 8)) % BUCKET_COUNT;
305 /* access the corresponding bucket and remember its location */
306 result = &cache->buckets[bucket_index];
307 cache->last_hit = bucket_index;
309 /* if it is *NOT* a match, clear the bucket, expect the caller to fill
310 in the node and count it as an insertion */
311 if ( (result->hash_value != hash_value)
312 || (result->revision != revision)
313 || (result->path_len != path_len)
314 || memcmp(result->path, path, path_len))
318 else if (result->node)
320 /* This bucket is valid & has a suitable DAG node in it.
321 Remember its location. */
322 cache->last_non_empty = bucket_index;
328 /* Store a copy of NODE in CACHE, taking REVISION and PATH as key.
329 * This function will clean the cache at regular intervals.
332 cache_insert(fs_fs_dag_cache_t *cache,
333 svn_revnum_t revision,
337 apr_size_t bucket_index;
338 apr_size_t path_len = strlen(path);
339 apr_uint32_t hash_value;
340 cache_entry_t *entry;
342 auto_clear_dag_cache(cache);
344 /* calculate the bucket index to use */
345 hash_value = hash_func(revision, path, path_len);
347 bucket_index = hash_value + (hash_value >> 16);
348 bucket_index = (bucket_index + (bucket_index >> 8)) % BUCKET_COUNT;
350 /* access the corresponding bucket and remember its location */
351 entry = &cache->buckets[bucket_index];
352 cache->last_hit = bucket_index;
354 /* if it is *NOT* a match, clear the bucket, expect the caller to fill
355 in the node and count it as an insertion */
356 entry->hash_value = hash_value;
357 entry->revision = revision;
358 if (entry->path_len < path_len)
359 entry->path = apr_palloc(cache->pool, path_len + 1);
360 entry->path_len = path_len;
361 memcpy(entry->path, path, path_len + 1);
363 entry->node = svn_fs_fs__dag_dup(node, cache->pool);
367 /* Optimistic lookup using the last seen non-empty location in CACHE.
368 Return the node of that entry, if it is still in use and matches PATH.
369 Return NULL otherwise. Since the caller usually already knows the path
370 length, provide it in PATH_LEN. */
372 cache_lookup_last_path(fs_fs_dag_cache_t *cache,
376 cache_entry_t *result = &cache->buckets[cache->last_non_empty];
377 assert(strlen(path) == path_len);
380 && (result->path_len == path_len)
381 && !memcmp(result->path, path, path_len))
389 /* 2nd level cache */
391 /* Find and return the DAG node cache for ROOT and the key that
392 should be used for PATH.
394 Pool will only be used for allocating a new keys if necessary */
396 locate_cache(svn_cache__t **cache,
402 if (root->is_txn_root)
404 fs_txn_root_data_t *frd = root->fsap_data;
407 *cache = frd->txn_node_cache;
413 fs_fs_data_t *ffd = root->fs->fsap_data;
416 *cache = ffd->rev_node_cache;
418 *key = svn_fs_fs__combine_number_and_string(root->rev, path, pool);
422 /* In *NODE_P, return the DAG node for PATH from ROOT's node cache, or NULL
423 if the node isn't cached. *NODE_P is allocated in POOL. */
425 dag_node_cache_get(dag_node_t **node_p,
431 dag_node_t *node = NULL;
435 SVN_ERR_ASSERT(*path == '/');
437 if (!root->is_txn_root)
439 /* immutable DAG node. use the global caches for it */
441 fs_fs_data_t *ffd = root->fs->fsap_data;
443 node = cache_lookup(ffd->dag_node_cache, root->rev, path);
446 locate_cache(&cache, &key, root, path, pool);
447 SVN_ERR(svn_cache__get((void **)&node, &found, cache, key, pool));
450 /* Patch up the FS, since this might have come from an old FS
452 svn_fs_fs__dag_set_fs(node, root->fs);
454 /* Retain the DAG node in L1 cache. */
455 cache_insert(ffd->dag_node_cache, root->rev, path, node);
460 /* Copy the node from L1 cache into the passed-in POOL. */
461 node = svn_fs_fs__dag_dup(node, pool);
466 /* DAG is mutable / may become invalid. Use the TXN-local cache */
468 locate_cache(&cache, &key, root, path, pool);
470 SVN_ERR(svn_cache__get((void **) &node, &found, cache, key, pool));
473 /* Patch up the FS, since this might have come from an old FS
475 svn_fs_fs__dag_set_fs(node, root->fs);
485 /* Add the NODE for PATH to ROOT's node cache. */
487 dag_node_cache_set(svn_fs_root_t *root,
495 SVN_ERR_ASSERT(*path == '/');
497 locate_cache(&cache, &key, root, path, pool);
498 return svn_cache__set(cache, key, node, pool);
502 /* Baton for find_descendants_in_cache. */
505 apr_array_header_t *list;
509 /* If the given item is a descendant of BATON->PATH, push
510 * it onto BATON->LIST (copying into BATON->POOL). Implements
511 * the svn_iter_apr_hash_cb_t prototype. */
513 find_descendants_in_cache(void *baton,
519 struct fdic_baton *b = baton;
520 const char *item_path = key;
522 if (svn_fspath__skip_ancestor(b->path, item_path))
523 APR_ARRAY_PUSH(b->list, const char *) = apr_pstrdup(b->pool, item_path);
528 /* Invalidate cache entries for PATH and any of its children. This
529 should *only* be called on a transaction root! */
531 dag_node_cache_invalidate(svn_fs_root_t *root,
537 apr_pool_t *iterpool;
541 b.pool = svn_pool_create(pool);
542 b.list = apr_array_make(b.pool, 1, sizeof(const char *));
544 SVN_ERR_ASSERT(root->is_txn_root);
545 locate_cache(&cache, NULL, root, NULL, b.pool);
548 SVN_ERR(svn_cache__iter(NULL, cache, find_descendants_in_cache,
551 iterpool = svn_pool_create(b.pool);
553 for (i = 0; i < b.list->nelts; i++)
555 const char *descendant = APR_ARRAY_IDX(b.list, i, const char *);
556 svn_pool_clear(iterpool);
557 SVN_ERR(svn_cache__set(cache, descendant, NULL, iterpool));
560 svn_pool_destroy(iterpool);
561 svn_pool_destroy(b.pool);
567 /* Creating transaction and revision root nodes. */
570 svn_fs_fs__txn_root(svn_fs_root_t **root_p,
574 apr_uint32_t flags = 0;
575 apr_hash_t *txnprops;
577 /* Look for the temporary txn props representing 'flags'. */
578 SVN_ERR(svn_fs_fs__txn_proplist(&txnprops, txn, pool));
581 if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD))
582 flags |= SVN_FS_TXN_CHECK_OOD;
584 if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS))
585 flags |= SVN_FS_TXN_CHECK_LOCKS;
588 return make_txn_root(root_p, txn->fs, svn_fs_fs__txn_get_id(txn),
589 txn->base_rev, flags, pool);
594 svn_fs_fs__revision_root(svn_fs_root_t **root_p,
599 dag_node_t *root_dir;
601 SVN_ERR(svn_fs__check_fs(fs, TRUE));
603 SVN_ERR(svn_fs_fs__dag_revision_root(&root_dir, fs, rev, pool));
605 *root_p = make_revision_root(fs, rev, root_dir, pool);
612 /* Getting dag nodes for roots. */
614 /* Return the transaction ID to a given transaction ROOT. */
615 static const svn_fs_fs__id_part_t *
616 root_txn_id(svn_fs_root_t *root)
618 fs_txn_root_data_t *frd = root->fsap_data;
619 assert(root->is_txn_root);
624 /* Set *NODE_P to a freshly opened dag node referring to the root
625 directory of ROOT, allocating from POOL. */
627 root_node(dag_node_t **node_p,
631 if (root->is_txn_root)
633 /* It's a transaction root. Open a fresh copy. */
634 return svn_fs_fs__dag_txn_root(node_p, root->fs, root_txn_id(root),
639 /* It's a revision root, so we already have its root directory
641 dag_node_t *root_dir = root->fsap_data;
642 *node_p = svn_fs_fs__dag_dup(root_dir, pool);
648 /* Set *NODE_P to a mutable root directory for ROOT, cloning if
649 necessary, allocating in POOL. ROOT must be a transaction root.
650 Use ERROR_PATH in error messages. */
652 mutable_root_node(dag_node_t **node_p,
654 const char *error_path,
657 if (root->is_txn_root)
659 /* It's a transaction root. Open a fresh copy. */
660 return svn_fs_fs__dag_clone_root(node_p, root->fs, root_txn_id(root),
664 /* If it's not a transaction root, we can't change its contents. */
665 return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path);
670 /* Traversing directory paths. */
672 typedef enum copy_id_inherit_t
674 copy_id_inherit_unknown = 0,
675 copy_id_inherit_self,
676 copy_id_inherit_parent,
681 /* A linked list representing the path from a node up to a root
682 directory. We use this for cloning, and for operations that need
683 to deal with both a node and its parent directory. For example, a
684 `delete' operation needs to know that the node actually exists, but
685 also needs to change the parent directory. */
686 typedef struct parent_path_t
689 /* A node along the path. This could be the final node, one of its
690 parents, or the root. Every parent path ends with an element for
691 the root directory. */
694 /* The name NODE has in its parent directory. This is zero for the
695 root directory, which (obviously) has no name in its parent. */
698 /* The parent of NODE, or zero if NODE is the root directory. */
699 struct parent_path_t *parent;
701 /* The copy ID inheritance style. */
702 copy_id_inherit_t copy_inherit;
704 /* If copy ID inheritance style is copy_id_inherit_new, this is the
705 path which should be implicitly copied; otherwise, this is NULL. */
706 const char *copy_src_path;
710 /* Return a text string describing the absolute path of parent_path
711 PARENT_PATH. It will be allocated in POOL. */
713 parent_path_path(parent_path_t *parent_path,
716 const char *path_so_far = "/";
717 if (parent_path->parent)
718 path_so_far = parent_path_path(parent_path->parent, pool);
719 return parent_path->entry
720 ? svn_fspath__join(path_so_far, parent_path->entry, pool)
725 /* Return the FS path for the parent path chain object CHILD relative
726 to its ANCESTOR in the same chain, allocated in POOL. */
728 parent_path_relpath(parent_path_t *child,
729 parent_path_t *ancestor,
732 const char *path_so_far = "";
733 parent_path_t *this_node = child;
734 while (this_node != ancestor)
736 assert(this_node != NULL);
737 path_so_far = svn_relpath_join(this_node->entry, path_so_far, pool);
738 this_node = this_node->parent;
745 /* Choose a copy ID inheritance method *INHERIT_P to be used in the
746 event that immutable node CHILD in FS needs to be made mutable. If
747 the inheritance method is copy_id_inherit_new, also return a
748 *COPY_SRC_PATH on which to base the new copy ID (else return NULL
749 for that path). CHILD must have a parent (it cannot be the root
750 node). Allocations are taken from POOL. */
752 get_copy_inheritance(copy_id_inherit_t *inherit_p,
753 const char **copy_src_path,
755 parent_path_t *child,
758 const svn_fs_id_t *child_id, *parent_id, *copyroot_id;
759 const svn_fs_fs__id_part_t *child_copy_id, *parent_copy_id;
760 const char *id_path = NULL;
761 svn_fs_root_t *copyroot_root;
762 dag_node_t *copyroot_node;
763 svn_revnum_t copyroot_rev;
764 const char *copyroot_path;
766 SVN_ERR_ASSERT(child && child->parent);
768 /* Initialize some convenience variables. */
769 child_id = svn_fs_fs__dag_get_id(child->node);
770 parent_id = svn_fs_fs__dag_get_id(child->parent->node);
771 child_copy_id = svn_fs_fs__id_copy_id(child_id);
772 parent_copy_id = svn_fs_fs__id_copy_id(parent_id);
774 /* If this child is already mutable, we have nothing to do. */
775 if (svn_fs_fs__id_is_txn(child_id))
777 *inherit_p = copy_id_inherit_self;
778 *copy_src_path = NULL;
782 /* From this point on, we'll assume that the child will just take
783 its copy ID from its parent. */
784 *inherit_p = copy_id_inherit_parent;
785 *copy_src_path = NULL;
787 /* Special case: if the child's copy ID is '0', use the parent's
789 if (svn_fs_fs__id_part_is_root(child_copy_id))
792 /* Compare the copy IDs of the child and its parent. If they are
793 the same, then the child is already on the same branch as the
794 parent, and should use the same mutability copy ID that the
796 if (svn_fs_fs__id_part_eq(child_copy_id, parent_copy_id))
799 /* If the child is on the same branch that the parent is on, the
800 child should just use the same copy ID that the parent would use.
801 Else, the child needs to generate a new copy ID to use should it
802 need to be made mutable. We will claim that child is on the same
803 branch as its parent if the child itself is not a branch point,
804 or if it is a branch point that we are accessing via its original
805 copy destination path. */
806 SVN_ERR(svn_fs_fs__dag_get_copyroot(©root_rev, ©root_path,
808 SVN_ERR(svn_fs_fs__revision_root(©root_root, fs, copyroot_rev, pool));
809 SVN_ERR(get_dag(©root_node, copyroot_root, copyroot_path, pool));
810 copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
812 if (svn_fs_fs__id_compare(copyroot_id, child_id) == svn_fs_node_unrelated)
815 /* Determine if we are looking at the child via its original path or
816 as a subtree item of a copied tree. */
817 id_path = svn_fs_fs__dag_get_created_path(child->node);
818 if (strcmp(id_path, parent_path_path(child, pool)) == 0)
820 *inherit_p = copy_id_inherit_self;
824 /* We are pretty sure that the child node is an unedited nested
825 branched node. When it needs to be made mutable, it should claim
827 *inherit_p = copy_id_inherit_new;
828 *copy_src_path = id_path;
832 /* Allocate a new parent_path_t node from POOL, referring to NODE,
833 ENTRY, PARENT, and COPY_ID. */
834 static parent_path_t *
835 make_parent_path(dag_node_t *node,
837 parent_path_t *parent,
840 parent_path_t *parent_path = apr_pcalloc(pool, sizeof(*parent_path));
841 parent_path->node = node;
842 parent_path->entry = entry;
843 parent_path->parent = parent;
844 parent_path->copy_inherit = copy_id_inherit_unknown;
845 parent_path->copy_src_path = NULL;
850 /* Flags for open_path. */
851 typedef enum open_path_flags_t {
853 /* The last component of the PATH need not exist. (All parent
854 directories must exist, as usual.) If the last component doesn't
855 exist, simply leave the `node' member of the bottom parent_path
857 open_path_last_optional = 1,
859 /* When this flag is set, don't bother to lookup the DAG node in
860 our caches because we already tried this. Ignoring this flag
861 has no functional impact. */
862 open_path_uncached = 2,
864 /* The caller does not care about the parent node chain but only
865 the final DAG node. */
866 open_path_node_only = 4,
868 /* The caller wants a NULL path object instead of an error if the
869 path cannot be found. */
870 open_path_allow_null = 8
873 /* Try a short-cut for the open_path() function using the last node accessed.
874 * If that ROOT is that nodes's "created rev" and PATH of PATH_LEN chars is
875 * its "created path", return the node in *NODE_P. Set it to NULL otherwise.
877 * This function is used to support ra_serf-style access patterns where we
878 * are first asked for path@rev and then for path@c_rev of the same node.
879 * The shortcut works by ignoring the "rev" part of the cache key and then
880 * checking whether we got lucky. Lookup and verification are both quick
881 * plus there are many early outs for common types of mismatch.
884 try_match_last_node(dag_node_t **node_p,
888 apr_pool_t *scratch_pool)
890 fs_fs_data_t *ffd = root->fs->fsap_data;
892 /* Optimistic lookup: if the last node returned from the cache applied to
893 the same PATH, return it in NODE. */
895 = cache_lookup_last_path(ffd->dag_node_cache, path, path_len);
897 /* Did we get a bucket with a committed node? */
898 if (node && !svn_fs_fs__dag_check_mutable(node))
900 /* Get the path&rev pair at which this node was created.
901 This is repository location for which this node is _known_ to be
902 the right lookup result irrespective of how we found it. */
903 const char *created_path
904 = svn_fs_fs__dag_get_created_path(node);
905 svn_revnum_t revision;
906 SVN_ERR(svn_fs_fs__dag_get_revision(&revision, node, scratch_pool));
908 /* Is it an exact match? */
909 if (revision == root->rev && strcmp(created_path, path) == 0)
911 /* Cache it under its full path@rev access path. */
912 SVN_ERR(dag_node_cache_set(root, path, node, scratch_pool));
924 /* Open the node identified by PATH in ROOT, allocating in POOL. Set
925 *PARENT_PATH_P to a path from the node up to ROOT. The resulting
926 **PARENT_PATH_P value is guaranteed to contain at least one
927 *element, for the root directory. PATH must be in canonical form.
929 If resulting *PARENT_PATH_P will eventually be made mutable and
930 modified, or if copy ID inheritance information is otherwise needed,
931 IS_TXN_PATH must be set. If IS_TXN_PATH is FALSE, no copy ID
932 inheritance information will be calculated for the *PARENT_PATH_P chain.
934 If FLAGS & open_path_last_optional is zero, return the error
935 SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist. If
936 non-zero, require all the parent directories to exist as normal,
937 but if the final path component doesn't exist, simply return a path
938 whose bottom `node' member is zero. This option is useful for
939 callers that create new nodes --- we find the parent directory for
940 them, and tell them whether the entry exists already.
942 The remaining bits in FLAGS are hints that allow this function
943 to take shortcuts based on knowledge that the caller provides,
944 such as the caller is not actually being interested in PARENT_PATH_P,
945 but only in (*PARENT_PATH_P)->NODE.
947 NOTE: Public interfaces which only *read* from the filesystem
948 should not call this function directly, but should instead use
952 open_path(parent_path_t **parent_path_p,
956 svn_boolean_t is_txn_path,
959 svn_fs_t *fs = root->fs;
960 dag_node_t *here = NULL; /* The directory we're currently looking at. */
961 parent_path_t *parent_path; /* The path from HERE up to the root. */
962 const char *rest = NULL; /* The portion of PATH we haven't traversed yet. */
963 apr_pool_t *iterpool = svn_pool_create(pool);
965 /* path to the currently processed entry without trailing '/'.
966 We will reuse this across iterations by simply putting a NUL terminator
967 at the respective position and replacing that with a '/' in the next
968 iteration. This is correct as we assert() PATH to be canonical. */
969 svn_stringbuf_t *path_so_far = svn_stringbuf_create(path, pool);
970 apr_size_t path_len = path_so_far->len;
972 /* Callers often traverse the DAG in some path-based order or along the
973 history segments. That allows us to try a few guesses about where to
974 find the next item. This is only useful if the caller didn't request
975 the full parent chain. */
976 assert(svn_fs__is_canonical_abspath(path));
977 path_so_far->len = 0; /* "" */
978 if (flags & open_path_node_only)
980 const char *directory;
982 /* First attempt: Assume that we access the DAG for the same path as
983 in the last lookup but for a different revision that happens to be
984 the last revision that touched the respective node. This is a
985 common pattern when e.g. checking out over ra_serf. Note that this
986 will only work for committed data as the revision info for nodes in
989 This shortcut is quick and will exit this function upon success.
991 if (!root->is_txn_root)
994 SVN_ERR(try_match_last_node(&node, root, path, path_len, iterpool));
996 /* Did the shortcut work? */
999 /* Construct and return the result. */
1000 svn_pool_destroy(iterpool);
1002 parent_path = make_parent_path(node, 0, 0, pool);
1003 parent_path->copy_inherit = copy_id_inherit_self;
1004 *parent_path_p = parent_path;
1006 return SVN_NO_ERROR;
1010 /* Second attempt: Try starting the lookup immediately at the parent
1011 node. We will often have recently accessed either a sibling or
1012 said parent DIRECTORY itself for the same revision. */
1013 directory = svn_dirent_dirname(path, pool);
1014 if (directory[1] != 0) /* root nodes are covered anyway */
1016 SVN_ERR(dag_node_cache_get(&here, root, directory, pool));
1018 /* Did the shortcut work? */
1021 apr_size_t dirname_len = strlen(directory);
1022 path_so_far->len = dirname_len;
1023 rest = path + dirname_len + 1;
1028 /* did the shortcut work? */
1031 /* Make a parent_path item for the root node, using its own current
1033 SVN_ERR(root_node(&here, root, pool));
1034 rest = path + 1; /* skip the leading '/', it saves in iteration */
1037 path_so_far->data[path_so_far->len] = '\0';
1038 parent_path = make_parent_path(here, 0, 0, pool);
1039 parent_path->copy_inherit = copy_id_inherit_self;
1041 /* Whenever we are at the top of this loop:
1042 - HERE is our current directory,
1043 - ID is the node revision ID of HERE,
1044 - REST is the path we're going to find in HERE, and
1045 - PARENT_PATH includes HERE and all its parents. */
1052 svn_pool_clear(iterpool);
1054 /* Parse out the next entry from the path. */
1055 entry = svn_fs__next_entry_name(&next, rest, pool);
1057 /* Update the path traversed thus far. */
1058 path_so_far->data[path_so_far->len] = '/';
1059 path_so_far->len += strlen(entry) + 1;
1060 path_so_far->data[path_so_far->len] = '\0';
1064 /* Given the behavior of svn_fs__next_entry_name(), this
1065 happens when the path either starts or ends with a slash.
1066 In either case, we stay put: the current directory stays
1067 the same, and we add nothing to the parent path. */
1072 copy_id_inherit_t inherit;
1073 const char *copy_path = NULL;
1074 dag_node_t *cached_node = NULL;
1076 /* If we found a directory entry, follow it. First, we
1077 check our node cache, and, failing that, we hit the DAG
1078 layer. Don't bother to contact the cache for the last
1079 element if we already know the lookup to fail for the
1081 if (next || !(flags & open_path_uncached))
1082 SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far->data,
1085 child = cached_node;
1087 SVN_ERR(svn_fs_fs__dag_open(&child, here, entry, pool, iterpool));
1089 /* "file not found" requires special handling. */
1092 /* If this was the last path component, and the caller
1093 said it was optional, then don't return an error;
1094 just put a NULL node pointer in the path. */
1096 if ((flags & open_path_last_optional)
1097 && (! next || *next == '\0'))
1099 parent_path = make_parent_path(NULL, entry, parent_path,
1103 else if (flags & open_path_allow_null)
1110 /* Build a better error message than svn_fs_fs__dag_open
1111 can provide, giving the root and full path name. */
1112 return SVN_FS__NOT_FOUND(root, path);
1116 if (flags & open_path_node_only)
1118 /* Shortcut: the caller only wants the final DAG node. */
1119 parent_path->node = child;
1123 /* Now, make a parent_path item for CHILD. */
1124 parent_path = make_parent_path(child, entry, parent_path, pool);
1127 SVN_ERR(get_copy_inheritance(&inherit, ©_path, fs,
1128 parent_path, iterpool));
1129 parent_path->copy_inherit = inherit;
1130 parent_path->copy_src_path = apr_pstrdup(pool, copy_path);
1134 /* Cache the node we found (if it wasn't already cached). */
1136 SVN_ERR(dag_node_cache_set(root, path_so_far->data, child,
1140 /* Are we finished traversing the path? */
1144 /* The path isn't finished yet; we'd better be in a directory. */
1145 if (svn_fs_fs__dag_node_kind(child) != svn_node_dir)
1149 /* Since this is not a directory and we are looking for some
1150 sub-path, that sub-path will not exist. That will be o.k.,
1151 if we are just here to check for the path's existence. */
1152 if (flags & open_path_allow_null)
1158 /* It's really a problem ... */
1159 msg = root->is_txn_root
1160 ? apr_psprintf(iterpool,
1161 _("Failure opening '%s' in transaction '%s'"),
1163 : apr_psprintf(iterpool,
1164 _("Failure opening '%s' in revision %ld"),
1166 SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far->data), msg);
1173 svn_pool_destroy(iterpool);
1174 *parent_path_p = parent_path;
1175 return SVN_NO_ERROR;
1179 /* Make the node referred to by PARENT_PATH mutable, if it isn't
1180 already, allocating from POOL. ROOT must be the root from which
1181 PARENT_PATH descends. Clone any parent directories as needed.
1182 Adjust the dag nodes in PARENT_PATH to refer to the clones. Use
1183 ERROR_PATH in error messages. */
1184 static svn_error_t *
1185 make_path_mutable(svn_fs_root_t *root,
1186 parent_path_t *parent_path,
1187 const char *error_path,
1191 const svn_fs_fs__id_part_t *txn_id = root_txn_id(root);
1193 /* Is the node mutable already? */
1194 if (svn_fs_fs__dag_check_mutable(parent_path->node))
1195 return SVN_NO_ERROR;
1197 /* Are we trying to clone the root, or somebody's child node? */
1198 if (parent_path->parent)
1200 const svn_fs_id_t *parent_id, *child_id, *copyroot_id;
1201 svn_fs_fs__id_part_t copy_id = { SVN_INVALID_REVNUM, 0 };
1202 svn_fs_fs__id_part_t *copy_id_ptr = ©_id;
1203 copy_id_inherit_t inherit = parent_path->copy_inherit;
1204 const char *clone_path, *copyroot_path;
1205 svn_revnum_t copyroot_rev;
1206 svn_boolean_t is_parent_copyroot = FALSE;
1207 svn_fs_root_t *copyroot_root;
1208 dag_node_t *copyroot_node;
1210 /* We're trying to clone somebody's child. Make sure our parent
1212 SVN_ERR(make_path_mutable(root, parent_path->parent,
1217 case copy_id_inherit_parent:
1218 parent_id = svn_fs_fs__dag_get_id(parent_path->parent->node);
1219 copy_id = *svn_fs_fs__id_copy_id(parent_id);
1222 case copy_id_inherit_new:
1223 SVN_ERR(svn_fs_fs__reserve_copy_id(©_id, root->fs, txn_id,
1227 case copy_id_inherit_self:
1231 case copy_id_inherit_unknown:
1233 SVN_ERR_MALFUNCTION(); /* uh-oh -- somebody didn't calculate copy-ID
1234 inheritance data. */
1237 /* Determine what copyroot our new child node should use. */
1238 SVN_ERR(svn_fs_fs__dag_get_copyroot(©root_rev, ©root_path,
1239 parent_path->node));
1240 SVN_ERR(svn_fs_fs__revision_root(©root_root, root->fs,
1241 copyroot_rev, pool));
1242 SVN_ERR(get_dag(©root_node, copyroot_root, copyroot_path, pool));
1244 child_id = svn_fs_fs__dag_get_id(parent_path->node);
1245 copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
1246 if (!svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(child_id),
1247 svn_fs_fs__id_node_id(copyroot_id)))
1248 is_parent_copyroot = TRUE;
1250 /* Now make this node mutable. */
1251 clone_path = parent_path_path(parent_path->parent, pool);
1252 SVN_ERR(svn_fs_fs__dag_clone_child(&clone,
1253 parent_path->parent->node,
1256 copy_id_ptr, txn_id,
1260 /* Update the path cache. */
1261 SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool),
1266 /* We're trying to clone the root directory. */
1267 SVN_ERR(mutable_root_node(&clone, root, error_path, pool));
1270 /* Update the PARENT_PATH link to refer to the clone. */
1271 parent_path->node = clone;
1273 return SVN_NO_ERROR;
1277 /* Open the node identified by PATH in ROOT. Set DAG_NODE_P to the
1278 node we find, allocated in POOL. Return the error
1279 SVN_ERR_FS_NOT_FOUND if this node doesn't exist. */
1280 static svn_error_t *
1281 get_dag(dag_node_t **dag_node_p,
1282 svn_fs_root_t *root,
1286 parent_path_t *parent_path;
1287 dag_node_t *node = NULL;
1289 /* First we look for the DAG in our cache
1290 (if the path may be canonical). */
1292 SVN_ERR(dag_node_cache_get(&node, root, path, pool));
1296 /* Canonicalize the input PATH. As it turns out, >95% of all paths
1297 * seen here during e.g. svnadmin verify are non-canonical, i.e.
1298 * miss the leading '/'. Check for those quickly.
1300 * For normalized paths, it is much faster to check the path than
1301 * to attempt a second cache lookup (which would fail). */
1302 if (*path != '/' || !svn_fs__is_canonical_abspath(path))
1304 path = svn_fs__canonicalize_abspath(path, pool);
1305 SVN_ERR(dag_node_cache_get(&node, root, path, pool));
1310 /* Call open_path with no flags, as we want this to return an
1311 * error if the node for which we are searching doesn't exist. */
1312 SVN_ERR(open_path(&parent_path, root, path,
1313 open_path_uncached | open_path_node_only,
1315 node = parent_path->node;
1317 /* No need to cache our find -- open_path() will do that for us. */
1322 return SVN_NO_ERROR;
1327 /* Populating the `changes' table. */
1329 /* Add a change to the changes table in FS, keyed on transaction id
1330 TXN_ID, and indicated that a change of kind CHANGE_KIND occurred on
1331 PATH (whose node revision id is--or was, in the case of a
1332 deletion--NODEREV_ID), and optionally that TEXT_MODs, PROP_MODs or
1333 MERGEINFO_MODs occurred. If the change resulted from a copy,
1334 COPYFROM_REV and COPYFROM_PATH specify under which revision and path
1335 the node was copied from. If this was not part of a copy, COPYFROM_REV
1336 should be SVN_INVALID_REVNUM. Do all this as part of POOL. */
1337 static svn_error_t *
1338 add_change(svn_fs_t *fs,
1339 const svn_fs_fs__id_part_t *txn_id,
1341 const svn_fs_id_t *noderev_id,
1342 svn_fs_path_change_kind_t change_kind,
1343 svn_boolean_t text_mod,
1344 svn_boolean_t prop_mod,
1345 svn_boolean_t mergeinfo_mod,
1346 svn_node_kind_t node_kind,
1347 svn_revnum_t copyfrom_rev,
1348 const char *copyfrom_path,
1351 return svn_fs_fs__add_change(fs, txn_id,
1352 svn_fs__canonicalize_abspath(path, pool),
1353 noderev_id, change_kind,
1354 text_mod, prop_mod, mergeinfo_mod,
1355 node_kind, copyfrom_rev, copyfrom_path,
1361 /* Generic node operations. */
1363 /* Get the id of a node referenced by path PATH in ROOT. Return the
1364 id in *ID_P allocated in POOL. */
1366 svn_fs_fs__node_id(const svn_fs_id_t **id_p,
1367 svn_fs_root_t *root,
1371 if ((! root->is_txn_root)
1372 && (path[0] == '\0' || ((path[0] == '/') && (path[1] == '\0'))))
1374 /* Optimize the case where we don't need any db access at all.
1375 The root directory ("" or "/") node is stored in the
1376 svn_fs_root_t object, and never changes when it's a revision
1377 root, so we can just reach in and grab it directly. */
1378 dag_node_t *root_dir = root->fsap_data;
1379 *id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(root_dir), pool);
1385 SVN_ERR(get_dag(&node, root, path, pool));
1386 *id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(node), pool);
1388 return SVN_NO_ERROR;
1391 static svn_error_t *
1392 fs_node_relation(svn_fs_node_relation_t *relation,
1393 svn_fs_root_t *root_a, const char *path_a,
1394 svn_fs_root_t *root_b, const char *path_b,
1398 const svn_fs_id_t *id_a, *id_b;
1399 svn_fs_fs__id_part_t node_id_a, node_id_b;
1401 /* Root paths are a common special case. */
1402 svn_boolean_t a_is_root_dir
1403 = (path_a[0] == '\0') || ((path_a[0] == '/') && (path_a[1] == '\0'));
1404 svn_boolean_t b_is_root_dir
1405 = (path_b[0] == '\0') || ((path_b[0] == '/') && (path_b[1] == '\0'));
1407 /* Another useful thing to know: Both are txns but not the same txn. */
1408 svn_boolean_t different_txn
1409 = root_a->is_txn_root && root_b->is_txn_root
1410 && strcmp(root_a->txn, root_b->txn);
1412 /* Path from different repository are always unrelated. */
1413 if (root_a->fs != root_b->fs)
1415 *relation = svn_fs_node_unrelated;
1416 return SVN_NO_ERROR;
1419 /* Are both (!) root paths? Then, they are related and we only test how
1420 * direct the relation is. */
1421 if (a_is_root_dir && b_is_root_dir)
1423 /* For txn roots, root->REV is the base revision of that TXN. */
1424 *relation = ( (root_a->rev == root_b->rev)
1425 && (root_a->is_txn_root == root_b->is_txn_root)
1427 ? svn_fs_node_unchanged
1428 : svn_fs_node_common_ancestor;
1429 return SVN_NO_ERROR;
1432 /* We checked for all separations between ID spaces (repos, txn).
1433 * Now, we can simply test for the ID values themselves. */
1434 SVN_ERR(get_dag(&node, root_a, path_a, pool));
1435 id_a = svn_fs_fs__dag_get_id(node);
1436 node_id_a = *svn_fs_fs__id_node_id(id_a);
1438 SVN_ERR(get_dag(&node, root_b, path_b, pool));
1439 id_b = svn_fs_fs__dag_get_id(node);
1440 node_id_b = *svn_fs_fs__id_node_id(id_b);
1442 /* Noderevs from different nodes are unrelated. */
1443 if (!svn_fs_fs__id_part_eq(&node_id_a, &node_id_b))
1445 *relation = svn_fs_node_unrelated;
1446 return SVN_NO_ERROR;
1449 /* Noderevs have the same node-ID now. So, they *seem* to be related.
1451 * Special case: Different txns may create the same (txn-local) node ID.
1452 * These are not related to each other, nor to any other node ID so far. */
1453 if (different_txn && node_id_a.revision == SVN_INVALID_REVNUM)
1455 *relation = svn_fs_node_unrelated;
1456 return SVN_NO_ERROR;
1459 /* The noderevs are actually related. Are they the same? */
1460 if (svn_fs_fs__id_eq(id_a, id_b))
1461 *relation = svn_fs_node_unchanged;
1463 *relation = svn_fs_node_common_ancestor;
1465 return SVN_NO_ERROR;
1469 svn_fs_fs__node_created_rev(svn_revnum_t *revision,
1470 svn_fs_root_t *root,
1476 SVN_ERR(get_dag(&node, root, path, pool));
1477 return svn_fs_fs__dag_get_revision(revision, node, pool);
1481 /* Set *CREATED_PATH to the path at which PATH under ROOT was created.
1482 Return a string allocated in POOL. */
1483 static svn_error_t *
1484 fs_node_created_path(const char **created_path,
1485 svn_fs_root_t *root,
1491 SVN_ERR(get_dag(&node, root, path, pool));
1492 *created_path = svn_fs_fs__dag_get_created_path(node);
1494 return SVN_NO_ERROR;
1497 /* Set *KIND_P to the type of node present at PATH under ROOT. If
1498 PATH does not exist under ROOT, set *KIND_P to svn_node_none. Use
1499 POOL for temporary allocation. */
1501 svn_fs_fs__check_path(svn_node_kind_t *kind_p,
1502 svn_fs_root_t *root,
1509 err = get_dag(&node, root, path, pool);
1511 ((err->apr_err == SVN_ERR_FS_NOT_FOUND)
1512 || (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY)))
1514 svn_error_clear(err);
1515 *kind_p = svn_node_none;
1516 return SVN_NO_ERROR;
1519 return svn_error_trace(err);
1521 *kind_p = svn_fs_fs__dag_node_kind(node);
1522 return SVN_NO_ERROR;
1525 /* Set *VALUE_P to the value of the property named PROPNAME of PATH in
1526 ROOT. If the node has no property by that name, set *VALUE_P to
1527 zero. Allocate the result in POOL. */
1528 static svn_error_t *
1529 fs_node_prop(svn_string_t **value_p,
1530 svn_fs_root_t *root,
1532 const char *propname,
1536 apr_hash_t *proplist;
1538 SVN_ERR(get_dag(&node, root, path, pool));
1539 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, node, pool));
1542 *value_p = svn_hash_gets(proplist, propname);
1544 return SVN_NO_ERROR;
1548 /* Set *TABLE_P to the entire property list of PATH under ROOT, as an
1549 APR hash table allocated in POOL. The resulting property table
1550 maps property names to pointers to svn_string_t objects containing
1551 the property value. */
1552 static svn_error_t *
1553 fs_node_proplist(apr_hash_t **table_p,
1554 svn_fs_root_t *root,
1561 SVN_ERR(get_dag(&node, root, path, pool));
1562 SVN_ERR(svn_fs_fs__dag_get_proplist(&table, node, pool));
1563 *table_p = table ? table : apr_hash_make(pool);
1565 return SVN_NO_ERROR;
1568 static svn_error_t *
1569 fs_node_has_props(svn_boolean_t *has_props,
1570 svn_fs_root_t *root,
1572 apr_pool_t *scratch_pool)
1576 SVN_ERR(get_dag(&node, root, path, scratch_pool));
1578 return svn_error_trace(svn_fs_fs__dag_has_props(has_props, node,
1582 static svn_error_t *
1583 increment_mergeinfo_up_tree(parent_path_t *pp,
1584 apr_int64_t increment,
1587 for (; pp; pp = pp->parent)
1588 SVN_ERR(svn_fs_fs__dag_increment_mergeinfo_count(pp->node,
1592 return SVN_NO_ERROR;
1595 /* Change, add, or delete a node's property value. The affected node
1596 is PATH under ROOT, the property value to modify is NAME, and VALUE
1597 points to either a string value to set the new contents to, or NULL
1598 if the property should be deleted. Perform temporary allocations
1600 static svn_error_t *
1601 fs_change_node_prop(svn_fs_root_t *root,
1604 const svn_string_t *value,
1607 parent_path_t *parent_path;
1608 apr_hash_t *proplist;
1609 const svn_fs_fs__id_part_t *txn_id;
1610 svn_boolean_t mergeinfo_mod = FALSE;
1612 if (! root->is_txn_root)
1613 return SVN_FS__NOT_TXN(root);
1614 txn_id = root_txn_id(root);
1616 path = svn_fs__canonicalize_abspath(path, pool);
1617 SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, pool));
1619 /* Check (non-recursively) to see if path is locked; if so, check
1620 that we can use it. */
1621 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
1622 SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, FALSE, FALSE,
1625 SVN_ERR(make_path_mutable(root, parent_path, path, pool));
1626 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, parent_path->node, pool));
1628 /* If there's no proplist, but we're just deleting a property, exit now. */
1629 if ((! proplist) && (! value))
1630 return SVN_NO_ERROR;
1632 /* Now, if there's no proplist, we know we need to make one. */
1634 proplist = apr_hash_make(pool);
1636 if (svn_fs_fs__fs_supports_mergeinfo(root->fs)
1637 && strcmp (name, SVN_PROP_MERGEINFO) == 0)
1639 apr_int64_t increment = 0;
1640 svn_boolean_t had_mergeinfo;
1641 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&had_mergeinfo, parent_path->node));
1643 if (value && !had_mergeinfo)
1645 else if (!value && had_mergeinfo)
1650 SVN_ERR(increment_mergeinfo_up_tree(parent_path, increment, pool));
1651 SVN_ERR(svn_fs_fs__dag_set_has_mergeinfo(parent_path->node,
1652 (value != NULL), pool));
1655 mergeinfo_mod = TRUE;
1658 /* Set the property. */
1659 svn_hash_sets(proplist, name, value);
1661 /* Overwrite the node's proplist. */
1662 SVN_ERR(svn_fs_fs__dag_set_proplist(parent_path->node, proplist,
1665 /* Make a record of this modification in the changes table. */
1666 return add_change(root->fs, txn_id, path,
1667 svn_fs_fs__dag_get_id(parent_path->node),
1668 svn_fs_path_change_modify, FALSE, TRUE, mergeinfo_mod,
1669 svn_fs_fs__dag_node_kind(parent_path->node),
1670 SVN_INVALID_REVNUM, NULL, pool);
1674 /* Determine if the properties of two path/root combinations are
1675 different. Set *CHANGED_P to TRUE if the properties at PATH1 under
1676 ROOT1 differ from those at PATH2 under ROOT2, or FALSE otherwise.
1677 Both roots must be in the same filesystem. */
1678 static svn_error_t *
1679 fs_props_changed(svn_boolean_t *changed_p,
1680 svn_fs_root_t *root1,
1682 svn_fs_root_t *root2,
1684 svn_boolean_t strict,
1687 dag_node_t *node1, *node2;
1689 /* Check that roots are in the same fs. */
1690 if (root1->fs != root2->fs)
1691 return svn_error_create
1692 (SVN_ERR_FS_GENERAL, NULL,
1693 _("Cannot compare property value between two different filesystems"));
1695 SVN_ERR(get_dag(&node1, root1, path1, pool));
1696 SVN_ERR(get_dag(&node2, root2, path2, pool));
1697 return svn_fs_fs__dag_things_different(changed_p, NULL,
1698 node1, node2, strict, pool);
1703 /* Merges and commits. */
1705 /* Set *NODE to the root node of ROOT. */
1706 static svn_error_t *
1707 get_root(dag_node_t **node, svn_fs_root_t *root, apr_pool_t *pool)
1709 return get_dag(node, root, "/", pool);
1713 /* Set the contents of CONFLICT_PATH to PATH, and return an
1714 SVN_ERR_FS_CONFLICT error that indicates that there was a conflict
1715 at PATH. Perform all allocations in POOL (except the allocation of
1716 CONFLICT_PATH, which should be handled outside this function). */
1717 static svn_error_t *
1718 conflict_err(svn_stringbuf_t *conflict_path,
1721 svn_stringbuf_set(conflict_path, path);
1722 return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL,
1723 _("Conflict at '%s'"), path);
1726 /* Compare the directory representations at nodes LHS and RHS and set
1727 * *CHANGED to TRUE, if at least one entry has been added or removed them.
1728 * Use POOL for temporary allocations.
1730 static svn_error_t *
1731 compare_dir_structure(svn_boolean_t *changed,
1736 apr_array_header_t *lhs_entries;
1737 apr_array_header_t *rhs_entries;
1740 SVN_ERR(svn_fs_fs__dag_dir_entries(&lhs_entries, lhs, pool));
1741 SVN_ERR(svn_fs_fs__dag_dir_entries(&rhs_entries, rhs, pool));
1743 /* different number of entries -> some addition / removal */
1744 if (lhs_entries->nelts != rhs_entries->nelts)
1747 return SVN_NO_ERROR;
1750 /* Since directories are sorted by name, we can simply compare their
1751 entries one-by-one without binary lookup etc. */
1752 for (i = 0; i < lhs_entries->nelts; ++i)
1754 svn_fs_dirent_t *lhs_entry
1755 = APR_ARRAY_IDX(lhs_entries, i, svn_fs_dirent_t *);
1756 svn_fs_dirent_t *rhs_entry
1757 = APR_ARRAY_IDX(rhs_entries, i, svn_fs_dirent_t *);
1759 if (strcmp(lhs_entry->name, rhs_entry->name)
1760 || !svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(lhs_entry->id),
1761 svn_fs_fs__id_node_id(rhs_entry->id))
1762 || !svn_fs_fs__id_part_eq(svn_fs_fs__id_copy_id(lhs_entry->id),
1763 svn_fs_fs__id_copy_id(rhs_entry->id)))
1766 return SVN_NO_ERROR;
1771 return SVN_NO_ERROR;
1774 /* Merge changes between ANCESTOR and SOURCE into TARGET. ANCESTOR
1775 * and TARGET must be distinct node revisions. TARGET_PATH should
1776 * correspond to TARGET's full path in its filesystem, and is used for
1777 * reporting conflict location.
1779 * SOURCE, TARGET, and ANCESTOR are generally directories; this
1780 * function recursively merges the directories' contents. If any are
1781 * files, this function simply returns an error whenever SOURCE,
1782 * TARGET, and ANCESTOR are all distinct node revisions.
1784 * If there are differences between ANCESTOR and SOURCE that conflict
1785 * with changes between ANCESTOR and TARGET, this function returns an
1786 * SVN_ERR_FS_CONFLICT error, and updates CONFLICT_P to the name of the
1787 * conflicting node in TARGET, with TARGET_PATH prepended as a path.
1789 * If there are no conflicting differences, CONFLICT_P is updated to
1792 * CONFLICT_P must point to a valid svn_stringbuf_t.
1794 * Do any necessary temporary allocation in POOL.
1796 static svn_error_t *
1797 merge(svn_stringbuf_t *conflict_p,
1798 const char *target_path,
1801 dag_node_t *ancestor,
1802 const svn_fs_fs__id_part_t *txn_id,
1803 apr_int64_t *mergeinfo_increment_out,
1806 const svn_fs_id_t *source_id, *target_id, *ancestor_id;
1807 apr_array_header_t *s_entries, *t_entries, *a_entries;
1808 int i, s_idx = -1, t_idx = -1;
1810 apr_pool_t *iterpool;
1811 apr_int64_t mergeinfo_increment = 0;
1812 svn_boolean_t fs_supports_mergeinfo;
1814 /* Make sure everyone comes from the same filesystem. */
1815 fs = svn_fs_fs__dag_get_fs(ancestor);
1816 if ((fs != svn_fs_fs__dag_get_fs(source))
1817 || (fs != svn_fs_fs__dag_get_fs(target)))
1819 return svn_error_create
1820 (SVN_ERR_FS_CORRUPT, NULL,
1821 _("Bad merge; ancestor, source, and target not all in same fs"));
1824 /* We have the same fs, now check it. */
1825 SVN_ERR(svn_fs__check_fs(fs, TRUE));
1827 source_id = svn_fs_fs__dag_get_id(source);
1828 target_id = svn_fs_fs__dag_get_id(target);
1829 ancestor_id = svn_fs_fs__dag_get_id(ancestor);
1831 /* It's improper to call this function with ancestor == target. */
1832 if (svn_fs_fs__id_eq(ancestor_id, target_id))
1834 svn_string_t *id_str = svn_fs_fs__id_unparse(target_id, pool);
1835 return svn_error_createf
1836 (SVN_ERR_FS_GENERAL, NULL,
1837 _("Bad merge; target '%s' has id '%s', same as ancestor"),
1838 target_path, id_str->data);
1841 svn_stringbuf_setempty(conflict_p);
1844 * Either no change made in source, or same change as made in target.
1845 * Both mean nothing to merge here.
1847 if (svn_fs_fs__id_eq(ancestor_id, source_id)
1848 || (svn_fs_fs__id_eq(source_id, target_id)))
1849 return SVN_NO_ERROR;
1851 /* Else proceed, knowing all three are distinct node revisions.
1853 * How to merge from this point:
1855 * if (not all 3 are directories)
1857 * early exit with conflict;
1860 * // Property changes may only be made to up-to-date
1861 * // directories, because once the client commits the prop
1862 * // change, it bumps the directory's revision, and therefore
1863 * // must be able to depend on there being no other changes to
1864 * // that directory in the repository.
1865 * if (target's property list differs from ancestor's)
1868 * For each entry NAME in the directory ANCESTOR:
1870 * Let ANCESTOR-ENTRY, SOURCE-ENTRY, and TARGET-ENTRY be the IDs of
1871 * the name within ANCESTOR, SOURCE, and TARGET respectively.
1872 * (Possibly null if NAME does not exist in SOURCE or TARGET.)
1874 * If ANCESTOR-ENTRY == SOURCE-ENTRY, then:
1875 * No changes were made to this entry while the transaction was in
1876 * progress, so do nothing to the target.
1878 * Else if ANCESTOR-ENTRY == TARGET-ENTRY, then:
1879 * A change was made to this entry while the transaction was in
1880 * process, but the transaction did not touch this entry. Replace
1881 * TARGET-ENTRY with SOURCE-ENTRY.
1884 * Changes were made to this entry both within the transaction and
1885 * to the repository while the transaction was in progress. They
1886 * must be merged or declared to be in conflict.
1888 * If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
1889 * double delete; flag a conflict.
1891 * If any of the three entries is of type file, declare a conflict.
1893 * If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
1894 * modification of ANCESTOR-ENTRY (determine by comparing the
1895 * node-id fields), declare a conflict. A replacement is
1896 * incompatible with a modification or other replacement--even
1897 * an identical replacement.
1899 * Direct modifications were made to the directory ANCESTOR-ENTRY
1900 * in both SOURCE and TARGET. Recursively merge these
1903 * For each leftover entry NAME in the directory SOURCE:
1905 * If NAME exists in TARGET, declare a conflict. Even if SOURCE and
1906 * TARGET are adding exactly the same thing, two additions are not
1907 * auto-mergeable with each other.
1909 * Add NAME to TARGET with the entry from SOURCE.
1911 * Now that we are done merging the changes from SOURCE into the
1912 * directory TARGET, update TARGET's predecessor to be SOURCE.
1915 if ((svn_fs_fs__dag_node_kind(source) != svn_node_dir)
1916 || (svn_fs_fs__dag_node_kind(target) != svn_node_dir)
1917 || (svn_fs_fs__dag_node_kind(ancestor) != svn_node_dir))
1919 return conflict_err(conflict_p, target_path);
1923 /* Possible early merge failure: if target and ancestor have
1924 different property lists, then the merge should fail.
1925 Propchanges can *only* be committed on an up-to-date directory.
1926 ### TODO: see issue #418 about the inelegance of this.
1928 Another possible, similar, early merge failure: if source and
1929 ancestor have different property lists (meaning someone else
1930 changed directory properties while our commit transaction was
1931 happening), the merge should fail. See issue #2751.
1934 node_revision_t *tgt_nr, *anc_nr, *src_nr;
1936 apr_pool_t *scratch_pool;
1938 /* Get node revisions for our id's. */
1939 scratch_pool = svn_pool_create(pool);
1940 SVN_ERR(svn_fs_fs__get_node_revision(&tgt_nr, fs, target_id, pool,
1942 svn_pool_clear(scratch_pool);
1943 SVN_ERR(svn_fs_fs__get_node_revision(&anc_nr, fs, ancestor_id, pool,
1945 svn_pool_clear(scratch_pool);
1946 SVN_ERR(svn_fs_fs__get_node_revision(&src_nr, fs, source_id, pool,
1948 svn_pool_destroy(scratch_pool);
1950 /* Now compare the prop-keys of the skels. Note that just because
1951 the keys are different -doesn't- mean the proplists have
1952 different contents. */
1953 SVN_ERR(svn_fs_fs__prop_rep_equal(&same, fs, src_nr, anc_nr, pool));
1955 return conflict_err(conflict_p, target_path);
1957 /* The directory entries got changed in the repository but the directory
1958 properties did not. */
1959 SVN_ERR(svn_fs_fs__prop_rep_equal(&same, fs, tgt_nr, anc_nr, pool));
1962 /* There is an incoming prop change for this directory.
1963 We will accept it only if the directory changes were mere updates
1964 to its entries, i.e. there were no additions or removals.
1965 Those could cause update problems to the working copy. */
1966 svn_boolean_t changed;
1967 SVN_ERR(compare_dir_structure(&changed, source, ancestor, pool));
1970 return conflict_err(conflict_p, target_path);
1974 /* ### todo: it would be more efficient to simply check for a NULL
1975 entries hash where necessary below than to allocate an empty hash
1976 here, but another day, another day... */
1977 SVN_ERR(svn_fs_fs__dag_dir_entries(&s_entries, source, pool));
1978 SVN_ERR(svn_fs_fs__dag_dir_entries(&t_entries, target, pool));
1979 SVN_ERR(svn_fs_fs__dag_dir_entries(&a_entries, ancestor, pool));
1981 fs_supports_mergeinfo = svn_fs_fs__fs_supports_mergeinfo(fs);
1983 /* for each entry E in a_entries... */
1984 iterpool = svn_pool_create(pool);
1985 for (i = 0; i < a_entries->nelts; ++i)
1987 svn_fs_dirent_t *s_entry, *t_entry, *a_entry;
1988 svn_pool_clear(iterpool);
1990 a_entry = APR_ARRAY_IDX(a_entries, i, svn_fs_dirent_t *);
1991 s_entry = svn_fs_fs__find_dir_entry(s_entries, a_entry->name, &s_idx);
1992 t_entry = svn_fs_fs__find_dir_entry(t_entries, a_entry->name, &t_idx);
1994 /* No changes were made to this entry while the transaction was
1995 in progress, so do nothing to the target. */
1996 if (s_entry && svn_fs_fs__id_eq(a_entry->id, s_entry->id))
1999 /* A change was made to this entry while the transaction was in
2000 process, but the transaction did not touch this entry. */
2001 else if (t_entry && svn_fs_fs__id_eq(a_entry->id, t_entry->id))
2003 dag_node_t *t_ent_node;
2004 SVN_ERR(svn_fs_fs__dag_get_node(&t_ent_node, fs,
2005 t_entry->id, iterpool));
2006 if (fs_supports_mergeinfo)
2008 apr_int64_t mergeinfo_start;
2009 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start,
2011 mergeinfo_increment -= mergeinfo_start;
2016 dag_node_t *s_ent_node;
2017 SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
2018 s_entry->id, iterpool));
2020 if (fs_supports_mergeinfo)
2022 apr_int64_t mergeinfo_end;
2023 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end,
2025 mergeinfo_increment += mergeinfo_end;
2028 SVN_ERR(svn_fs_fs__dag_set_entry(target, a_entry->name,
2036 SVN_ERR(svn_fs_fs__dag_delete(target, a_entry->name, txn_id,
2041 /* Changes were made to this entry both within the transaction
2042 and to the repository while the transaction was in progress.
2043 They must be merged or declared to be in conflict. */
2046 dag_node_t *s_ent_node, *t_ent_node, *a_ent_node;
2047 const char *new_tpath;
2048 apr_int64_t sub_mergeinfo_increment;
2050 /* If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
2051 double delete; if one of them is null, that's a delete versus
2052 a modification. In any of these cases, flag a conflict. */
2053 if (s_entry == NULL || t_entry == NULL)
2054 return conflict_err(conflict_p,
2055 svn_fspath__join(target_path,
2059 /* If any of the three entries is of type file, flag a conflict. */
2060 if (s_entry->kind == svn_node_file
2061 || t_entry->kind == svn_node_file
2062 || a_entry->kind == svn_node_file)
2063 return conflict_err(conflict_p,
2064 svn_fspath__join(target_path,
2068 /* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
2069 modification of ANCESTOR-ENTRY, declare a conflict. */
2070 if (!svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(s_entry->id),
2071 svn_fs_fs__id_node_id(a_entry->id))
2072 || !svn_fs_fs__id_part_eq(svn_fs_fs__id_copy_id(s_entry->id),
2073 svn_fs_fs__id_copy_id(a_entry->id))
2074 || !svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(t_entry->id),
2075 svn_fs_fs__id_node_id(a_entry->id))
2076 || !svn_fs_fs__id_part_eq(svn_fs_fs__id_copy_id(t_entry->id),
2077 svn_fs_fs__id_copy_id(a_entry->id)))
2078 return conflict_err(conflict_p,
2079 svn_fspath__join(target_path,
2083 /* Direct modifications were made to the directory
2084 ANCESTOR-ENTRY in both SOURCE and TARGET. Recursively
2085 merge these modifications. */
2086 SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
2087 s_entry->id, iterpool));
2088 SVN_ERR(svn_fs_fs__dag_get_node(&t_ent_node, fs,
2089 t_entry->id, iterpool));
2090 SVN_ERR(svn_fs_fs__dag_get_node(&a_ent_node, fs,
2091 a_entry->id, iterpool));
2092 new_tpath = svn_fspath__join(target_path, t_entry->name, iterpool);
2093 SVN_ERR(merge(conflict_p, new_tpath,
2094 t_ent_node, s_ent_node, a_ent_node,
2096 &sub_mergeinfo_increment,
2098 if (fs_supports_mergeinfo)
2099 mergeinfo_increment += sub_mergeinfo_increment;
2103 /* For each entry E in source but not in ancestor */
2104 for (i = 0; i < s_entries->nelts; ++i)
2106 svn_fs_dirent_t *a_entry, *s_entry, *t_entry;
2107 dag_node_t *s_ent_node;
2109 svn_pool_clear(iterpool);
2111 s_entry = APR_ARRAY_IDX(s_entries, i, svn_fs_dirent_t *);
2112 a_entry = svn_fs_fs__find_dir_entry(a_entries, s_entry->name, &s_idx);
2113 t_entry = svn_fs_fs__find_dir_entry(t_entries, s_entry->name, &t_idx);
2115 /* Process only entries in source that are NOT in ancestor. */
2119 /* If NAME exists in TARGET, declare a conflict. */
2121 return conflict_err(conflict_p,
2122 svn_fspath__join(target_path,
2126 SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
2127 s_entry->id, iterpool));
2128 if (fs_supports_mergeinfo)
2130 apr_int64_t mergeinfo_s;
2131 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_s,
2133 mergeinfo_increment += mergeinfo_s;
2136 SVN_ERR(svn_fs_fs__dag_set_entry
2137 (target, s_entry->name, s_entry->id, s_entry->kind,
2140 svn_pool_destroy(iterpool);
2142 SVN_ERR(svn_fs_fs__dag_update_ancestry(target, source, pool));
2144 if (fs_supports_mergeinfo)
2145 SVN_ERR(svn_fs_fs__dag_increment_mergeinfo_count(target,
2146 mergeinfo_increment,
2149 if (mergeinfo_increment_out)
2150 *mergeinfo_increment_out = mergeinfo_increment;
2152 return SVN_NO_ERROR;
2155 /* Merge changes between an ancestor and SOURCE_NODE into
2156 TXN. The ancestor is either ANCESTOR_NODE, or if
2157 that is null, TXN's base node.
2159 If the merge is successful, TXN's base will become
2160 SOURCE_NODE, and its root node will have a new ID, a
2161 successor of SOURCE_NODE.
2163 If a conflict results, update *CONFLICT to the path in the txn that
2164 conflicted; see the CONFLICT_P parameter of merge() for details. */
2165 static svn_error_t *
2166 merge_changes(dag_node_t *ancestor_node,
2167 dag_node_t *source_node,
2169 svn_stringbuf_t *conflict,
2172 dag_node_t *txn_root_node;
2173 svn_fs_t *fs = txn->fs;
2174 const svn_fs_fs__id_part_t *txn_id = svn_fs_fs__txn_get_id(txn);
2176 SVN_ERR(svn_fs_fs__dag_txn_root(&txn_root_node, fs, txn_id, pool));
2178 if (ancestor_node == NULL)
2180 SVN_ERR(svn_fs_fs__dag_txn_base_root(&ancestor_node, fs,
2184 if (svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(ancestor_node),
2185 svn_fs_fs__dag_get_id(txn_root_node)))
2187 /* If no changes have been made in TXN since its current base,
2188 then it can't conflict with any changes since that base.
2189 The caller isn't supposed to call us in that case. */
2190 SVN_ERR_MALFUNCTION();
2193 SVN_ERR(merge(conflict, "/", txn_root_node,
2194 source_node, ancestor_node, txn_id, NULL, pool));
2196 return SVN_NO_ERROR;
2201 svn_fs_fs__commit_txn(const char **conflict_p,
2202 svn_revnum_t *new_rev,
2206 /* How do commits work in Subversion?
2208 * When you're ready to commit, here's what you have:
2210 * 1. A transaction, with a mutable tree hanging off it.
2211 * 2. A base revision, against which TXN_TREE was made.
2212 * 3. A latest revision, which may be newer than the base rev.
2214 * The problem is that if latest != base, then one can't simply
2215 * attach the txn root as the root of the new revision, because that
2216 * would lose all the changes between base and latest. It is also
2217 * not acceptable to insist that base == latest; in a busy
2218 * repository, commits happen too fast to insist that everyone keep
2219 * their entire tree up-to-date at all times. Non-overlapping
2220 * changes should not interfere with each other.
2222 * The solution is to merge the changes between base and latest into
2223 * the txn tree [see the function merge()]. The txn tree is the
2224 * only one of the three trees that is mutable, so it has to be the
2227 * You might have to adjust it more than once, if a new latest
2228 * revision gets committed while you were merging in the previous
2231 * 1. Jane starts txn T, based at revision 6.
2232 * 2. Someone commits (or already committed) revision 7.
2233 * 3. Jane's starts merging the changes between 6 and 7 into T.
2234 * 4. Meanwhile, someone commits revision 8.
2235 * 5. Jane finishes the 6-->7 merge. T could now be committed
2236 * against a latest revision of 7, if only that were still the
2237 * latest. Unfortunately, 8 is now the latest, so...
2238 * 6. Jane starts merging the changes between 7 and 8 into T.
2239 * 7. Meanwhile, no one commits any new revisions. Whew.
2240 * 8. Jane commits T, creating revision 9, whose tree is exactly
2241 * T's tree, except immutable now.
2243 * Lather, rinse, repeat.
2246 svn_error_t *err = SVN_NO_ERROR;
2247 svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool);
2248 svn_fs_t *fs = txn->fs;
2249 fs_fs_data_t *ffd = fs->fsap_data;
2251 /* Limit memory usage when the repository has a high commit rate and
2252 needs to run the following while loop multiple times. The memory
2253 growth without an iteration pool is very noticeable when the
2254 transaction modifies a node that has 20,000 sibling nodes. */
2255 apr_pool_t *iterpool = svn_pool_create(pool);
2257 /* Initialize output params. */
2258 *new_rev = SVN_INVALID_REVNUM;
2264 svn_revnum_t youngish_rev;
2265 svn_fs_root_t *youngish_root;
2266 dag_node_t *youngish_root_node;
2268 svn_pool_clear(iterpool);
2270 /* Get the *current* youngest revision. We call it "youngish"
2271 because new revisions might get committed after we've
2274 SVN_ERR(svn_fs_fs__youngest_rev(&youngish_rev, fs, iterpool));
2275 SVN_ERR(svn_fs_fs__revision_root(&youngish_root, fs, youngish_rev,
2278 /* Get the dag node for the youngest revision. Later we'll use
2279 it as the SOURCE argument to a merge, and if the merge
2280 succeeds, this youngest root node will become the new base
2281 root for the svn txn that was the target of the merge (but
2282 note that the youngest rev may have changed by then -- that's
2283 why we're careful to get this root in its own bdb txn
2285 SVN_ERR(get_root(&youngish_root_node, youngish_root, iterpool));
2287 /* Try to merge. If the merge succeeds, the base root node of
2288 TARGET's txn will become the same as youngish_root_node, so
2289 any future merges will only be between that node and whatever
2290 the root node of the youngest rev is by then. */
2291 err = merge_changes(NULL, youngish_root_node, txn, conflict, iterpool);
2294 if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2295 *conflict_p = conflict->data;
2298 txn->base_rev = youngish_rev;
2300 /* Try to commit. */
2301 err = svn_fs_fs__commit(new_rev, fs, txn, iterpool);
2302 if (err && (err->apr_err == SVN_ERR_FS_TXN_OUT_OF_DATE))
2304 /* Did someone else finish committing a new revision while we
2305 were in mid-merge or mid-commit? If so, we'll need to
2306 loop again to merge the new changes in, then try to
2307 commit again. Or if that's not what happened, then just
2308 return the error. */
2309 svn_revnum_t youngest_rev;
2310 SVN_ERR(svn_fs_fs__youngest_rev(&youngest_rev, fs, iterpool));
2311 if (youngest_rev == youngish_rev)
2314 svn_error_clear(err);
2329 svn_fs_fs__reset_txn_caches(fs);
2331 svn_pool_destroy(iterpool);
2335 if (ffd->pack_after_commit)
2337 SVN_ERR(svn_fs_fs__pack(fs, 0, NULL, NULL, NULL, NULL, pool));
2340 return SVN_NO_ERROR;
2344 /* Merge changes between two nodes into a third node. Given nodes
2345 SOURCE_PATH under SOURCE_ROOT, TARGET_PATH under TARGET_ROOT and
2346 ANCESTOR_PATH under ANCESTOR_ROOT, modify target to contain all the
2347 changes between the ancestor and source. If there are conflicts,
2348 return SVN_ERR_FS_CONFLICT and set *CONFLICT_P to a textual
2349 description of the offending changes. Perform any temporary
2350 allocations in POOL. */
2351 static svn_error_t *
2352 fs_merge(const char **conflict_p,
2353 svn_fs_root_t *source_root,
2354 const char *source_path,
2355 svn_fs_root_t *target_root,
2356 const char *target_path,
2357 svn_fs_root_t *ancestor_root,
2358 const char *ancestor_path,
2361 dag_node_t *source, *ancestor;
2364 svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool);
2366 if (! target_root->is_txn_root)
2367 return SVN_FS__NOT_TXN(target_root);
2370 if ((source_root->fs != ancestor_root->fs)
2371 || (target_root->fs != ancestor_root->fs))
2373 return svn_error_create
2374 (SVN_ERR_FS_CORRUPT, NULL,
2375 _("Bad merge; ancestor, source, and target not all in same fs"));
2378 /* ### kff todo: is there any compelling reason to get the nodes in
2379 one db transaction? Right now we don't; txn_body_get_root() gets
2380 one node at a time. This will probably need to change:
2382 Jim Blandy <jimb@zwingli.cygnus.com> writes:
2383 > svn_fs_merge needs to be a single transaction, to protect it against
2384 > people deleting parents of nodes it's working on, etc.
2387 /* Get the ancestor node. */
2388 SVN_ERR(get_root(&ancestor, ancestor_root, pool));
2390 /* Get the source node. */
2391 SVN_ERR(get_root(&source, source_root, pool));
2393 /* Open a txn for the txn root into which we're merging. */
2394 SVN_ERR(svn_fs_fs__open_txn(&txn, ancestor_root->fs, target_root->txn,
2397 /* Merge changes between ANCESTOR and SOURCE into TXN. */
2398 err = merge_changes(ancestor, source, txn, conflict, pool);
2401 if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2402 *conflict_p = conflict->data;
2403 return svn_error_trace(err);
2406 return SVN_NO_ERROR;
2410 svn_fs_fs__deltify(svn_fs_t *fs,
2411 svn_revnum_t revision,
2414 /* Deltify is a no-op for fs_fs. */
2416 return SVN_NO_ERROR;
2423 /* Set *TABLE_P to a newly allocated APR hash table containing the
2424 entries of the directory at PATH in ROOT. The keys of the table
2425 are entry names, as byte strings, excluding the final null
2426 character; the table's values are pointers to svn_fs_dirent_t
2427 structures. Allocate the table and its contents in POOL. */
2428 static svn_error_t *
2429 fs_dir_entries(apr_hash_t **table_p,
2430 svn_fs_root_t *root,
2435 apr_hash_t *hash = svn_hash__make(pool);
2436 apr_array_header_t *table;
2439 /* Get the entries for this path in the caller's pool. */
2440 SVN_ERR(get_dag(&node, root, path, pool));
2441 SVN_ERR(svn_fs_fs__dag_dir_entries(&table, node, pool));
2443 /* Convert directory array to hash. */
2444 for (i = 0; i < table->nelts; ++i)
2446 svn_fs_dirent_t *entry = APR_ARRAY_IDX(table, i, svn_fs_dirent_t *);
2447 svn_hash_sets(hash, entry->name, entry);
2451 return SVN_NO_ERROR;
2454 static svn_error_t *
2455 fs_dir_optimal_order(apr_array_header_t **ordered_p,
2456 svn_fs_root_t *root,
2457 apr_hash_t *entries,
2458 apr_pool_t *result_pool,
2459 apr_pool_t *scratch_pool)
2461 *ordered_p = svn_fs_fs__order_dir_entries(root->fs, entries, result_pool,
2464 return SVN_NO_ERROR;
2467 /* Raise an error if PATH contains a newline because FSFS cannot handle
2468 * such paths. See issue #4340. */
2469 static svn_error_t *
2470 check_newline(const char *path, apr_pool_t *pool)
2472 char *c = strchr(path, '\n');
2475 return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
2476 _("Invalid control character '0x%02x' in path '%s'"),
2477 (unsigned char)*c, svn_path_illegal_path_escape(path, pool));
2479 return SVN_NO_ERROR;
2482 /* Create a new directory named PATH in ROOT. The new directory has
2483 no entries, and no properties. ROOT must be the root of a
2484 transaction, not a revision. Do any necessary temporary allocation
2486 static svn_error_t *
2487 fs_make_dir(svn_fs_root_t *root,
2491 parent_path_t *parent_path;
2492 dag_node_t *sub_dir;
2493 const svn_fs_fs__id_part_t *txn_id = root_txn_id(root);
2495 SVN_ERR(check_newline(path, pool));
2497 path = svn_fs__canonicalize_abspath(path, pool);
2498 SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
2501 /* Check (recursively) to see if some lock is 'reserving' a path at
2502 that location, or even some child-path; if so, check that we can
2504 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2505 SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, TRUE, FALSE,
2508 /* If there's already a sub-directory by that name, complain. This
2509 also catches the case of trying to make a subdirectory named `/'. */
2510 if (parent_path->node)
2511 return SVN_FS__ALREADY_EXISTS(root, path);
2513 /* Create the subdirectory. */
2514 SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
2515 SVN_ERR(svn_fs_fs__dag_make_dir(&sub_dir,
2516 parent_path->parent->node,
2517 parent_path_path(parent_path->parent,
2523 /* Add this directory to the path cache. */
2524 SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool),
2527 /* Make a record of this modification in the changes table. */
2528 return add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(sub_dir),
2529 svn_fs_path_change_add, FALSE, FALSE, FALSE,
2530 svn_node_dir, SVN_INVALID_REVNUM, NULL, pool);
2534 /* Delete the node at PATH under ROOT. ROOT must be a transaction
2535 root. Perform temporary allocations in POOL. */
2536 static svn_error_t *
2537 fs_delete_node(svn_fs_root_t *root,
2541 parent_path_t *parent_path;
2542 const svn_fs_fs__id_part_t *txn_id;
2543 apr_int64_t mergeinfo_count = 0;
2544 svn_node_kind_t kind;
2546 if (! root->is_txn_root)
2547 return SVN_FS__NOT_TXN(root);
2549 txn_id = root_txn_id(root);
2550 path = svn_fs__canonicalize_abspath(path, pool);
2551 SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, pool));
2552 kind = svn_fs_fs__dag_node_kind(parent_path->node);
2554 /* We can't remove the root of the filesystem. */
2555 if (! parent_path->parent)
2556 return svn_error_create(SVN_ERR_FS_ROOT_DIR, NULL,
2557 _("The root directory cannot be deleted"));
2559 /* Check to see if path (or any child thereof) is locked; if so,
2560 check that we can use the existing lock(s). */
2561 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2562 SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, TRUE, FALSE,
2565 /* Make the parent directory mutable, and do the deletion. */
2566 SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
2567 if (svn_fs_fs__fs_supports_mergeinfo(root->fs))
2568 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_count,
2569 parent_path->node));
2570 SVN_ERR(svn_fs_fs__dag_delete(parent_path->parent->node,
2574 /* Remove this node and any children from the path cache. */
2575 SVN_ERR(dag_node_cache_invalidate(root, parent_path_path(parent_path, pool),
2578 /* Update mergeinfo counts for parents */
2579 if (mergeinfo_count > 0)
2580 SVN_ERR(increment_mergeinfo_up_tree(parent_path->parent,
2584 /* Make a record of this modification in the changes table. */
2585 return add_change(root->fs, txn_id, path,
2586 svn_fs_fs__dag_get_id(parent_path->node),
2587 svn_fs_path_change_delete, FALSE, FALSE, FALSE, kind,
2588 SVN_INVALID_REVNUM, NULL, pool);
2592 /* Set *SAME_P to TRUE if FS1 and FS2 have the same UUID, else set to FALSE.
2593 Use POOL for temporary allocation only.
2594 Note: this code is duplicated between libsvn_fs_fs and libsvn_fs_base. */
2595 static svn_error_t *
2596 fs_same_p(svn_boolean_t *same_p,
2601 *same_p = ! strcmp(fs1->uuid, fs2->uuid);
2602 return SVN_NO_ERROR;
2605 /* Copy the node at FROM_PATH under FROM_ROOT to TO_PATH under
2606 TO_ROOT. If PRESERVE_HISTORY is set, then the copy is recorded in
2607 the copies table. Perform temporary allocations in POOL. */
2608 static svn_error_t *
2609 copy_helper(svn_fs_root_t *from_root,
2610 const char *from_path,
2611 svn_fs_root_t *to_root,
2612 const char *to_path,
2613 svn_boolean_t preserve_history,
2616 dag_node_t *from_node;
2617 parent_path_t *to_parent_path;
2618 const svn_fs_fs__id_part_t *txn_id = root_txn_id(to_root);
2619 svn_boolean_t same_p;
2621 /* Use an error check, not an assert, because even the caller cannot
2622 guarantee that a filesystem's UUID has not changed "on the fly". */
2623 SVN_ERR(fs_same_p(&same_p, from_root->fs, to_root->fs, pool));
2625 return svn_error_createf
2626 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2627 _("Cannot copy between two different filesystems ('%s' and '%s')"),
2628 from_root->fs->path, to_root->fs->path);
2630 /* more things that we can't do ATM */
2631 if (from_root->is_txn_root)
2632 return svn_error_create
2633 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2634 _("Copy from mutable tree not currently supported"));
2636 if (! to_root->is_txn_root)
2637 return svn_error_create
2638 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2639 _("Copy immutable tree not supported"));
2641 /* Get the NODE for FROM_PATH in FROM_ROOT.*/
2642 SVN_ERR(get_dag(&from_node, from_root, from_path, pool));
2644 /* Build up the parent path from TO_PATH in TO_ROOT. If the last
2645 component does not exist, it's not that big a deal. We'll just
2647 SVN_ERR(open_path(&to_parent_path, to_root, to_path,
2648 open_path_last_optional, TRUE, pool));
2650 /* Check to see if path (or any child thereof) is locked; if so,
2651 check that we can use the existing lock(s). */
2652 if (to_root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2653 SVN_ERR(svn_fs_fs__allow_locked_operation(to_path, to_root->fs,
2654 TRUE, FALSE, pool));
2656 /* If the destination node already exists as the same node as the
2657 source (in other words, this operation would result in nothing
2658 happening at all), just do nothing an return successfully,
2659 proud that you saved yourself from a tiresome task. */
2660 if (to_parent_path->node &&
2661 svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(from_node),
2662 svn_fs_fs__dag_get_id(to_parent_path->node)))
2663 return SVN_NO_ERROR;
2665 if (! from_root->is_txn_root)
2667 svn_fs_path_change_kind_t kind;
2668 dag_node_t *new_node;
2669 const char *from_canonpath;
2670 apr_int64_t mergeinfo_start;
2671 apr_int64_t mergeinfo_end;
2673 /* If TO_PATH already existed prior to the copy, note that this
2674 operation is a replacement, not an addition. */
2675 if (to_parent_path->node)
2677 kind = svn_fs_path_change_replace;
2678 if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs))
2679 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start,
2680 to_parent_path->node));
2684 kind = svn_fs_path_change_add;
2685 mergeinfo_start = 0;
2688 if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs))
2689 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end,
2692 /* Make sure the target node's parents are mutable. */
2693 SVN_ERR(make_path_mutable(to_root, to_parent_path->parent,
2696 /* Canonicalize the copyfrom path. */
2697 from_canonpath = svn_fs__canonicalize_abspath(from_path, pool);
2699 SVN_ERR(svn_fs_fs__dag_copy(to_parent_path->parent->node,
2700 to_parent_path->entry,
2707 if (kind != svn_fs_path_change_add)
2708 SVN_ERR(dag_node_cache_invalidate(to_root,
2709 parent_path_path(to_parent_path,
2712 if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs)
2713 && mergeinfo_start != mergeinfo_end)
2714 SVN_ERR(increment_mergeinfo_up_tree(to_parent_path->parent,
2715 mergeinfo_end - mergeinfo_start,
2718 /* Make a record of this modification in the changes table. */
2719 SVN_ERR(get_dag(&new_node, to_root, to_path, pool));
2720 SVN_ERR(add_change(to_root->fs, txn_id, to_path,
2721 svn_fs_fs__dag_get_id(new_node), kind, FALSE,
2722 FALSE, FALSE, svn_fs_fs__dag_node_kind(from_node),
2723 from_root->rev, from_canonpath, pool));
2727 /* See IZ Issue #436 */
2728 /* Copying from transaction roots not currently available.
2730 ### cmpilato todo someday: make this not so. :-) Note that
2731 when copying from mutable trees, you have to make sure that
2732 you aren't creating a cyclic graph filesystem, and a simple
2733 referencing operation won't cut it. Currently, we should not
2734 be able to reach this clause, and the interface reports that
2735 this only works from immutable trees anyway, but JimB has
2736 stated that this requirement need not be necessary in the
2739 SVN_ERR_MALFUNCTION();
2742 return SVN_NO_ERROR;
2746 /* Create a copy of FROM_PATH in FROM_ROOT named TO_PATH in TO_ROOT.
2747 If FROM_PATH is a directory, copy it recursively. Temporary
2748 allocations are from POOL.*/
2749 static svn_error_t *
2750 fs_copy(svn_fs_root_t *from_root,
2751 const char *from_path,
2752 svn_fs_root_t *to_root,
2753 const char *to_path,
2756 SVN_ERR(check_newline(to_path, pool));
2758 return svn_error_trace(copy_helper(from_root,
2759 svn_fs__canonicalize_abspath(from_path,
2762 svn_fs__canonicalize_abspath(to_path,
2768 /* Create a copy of FROM_PATH in FROM_ROOT named TO_PATH in TO_ROOT.
2769 If FROM_PATH is a directory, copy it recursively. No history is
2770 preserved. Temporary allocations are from POOL. */
2771 static svn_error_t *
2772 fs_revision_link(svn_fs_root_t *from_root,
2773 svn_fs_root_t *to_root,
2777 if (! to_root->is_txn_root)
2778 return SVN_FS__NOT_TXN(to_root);
2780 path = svn_fs__canonicalize_abspath(path, pool);
2781 return svn_error_trace(copy_helper(from_root, path, to_root, path,
2786 /* Discover the copy ancestry of PATH under ROOT. Return a relevant
2787 ancestor/revision combination in *PATH_P and *REV_P. Temporary
2788 allocations are in POOL. */
2789 static svn_error_t *
2790 fs_copied_from(svn_revnum_t *rev_p,
2791 const char **path_p,
2792 svn_fs_root_t *root,
2798 /* There is no cached entry, look it up the old-fashioned
2800 SVN_ERR(get_dag(&node, root, path, pool));
2801 SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(rev_p, node));
2802 SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(path_p, node));
2804 return SVN_NO_ERROR;
2811 /* Create the empty file PATH under ROOT. Temporary allocations are
2813 static svn_error_t *
2814 fs_make_file(svn_fs_root_t *root,
2818 parent_path_t *parent_path;
2820 const svn_fs_fs__id_part_t *txn_id = root_txn_id(root);
2822 SVN_ERR(check_newline(path, pool));
2824 path = svn_fs__canonicalize_abspath(path, pool);
2825 SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
2828 /* If there's already a file by that name, complain.
2829 This also catches the case of trying to make a file named `/'. */
2830 if (parent_path->node)
2831 return SVN_FS__ALREADY_EXISTS(root, path);
2833 /* Check (non-recursively) to see if path is locked; if so, check
2834 that we can use it. */
2835 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2836 SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, FALSE, FALSE,
2839 /* Create the file. */
2840 SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
2841 SVN_ERR(svn_fs_fs__dag_make_file(&child,
2842 parent_path->parent->node,
2843 parent_path_path(parent_path->parent,
2849 /* Add this file to the path cache. */
2850 SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool), child,
2853 /* Make a record of this modification in the changes table. */
2854 return add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(child),
2855 svn_fs_path_change_add, TRUE, FALSE, FALSE,
2856 svn_node_file, SVN_INVALID_REVNUM, NULL, pool);
2860 /* Set *LENGTH_P to the size of the file PATH under ROOT. Temporary
2861 allocations are in POOL. */
2862 static svn_error_t *
2863 fs_file_length(svn_filesize_t *length_p,
2864 svn_fs_root_t *root,
2870 /* First create a dag_node_t from the root/path pair. */
2871 SVN_ERR(get_dag(&file, root, path, pool));
2873 /* Now fetch its length */
2874 return svn_fs_fs__dag_file_length(length_p, file, pool);
2878 /* Set *CHECKSUM to the checksum of type KIND for PATH under ROOT, or
2879 NULL if that information isn't available. Temporary allocations
2881 static svn_error_t *
2882 fs_file_checksum(svn_checksum_t **checksum,
2883 svn_checksum_kind_t kind,
2884 svn_fs_root_t *root,
2890 SVN_ERR(get_dag(&file, root, path, pool));
2891 return svn_fs_fs__dag_file_checksum(checksum, file, kind, pool);
2895 /* --- Machinery for svn_fs_file_contents() --- */
2897 /* Set *CONTENTS to a readable stream that will return the contents of
2898 PATH under ROOT. The stream is allocated in POOL. */
2899 static svn_error_t *
2900 fs_file_contents(svn_stream_t **contents,
2901 svn_fs_root_t *root,
2906 svn_stream_t *file_stream;
2908 /* First create a dag_node_t from the root/path pair. */
2909 SVN_ERR(get_dag(&node, root, path, pool));
2911 /* Then create a readable stream from the dag_node_t. */
2912 SVN_ERR(svn_fs_fs__dag_get_contents(&file_stream, node, pool));
2914 *contents = file_stream;
2915 return SVN_NO_ERROR;
2918 /* --- End machinery for svn_fs_file_contents() --- */
2921 /* --- Machinery for svn_fs_try_process_file_contents() --- */
2923 static svn_error_t *
2924 fs_try_process_file_contents(svn_boolean_t *success,
2925 svn_fs_root_t *root,
2927 svn_fs_process_contents_func_t processor,
2932 SVN_ERR(get_dag(&node, root, path, pool));
2934 return svn_fs_fs__dag_try_process_file_contents(success, node,
2935 processor, baton, pool);
2938 /* --- End machinery for svn_fs_try_process_file_contents() --- */
2941 /* --- Machinery for svn_fs_apply_textdelta() --- */
2944 /* Local baton type for all the helper functions below. */
2945 typedef struct txdelta_baton_t
2947 /* This is the custom-built window consumer given to us by the delta
2948 library; it uniquely knows how to read data from our designated
2949 "source" stream, interpret the window, and write data to our
2950 designated "target" stream (in this case, our repos file.) */
2951 svn_txdelta_window_handler_t interpreter;
2952 void *interpreter_baton;
2954 /* The original file info */
2955 svn_fs_root_t *root;
2958 /* Derived from the file info */
2961 svn_stream_t *source_stream;
2962 svn_stream_t *target_stream;
2964 /* MD5 digest for the base text against which a delta is to be
2965 applied, and for the resultant fulltext, respectively. Either or
2966 both may be null, in which case ignored. */
2967 svn_checksum_t *base_checksum;
2968 svn_checksum_t *result_checksum;
2970 /* Pool used by db txns */
2976 /* The main window handler returned by svn_fs_apply_textdelta. */
2977 static svn_error_t *
2978 window_consumer(svn_txdelta_window_t *window, void *baton)
2980 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
2982 /* Send the window right through to the custom window interpreter.
2983 In theory, the interpreter will then write more data to
2984 cb->target_string. */
2985 SVN_ERR(tb->interpreter(window, tb->interpreter_baton));
2987 /* Is the window NULL? If so, we're done. The stream has already been
2988 closed by the interpreter. */
2990 SVN_ERR(svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum,
2993 return SVN_NO_ERROR;
2996 /* Helper function for fs_apply_textdelta. BATON is of type
2998 static svn_error_t *
2999 apply_textdelta(void *baton, apr_pool_t *pool)
3001 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3002 parent_path_t *parent_path;
3003 const svn_fs_fs__id_part_t *txn_id = root_txn_id(tb->root);
3005 /* Call open_path with no flags, as we want this to return an error
3006 if the node for which we are searching doesn't exist. */
3007 SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, TRUE, pool));
3009 /* Check (non-recursively) to see if path is locked; if so, check
3010 that we can use it. */
3011 if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3012 SVN_ERR(svn_fs_fs__allow_locked_operation(tb->path, tb->root->fs,
3013 FALSE, FALSE, pool));
3015 /* Now, make sure this path is mutable. */
3016 SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, pool));
3017 tb->node = parent_path->node;
3019 if (tb->base_checksum)
3021 svn_checksum_t *checksum;
3023 /* Until we finalize the node, its data_key points to the old
3024 contents, in other words, the base text. */
3025 SVN_ERR(svn_fs_fs__dag_file_checksum(&checksum, tb->node,
3026 tb->base_checksum->kind, pool));
3027 if (!svn_checksum_match(tb->base_checksum, checksum))
3028 return svn_checksum_mismatch_err(tb->base_checksum, checksum, pool,
3029 _("Base checksum mismatch on '%s'"),
3033 /* Make a readable "source" stream out of the current contents of
3034 ROOT/PATH; obviously, this must done in the context of a db_txn.
3035 The stream is returned in tb->source_stream. */
3036 SVN_ERR(svn_fs_fs__dag_get_contents(&(tb->source_stream),
3037 tb->node, tb->pool));
3039 /* Make a writable "target" stream */
3040 SVN_ERR(svn_fs_fs__dag_get_edit_stream(&(tb->target_stream), tb->node,
3043 /* Now, create a custom window handler that uses our two streams. */
3044 svn_txdelta_apply(tb->source_stream,
3050 &(tb->interpreter_baton));
3052 /* Make a record of this modification in the changes table. */
3053 return add_change(tb->root->fs, txn_id, tb->path,
3054 svn_fs_fs__dag_get_id(tb->node),
3055 svn_fs_path_change_modify, TRUE, FALSE, FALSE,
3056 svn_node_file, SVN_INVALID_REVNUM, NULL, pool);
3060 /* Set *CONTENTS_P and *CONTENTS_BATON_P to a window handler and baton
3061 that will accept text delta windows to modify the contents of PATH
3062 under ROOT. Allocations are in POOL. */
3063 static svn_error_t *
3064 fs_apply_textdelta(svn_txdelta_window_handler_t *contents_p,
3065 void **contents_baton_p,
3066 svn_fs_root_t *root,
3068 svn_checksum_t *base_checksum,
3069 svn_checksum_t *result_checksum,
3072 txdelta_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
3075 tb->path = svn_fs__canonicalize_abspath(path, pool);
3077 tb->base_checksum = svn_checksum_dup(base_checksum, pool);
3078 tb->result_checksum = svn_checksum_dup(result_checksum, pool);
3080 SVN_ERR(apply_textdelta(tb, pool));
3082 *contents_p = window_consumer;
3083 *contents_baton_p = tb;
3084 return SVN_NO_ERROR;
3087 /* --- End machinery for svn_fs_apply_textdelta() --- */
3089 /* --- Machinery for svn_fs_apply_text() --- */
3091 /* Baton for svn_fs_apply_text(). */
3094 /* The original file info */
3095 svn_fs_root_t *root;
3098 /* Derived from the file info */
3101 /* The returned stream that will accept the file's new contents. */
3102 svn_stream_t *stream;
3104 /* The actual fs stream that the returned stream will write to. */
3105 svn_stream_t *file_stream;
3107 /* MD5 digest for the final fulltext written to the file. May
3108 be null, in which case ignored. */
3109 svn_checksum_t *result_checksum;
3111 /* Pool used by db txns */
3116 /* A wrapper around svn_fs_fs__dag_finalize_edits, but for
3117 * fulltext data, not text deltas. Closes BATON->file_stream.
3119 * Note: If you're confused about how this function relates to another
3120 * of similar name, think of it this way:
3122 * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits()
3123 * svn_fs_apply_text() ==> ... ==> txn_body_fulltext_finalize_edits()
3126 /* Write function for the publically returned stream. */
3127 static svn_error_t *
3128 text_stream_writer(void *baton,
3132 struct text_baton_t *tb = baton;
3134 /* Psst, here's some data. Pass it on to the -real- file stream. */
3135 return svn_stream_write(tb->file_stream, data, len);
3138 /* Close function for the publically returned stream. */
3139 static svn_error_t *
3140 text_stream_closer(void *baton)
3142 struct text_baton_t *tb = baton;
3144 /* Close the internal-use stream. ### This used to be inside of
3145 txn_body_fulltext_finalize_edits(), but that invoked a nested
3146 Berkeley DB transaction -- scandalous! */
3147 SVN_ERR(svn_stream_close(tb->file_stream));
3149 /* Need to tell fs that we're done sending text */
3150 return svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum,
3155 /* Helper function for fs_apply_text. BATON is of type
3157 static svn_error_t *
3158 apply_text(void *baton, apr_pool_t *pool)
3160 struct text_baton_t *tb = baton;
3161 parent_path_t *parent_path;
3162 const svn_fs_fs__id_part_t *txn_id = root_txn_id(tb->root);
3164 /* Call open_path with no flags, as we want this to return an error
3165 if the node for which we are searching doesn't exist. */
3166 SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, TRUE, pool));
3168 /* Check (non-recursively) to see if path is locked; if so, check
3169 that we can use it. */
3170 if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3171 SVN_ERR(svn_fs_fs__allow_locked_operation(tb->path, tb->root->fs,
3172 FALSE, FALSE, pool));
3174 /* Now, make sure this path is mutable. */
3175 SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, pool));
3176 tb->node = parent_path->node;
3178 /* Make a writable stream for replacing the file's text. */
3179 SVN_ERR(svn_fs_fs__dag_get_edit_stream(&(tb->file_stream), tb->node,
3182 /* Create a 'returnable' stream which writes to the file_stream. */
3183 tb->stream = svn_stream_create(tb, tb->pool);
3184 svn_stream_set_write(tb->stream, text_stream_writer);
3185 svn_stream_set_close(tb->stream, text_stream_closer);
3187 /* Make a record of this modification in the changes table. */
3188 return add_change(tb->root->fs, txn_id, tb->path,
3189 svn_fs_fs__dag_get_id(tb->node),
3190 svn_fs_path_change_modify, TRUE, FALSE, FALSE,
3191 svn_node_file, SVN_INVALID_REVNUM, NULL, pool);
3195 /* Return a writable stream that will set the contents of PATH under
3196 ROOT. RESULT_CHECKSUM is the MD5 checksum of the final result.
3197 Temporary allocations are in POOL. */
3198 static svn_error_t *
3199 fs_apply_text(svn_stream_t **contents_p,
3200 svn_fs_root_t *root,
3202 svn_checksum_t *result_checksum,
3205 struct text_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
3208 tb->path = svn_fs__canonicalize_abspath(path, pool);
3210 tb->result_checksum = svn_checksum_dup(result_checksum, pool);
3212 SVN_ERR(apply_text(tb, pool));
3214 *contents_p = tb->stream;
3215 return SVN_NO_ERROR;
3218 /* --- End machinery for svn_fs_apply_text() --- */
3221 /* Check if the contents of PATH1 under ROOT1 are different from the
3222 contents of PATH2 under ROOT2. If they are different set
3223 *CHANGED_P to TRUE, otherwise set it to FALSE. */
3224 static svn_error_t *
3225 fs_contents_changed(svn_boolean_t *changed_p,
3226 svn_fs_root_t *root1,
3228 svn_fs_root_t *root2,
3230 svn_boolean_t strict,
3233 dag_node_t *node1, *node2;
3235 /* Check that roots are in the same fs. */
3236 if (root1->fs != root2->fs)
3237 return svn_error_create
3238 (SVN_ERR_FS_GENERAL, NULL,
3239 _("Cannot compare file contents between two different filesystems"));
3241 SVN_ERR(get_dag(&node1, root1, path1, pool));
3242 /* Make sure that path is file. */
3243 if (svn_fs_fs__dag_node_kind(node1) != svn_node_file)
3244 return svn_error_createf
3245 (SVN_ERR_FS_NOT_FILE, NULL, _("'%s' is not a file"), path1);
3247 SVN_ERR(get_dag(&node2, root2, path2, pool));
3248 /* Make sure that path is file. */
3249 if (svn_fs_fs__dag_node_kind(node2) != svn_node_file)
3250 return svn_error_createf
3251 (SVN_ERR_FS_NOT_FILE, NULL, _("'%s' is not a file"), path2);
3253 return svn_fs_fs__dag_things_different(NULL, changed_p,
3254 node1, node2, strict, pool);
3259 /* Public interface to computing file text deltas. */
3261 static svn_error_t *
3262 fs_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
3263 svn_fs_root_t *source_root,
3264 const char *source_path,
3265 svn_fs_root_t *target_root,
3266 const char *target_path,
3269 dag_node_t *source_node, *target_node;
3271 if (source_root && source_path)
3272 SVN_ERR(get_dag(&source_node, source_root, source_path, pool));
3275 SVN_ERR(get_dag(&target_node, target_root, target_path, pool));
3277 /* Create a delta stream that turns the source into the target. */
3278 return svn_fs_fs__dag_get_file_delta_stream(stream_p, source_node,
3284 /* Finding Changes */
3286 /* Set *CHANGED_PATHS_P to a newly allocated hash containing
3287 descriptions of the paths changed under ROOT. The hash is keyed
3288 with const char * paths and has svn_fs_path_change2_t * values. Use
3289 POOL for all allocations. */
3290 static svn_error_t *
3291 fs_paths_changed(apr_hash_t **changed_paths_p,
3292 svn_fs_root_t *root,
3295 if (root->is_txn_root)
3296 return svn_fs_fs__txn_changes_fetch(changed_paths_p, root->fs,
3297 root_txn_id(root), pool);
3299 return svn_fs_fs__paths_changed(changed_paths_p, root->fs, root->rev,
3304 /* Copy the contents of ENTRY at PATH with LEN to OUTPUT. */
3306 convert_path_change(svn_fs_path_change3_t *output,
3309 svn_fs_path_change2_t *entry)
3311 output->path.data = path;
3312 output->path.len = path_len;
3313 output->change_kind = entry->change_kind;
3314 output->node_kind = entry->node_kind;
3315 output->text_mod = entry->text_mod;
3316 output->prop_mod = entry->prop_mod;
3317 output->mergeinfo_mod = entry->mergeinfo_mod;
3318 output->copyfrom_known = entry->copyfrom_known;
3319 output->copyfrom_rev = entry->copyfrom_rev;
3320 output->copyfrom_path = entry->copyfrom_path;
3323 /* FSAP data structure for in-txn changes list iterators. */
3324 typedef struct fs_txn_changes_iterator_data_t
3326 /* Current iterator position. */
3327 apr_hash_index_t *hi;
3329 /* For efficiency such that we don't need to dynamically allocate
3330 yet another copy of that data. */
3331 svn_fs_path_change3_t change;
3332 } fs_txn_changes_iterator_data_t;
3334 /* Implement changes_iterator_vtable_t.get for in-txn change lists. */
3335 static svn_error_t *
3336 fs_txn_changes_iterator_get(svn_fs_path_change3_t **change,
3337 svn_fs_path_change_iterator_t *iterator)
3339 fs_txn_changes_iterator_data_t *data = iterator->fsap_data;
3346 apr_hash_this(data->hi, &key, &length, &value);
3348 convert_path_change(&data->change, key, length, value);
3350 *change = &data->change;
3351 data->hi = apr_hash_next(data->hi);
3358 return SVN_NO_ERROR;
3361 static changes_iterator_vtable_t txn_changes_iterator_vtable =
3363 fs_txn_changes_iterator_get
3366 /* FSAP data structure for in-revision changes list iterators. */
3367 typedef struct fs_revision_changes_iterator_data_t
3369 /* Context that tells the lower layers from where to fetch the next
3370 block of changes. */
3371 svn_fs_fs__changes_context_t *context;
3373 /* Changes to send. */
3374 apr_array_header_t *changes;
3376 /* Current indexes within CHANGES. */
3379 /* For efficiency such that we don't need to dynamically allocate
3380 yet another copy of that data. */
3381 svn_fs_path_change3_t change;
3383 /* A cleanable scratch pool in case we need one.
3384 No further sub-pool creation necessary. */
3385 apr_pool_t *scratch_pool;
3386 } fs_revision_changes_iterator_data_t;
3388 /* Implement changes_iterator_vtable_t.get for in-revision change lists. */
3389 static svn_error_t *
3390 fs_revision_changes_iterator_get(svn_fs_path_change3_t **change,
3391 svn_fs_path_change_iterator_t *iterator)
3393 fs_revision_changes_iterator_data_t *data = iterator->fsap_data;
3395 /* If we exhausted our block of changes and did not reach the end of the
3396 list, yet, fetch the next block. Note that that block may be empty. */
3397 if ((data->idx >= data->changes->nelts) && !data->context->eol)
3399 apr_pool_t *changes_pool = data->changes->pool;
3401 /* Drop old changes block, read new block. */
3402 svn_pool_clear(changes_pool);
3403 SVN_ERR(svn_fs_fs__get_changes(&data->changes, data->context,
3404 changes_pool, data->scratch_pool));
3407 /* Immediately release any temporary data. */
3408 svn_pool_clear(data->scratch_pool);
3411 if (data->idx < data->changes->nelts)
3413 change_t *entry = APR_ARRAY_IDX(data->changes, data->idx, change_t *);
3414 convert_path_change(&data->change, entry->path.data, entry->path.len,
3417 *change = &data->change;
3425 return SVN_NO_ERROR;
3428 static changes_iterator_vtable_t rev_changes_iterator_vtable =
3430 fs_revision_changes_iterator_get
3433 static svn_error_t *
3434 fs_report_changes(svn_fs_path_change_iterator_t **iterator,
3435 svn_fs_root_t *root,
3436 apr_pool_t *result_pool,
3437 apr_pool_t *scratch_pool)
3439 svn_fs_path_change_iterator_t *result = apr_pcalloc(result_pool,
3441 if (root->is_txn_root)
3443 fs_txn_changes_iterator_data_t *data = apr_pcalloc(result_pool,
3445 apr_hash_t *changed_paths;
3446 SVN_ERR(svn_fs_fs__txn_changes_fetch(&changed_paths, root->fs,
3447 root_txn_id(root), result_pool));
3449 data->hi = apr_hash_first(result_pool, changed_paths);
3450 result->fsap_data = data;
3451 result->vtable = &txn_changes_iterator_vtable;
3455 /* The block of changes that we retrieve need to live in a separately
3457 apr_pool_t *changes_pool = svn_pool_create(result_pool);
3459 /* Our iteration context info. */
3460 fs_revision_changes_iterator_data_t *data = apr_pcalloc(result_pool,
3463 /* This pool must remain valid as long as ITERATOR lives but will
3464 be used only for temporary allocations and will be cleaned up
3465 frequently. So, this must be a sub-pool of RESULT_POOL. */
3466 data->scratch_pool = svn_pool_create(result_pool);
3468 /* Fetch the first block of data. */
3469 SVN_ERR(svn_fs_fs__create_changes_context(&data->context,
3470 root->fs, root->rev,
3472 SVN_ERR(svn_fs_fs__get_changes(&data->changes, data->context,
3473 changes_pool, scratch_pool));
3475 /* Return the fully initialized object. */
3476 result->fsap_data = data;
3477 result->vtable = &rev_changes_iterator_vtable;
3482 return SVN_NO_ERROR;
3486 /* Our coolio opaque history object. */
3487 typedef struct fs_history_data_t
3489 /* filesystem object */
3492 /* path and revision of historical location */
3494 svn_revnum_t revision;
3496 /* internal-use hints about where to resume the history search. */
3497 const char *path_hint;
3498 svn_revnum_t rev_hint;
3500 /* FALSE until the first call to svn_fs_history_prev(). */
3501 svn_boolean_t is_interesting;
3503 /* If not SVN_INVALID_REVISION, we know that the next copy operation
3504 is at this revision. */
3505 svn_revnum_t next_copy;
3507 /* If not NULL, this is the noderev ID of PATH@REVISION. */
3508 const svn_fs_id_t *current_id;
3510 } fs_history_data_t;
3512 static svn_fs_history_t *
3513 assemble_history(svn_fs_t *fs,
3515 svn_revnum_t revision,
3516 svn_boolean_t is_interesting,
3517 const char *path_hint,
3518 svn_revnum_t rev_hint,
3519 svn_revnum_t next_copy,
3520 const svn_fs_id_t *current_id,
3524 /* Set *HISTORY_P to an opaque node history object which represents
3525 PATH under ROOT. ROOT must be a revision root. Use POOL for all
3527 static svn_error_t *
3528 fs_node_history(svn_fs_history_t **history_p,
3529 svn_fs_root_t *root,
3531 apr_pool_t *result_pool,
3532 apr_pool_t *scratch_pool)
3534 svn_node_kind_t kind;
3536 /* We require a revision root. */
3537 if (root->is_txn_root)
3538 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
3540 /* And we require that the path exist in the root. */
3541 SVN_ERR(svn_fs_fs__check_path(&kind, root, path, scratch_pool));
3542 if (kind == svn_node_none)
3543 return SVN_FS__NOT_FOUND(root, path);
3545 /* Okay, all seems well. Build our history object and return it. */
3546 *history_p = assemble_history(root->fs, path, root->rev, FALSE, NULL,
3547 SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
3549 return SVN_NO_ERROR;
3552 /* Find the youngest copyroot for path PARENT_PATH or its parents in
3553 filesystem FS, and store the copyroot in *REV_P and *PATH_P.
3554 Perform all allocations in POOL. */
3555 static svn_error_t *
3556 find_youngest_copyroot(svn_revnum_t *rev_p,
3557 const char **path_p,
3559 parent_path_t *parent_path,
3562 svn_revnum_t rev_mine;
3563 svn_revnum_t rev_parent = SVN_INVALID_REVNUM;
3564 const char *path_mine;
3565 const char *path_parent = NULL;
3567 /* First find our parent's youngest copyroot. */
3568 if (parent_path->parent)
3569 SVN_ERR(find_youngest_copyroot(&rev_parent, &path_parent, fs,
3570 parent_path->parent, pool));
3572 /* Find our copyroot. */
3573 SVN_ERR(svn_fs_fs__dag_get_copyroot(&rev_mine, &path_mine,
3574 parent_path->node));
3576 /* If a parent and child were copied to in the same revision, prefer
3577 the child copy target, since it is the copy relevant to the
3578 history of the child. */
3579 if (rev_mine >= rev_parent)
3582 *path_p = path_mine;
3586 *rev_p = rev_parent;
3587 *path_p = path_parent;
3590 return SVN_NO_ERROR;
3594 static svn_error_t *fs_closest_copy(svn_fs_root_t **root_p,
3595 const char **path_p,
3596 svn_fs_root_t *root,
3600 svn_fs_t *fs = root->fs;
3601 parent_path_t *parent_path, *copy_dst_parent_path;
3602 svn_revnum_t copy_dst_rev, created_rev;
3603 const char *copy_dst_path;
3604 svn_fs_root_t *copy_dst_root;
3605 dag_node_t *copy_dst_node;
3607 /* Initialize return values. */
3611 path = svn_fs__canonicalize_abspath(path, pool);
3612 SVN_ERR(open_path(&parent_path, root, path, 0, FALSE, pool));
3614 /* Find the youngest copyroot in the path of this node-rev, which
3615 will indicate the target of the innermost copy affecting the
3617 SVN_ERR(find_youngest_copyroot(©_dst_rev, ©_dst_path,
3618 fs, parent_path, pool));
3619 if (copy_dst_rev == 0) /* There are no copies affecting this node-rev. */
3620 return SVN_NO_ERROR;
3622 /* It is possible that this node was created from scratch at some
3623 revision between COPY_DST_REV and REV. Make sure that PATH
3624 exists as of COPY_DST_REV and is related to this node-rev. */
3625 SVN_ERR(svn_fs_fs__revision_root(©_dst_root, fs, copy_dst_rev, pool));
3626 SVN_ERR(open_path(©_dst_parent_path, copy_dst_root, path,
3627 open_path_node_only | open_path_allow_null, FALSE, pool));
3628 if (copy_dst_parent_path == NULL)
3629 return SVN_NO_ERROR;
3631 copy_dst_node = copy_dst_parent_path->node;
3632 if (! svn_fs_fs__id_check_related(svn_fs_fs__dag_get_id(copy_dst_node),
3633 svn_fs_fs__dag_get_id(parent_path->node)))
3634 return SVN_NO_ERROR;
3636 /* One final check must be done here. If you copy a directory and
3637 create a new entity somewhere beneath that directory in the same
3638 txn, then we can't claim that the copy affected the new entity.
3639 For example, if you do:
3642 create dir2/new-thing
3645 then dir2/new-thing was not affected by the copy of dir1 to dir2.
3646 We detect this situation by asking if PATH@COPY_DST_REV's
3647 created-rev is COPY_DST_REV, and that node-revision has no
3648 predecessors, then there is no relevant closest copy.
3650 SVN_ERR(svn_fs_fs__dag_get_revision(&created_rev, copy_dst_node, pool));
3651 if (created_rev == copy_dst_rev)
3653 const svn_fs_id_t *pred;
3654 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred, copy_dst_node));
3656 return SVN_NO_ERROR;
3659 /* The copy destination checks out. Return it. */
3660 *root_p = copy_dst_root;
3661 *path_p = copy_dst_path;
3662 return SVN_NO_ERROR;
3666 /* Set *PREV_PATH and *PREV_REV to the path and revision which
3667 represent the location at which PATH in FS was located immediately
3668 prior to REVISION iff there was a copy operation (to PATH or one of
3669 its parent directories) between that previous location and
3672 If there was no such copy operation in that portion of PATH's
3673 history, set *PREV_PATH to NULL and *PREV_REV to SVN_INVALID_REVNUM. */
3674 static svn_error_t *
3675 prev_location(const char **prev_path,
3676 svn_revnum_t *prev_rev,
3678 svn_fs_root_t *root,
3682 const char *copy_path, *copy_src_path, *remainder_path;
3683 svn_fs_root_t *copy_root;
3684 svn_revnum_t copy_src_rev;
3686 /* Ask about the most recent copy which affected PATH@REVISION. If
3687 there was no such copy, we're done. */
3688 SVN_ERR(fs_closest_copy(©_root, ©_path, root, path, pool));
3691 *prev_rev = SVN_INVALID_REVNUM;
3693 return SVN_NO_ERROR;
3696 /* Ultimately, it's not the path of the closest copy's source that
3697 we care about -- it's our own path's location in the copy source
3698 revision. So we'll tack the relative path that expresses the
3699 difference between the copy destination and our path in the copy
3700 revision onto the copy source path to determine this information.
3702 In other words, if our path is "/branches/my-branch/foo/bar", and
3703 we know that the closest relevant copy was a copy of "/trunk" to
3704 "/branches/my-branch", then that relative path under the copy
3705 destination is "/foo/bar". Tacking that onto the copy source
3706 path tells us that our path was located at "/trunk/foo/bar"
3709 SVN_ERR(fs_copied_from(©_src_rev, ©_src_path,
3710 copy_root, copy_path, pool));
3711 remainder_path = svn_fspath__skip_ancestor(copy_path, path);
3712 *prev_path = svn_fspath__join(copy_src_path, remainder_path, pool);
3713 *prev_rev = copy_src_rev;
3714 return SVN_NO_ERROR;
3718 static svn_error_t *
3719 fs_node_origin_rev(svn_revnum_t *revision,
3720 svn_fs_root_t *root,
3724 svn_fs_t *fs = root->fs;
3725 const svn_fs_id_t *given_noderev_id, *cached_origin_id;
3726 const svn_fs_fs__id_part_t *node_id;
3728 path = svn_fs__canonicalize_abspath(path, pool);
3730 /* Check the cache first. */
3731 SVN_ERR(svn_fs_fs__node_id(&given_noderev_id, root, path, pool));
3732 node_id = svn_fs_fs__id_node_id(given_noderev_id);
3734 /* Is it a brand new uncommitted node or a new-style node ID?
3735 * (committed old-style nodes will have a 0 revision value;
3736 * rev 0, number 0 is rev 0 root node). Note that != 0 includes
3737 * SVN_INVALID_REVNUM for uncommitted nodes. */
3738 if (node_id->revision != 0 || node_id->number == 0)
3740 *revision = node_id->revision;
3741 return SVN_NO_ERROR;
3744 /* OK, it's an old-style ID? Maybe it's cached. */
3745 SVN_ERR(svn_fs_fs__get_node_origin(&cached_origin_id,
3749 if (cached_origin_id != NULL)
3751 *revision = svn_fs_fs__id_rev(cached_origin_id);
3752 return SVN_NO_ERROR;
3756 /* Ah well, the answer isn't in the ID itself or in the cache.
3757 Let's actually calculate it, then. */
3758 svn_fs_root_t *curroot = root;
3759 apr_pool_t *subpool = svn_pool_create(pool);
3760 apr_pool_t *predidpool = svn_pool_create(pool);
3761 svn_stringbuf_t *lastpath = svn_stringbuf_create(path, pool);
3762 svn_revnum_t lastrev = SVN_INVALID_REVNUM;
3764 const svn_fs_id_t *pred_id;
3766 /* Walk the closest-copy chain back to the first copy in our history.
3768 NOTE: We merely *assume* that this is faster than walking the
3769 predecessor chain, because we *assume* that copies of parent
3770 directories happen less often than modifications to a given item. */
3773 svn_revnum_t currev;
3774 const char *curpath = lastpath->data;
3776 svn_pool_clear(subpool);
3778 /* Get a root pointing to LASTREV. (The first time around,
3779 LASTREV is invalid, but that's cool because CURROOT is
3780 already initialized.) */
3781 if (SVN_IS_VALID_REVNUM(lastrev))
3782 SVN_ERR(svn_fs_fs__revision_root(&curroot, fs, lastrev, subpool));
3784 /* Find the previous location using the closest-copy shortcut. */
3785 SVN_ERR(prev_location(&curpath, &currev, fs, curroot, curpath,
3790 /* Update our LASTPATH and LASTREV variables (which survive
3792 svn_stringbuf_set(lastpath, curpath);
3796 /* Walk the predecessor links back to origin. */
3797 SVN_ERR(svn_fs_fs__node_id(&pred_id, curroot, lastpath->data, predidpool));
3800 svn_pool_clear(subpool);
3801 SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, subpool));
3803 /* Why not just fetch the predecessor ID in PREDIDPOOL?
3804 Because svn_fs_fs__dag_get_predecessor_id() doesn't
3805 necessarily honor the passed-in pool, and might return a
3806 value cached in the node (which is allocated in
3807 SUBPOOL... maybe). */
3808 svn_pool_clear(predidpool);
3809 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
3810 pred_id = pred_id ? svn_fs_fs__id_copy(pred_id, predidpool) : NULL;
3814 /* When we get here, NODE should be the first node-revision in our
3816 SVN_ERR(svn_fs_fs__dag_get_revision(revision, node, pool));
3818 /* Wow, I don't want to have to do all that again. Let's cache
3820 if (node_id->revision != SVN_INVALID_REVNUM)
3821 SVN_ERR(svn_fs_fs__set_node_origin(fs, node_id,
3822 svn_fs_fs__dag_get_id(node), pool));
3824 svn_pool_destroy(subpool);
3825 svn_pool_destroy(predidpool);
3826 return SVN_NO_ERROR;
3831 static svn_error_t *
3832 history_prev(svn_fs_history_t **prev_history,
3833 svn_fs_history_t *history,
3834 svn_boolean_t cross_copies,
3835 apr_pool_t *result_pool,
3836 apr_pool_t *scratch_pool)
3838 fs_history_data_t *fhd = history->fsap_data;
3839 const char *commit_path, *src_path, *path = fhd->path;
3840 svn_revnum_t commit_rev, src_rev, dst_rev;
3841 svn_revnum_t revision = fhd->revision;
3842 svn_fs_t *fs = fhd->fs;
3843 parent_path_t *parent_path;
3845 svn_fs_root_t *root;
3846 svn_boolean_t reported = fhd->is_interesting;
3847 svn_revnum_t copyroot_rev;
3848 const char *copyroot_path;
3849 const svn_fs_id_t *pred_id = NULL;
3851 /* Initialize our return value. */
3852 *prev_history = NULL;
3854 /* When following history, there tend to be long sections of linear
3855 history where there are no copies at PATH or its parents. Within
3856 these sections, we only need to follow the node history. */
3857 if ( SVN_IS_VALID_REVNUM(fhd->next_copy)
3858 && revision > fhd->next_copy
3861 /* We know the last reported node (CURRENT_ID) and the NEXT_COPY
3862 revision is somewhat further in the past. */
3863 node_revision_t *noderev;
3866 /* Get the previous node change. If there is none, then we already
3867 reported the initial addition and this history traversal is done. */
3868 SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, fhd->current_id,
3869 scratch_pool, scratch_pool));
3870 if (! noderev->predecessor_id)
3871 return SVN_NO_ERROR;
3873 /* If the previous node change is younger than the next copy, it is
3874 part of the linear history section. */
3875 commit_rev = svn_fs_fs__id_rev(noderev->predecessor_id);
3876 if (commit_rev > fhd->next_copy)
3878 /* Within the linear history, simply report all node changes and
3879 continue with the respective predecessor. */
3880 *prev_history = assemble_history(fs, noderev->created_path,
3881 commit_rev, TRUE, NULL,
3884 noderev->predecessor_id,
3887 return SVN_NO_ERROR;
3890 /* We hit a copy. Fall back to the standard code path. */
3893 /* If our last history report left us hints about where to pickup
3894 the chase, then our last report was on the destination of a
3895 copy. If we are crossing copies, start from those locations,
3896 otherwise, we're all done here. */
3897 if (fhd->path_hint && SVN_IS_VALID_REVNUM(fhd->rev_hint))
3901 return SVN_NO_ERROR;
3902 path = fhd->path_hint;
3903 revision = fhd->rev_hint;
3906 /* Construct a ROOT for the current revision. */
3907 SVN_ERR(svn_fs_fs__revision_root(&root, fs, revision, scratch_pool));
3909 /* Open PATH/REVISION, and get its node and a bunch of other
3911 SVN_ERR(open_path(&parent_path, root, path, 0, FALSE, scratch_pool));
3912 node = parent_path->node;
3913 commit_path = svn_fs_fs__dag_get_created_path(node);
3914 SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, scratch_pool));
3916 /* The Subversion filesystem is written in such a way that a given
3917 line of history may have at most one interesting history point
3918 per filesystem revision. Either that node was edited (and
3919 possibly copied), or it was copied but not edited. And a copy
3920 source cannot be from the same revision as its destination. So,
3921 if our history revision matches its node's commit revision, we
3923 if (revision == commit_rev)
3927 /* ... we either have not yet reported on this revision (and
3928 need now to do so) ... */
3929 *prev_history = assemble_history(fs, commit_path,
3930 commit_rev, TRUE, NULL,
3932 SVN_INVALID_REVNUM, NULL,
3934 return SVN_NO_ERROR;
3938 /* ... or we *have* reported on this revision, and must now
3939 progress toward this node's predecessor (unless there is
3940 no predecessor, in which case we're all done!). */
3941 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
3943 return SVN_NO_ERROR;
3945 /* Replace NODE and friends with the information from its
3947 SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, scratch_pool));
3948 commit_path = svn_fs_fs__dag_get_created_path(node);
3949 SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, scratch_pool));
3953 /* Find the youngest copyroot in the path of this node, including
3955 SVN_ERR(find_youngest_copyroot(©root_rev, ©root_path, fs,
3956 parent_path, scratch_pool));
3958 /* Initialize some state variables. */
3960 src_rev = SVN_INVALID_REVNUM;
3961 dst_rev = SVN_INVALID_REVNUM;
3963 if (copyroot_rev > commit_rev)
3965 const char *remainder_path;
3966 const char *copy_dst, *copy_src;
3967 svn_fs_root_t *copyroot_root;
3969 SVN_ERR(svn_fs_fs__revision_root(©root_root, fs, copyroot_rev,
3971 SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, scratch_pool));
3972 copy_dst = svn_fs_fs__dag_get_created_path(node);
3974 /* If our current path was the very destination of the copy,
3975 then our new current path will be the copy source. If our
3976 current path was instead the *child* of the destination of
3977 the copy, then figure out its previous location by taking its
3978 path relative to the copy destination and appending that to
3979 the copy source. Finally, if our current path doesn't meet
3980 one of these other criteria ... ### for now just fallback to
3981 the old copy hunt algorithm. */
3982 remainder_path = svn_fspath__skip_ancestor(copy_dst, path);
3986 /* If we get here, then our current path is the destination
3987 of, or the child of the destination of, a copy. Fill
3988 in the return values and get outta here. */
3989 SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(&src_rev, node));
3990 SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(©_src, node));
3992 dst_rev = copyroot_rev;
3993 src_path = svn_fspath__join(copy_src, remainder_path, scratch_pool);
3997 /* If we calculated a copy source path and revision, we'll make a
3998 'copy-style' history object. */
3999 if (src_path && SVN_IS_VALID_REVNUM(src_rev))
4001 svn_boolean_t retry = FALSE;
4003 /* It's possible for us to find a copy location that is the same
4004 as the history point we've just reported. If that happens,
4005 we simply need to take another trip through this history
4007 if ((dst_rev == revision) && reported)
4010 *prev_history = assemble_history(fs, path, dst_rev, ! retry,
4012 SVN_INVALID_REVNUM, NULL,
4017 /* We know the next copy revision. If we are not at the copy rev
4018 itself, we will also know the predecessor node ID and the next
4019 invocation will use the optimized "linear history" code path. */
4020 *prev_history = assemble_history(fs, commit_path, commit_rev, TRUE,
4021 NULL, SVN_INVALID_REVNUM,
4022 copyroot_rev, pred_id, result_pool);
4025 return SVN_NO_ERROR;
4029 /* Implement svn_fs_history_prev, set *PREV_HISTORY_P to a new
4030 svn_fs_history_t object that represents the predecessory of
4031 HISTORY. If CROSS_COPIES is true, *PREV_HISTORY_P may be related
4032 only through a copy operation. Perform all allocations in POOL. */
4033 static svn_error_t *
4034 fs_history_prev(svn_fs_history_t **prev_history_p,
4035 svn_fs_history_t *history,
4036 svn_boolean_t cross_copies,
4037 apr_pool_t *result_pool,
4038 apr_pool_t *scratch_pool)
4040 svn_fs_history_t *prev_history = NULL;
4041 fs_history_data_t *fhd = history->fsap_data;
4042 svn_fs_t *fs = fhd->fs;
4044 /* Special case: the root directory changes in every single
4045 revision, no exceptions. And, the root can't be the target (or
4046 child of a target -- duh) of a copy. So, if that's our path,
4047 then we need only decrement our revision by 1, and there you go. */
4048 if (strcmp(fhd->path, "/") == 0)
4050 if (! fhd->is_interesting)
4051 prev_history = assemble_history(fs, "/", fhd->revision,
4052 1, NULL, SVN_INVALID_REVNUM,
4053 SVN_INVALID_REVNUM, NULL,
4055 else if (fhd->revision > 0)
4056 prev_history = assemble_history(fs, "/", fhd->revision - 1,
4057 1, NULL, SVN_INVALID_REVNUM,
4058 SVN_INVALID_REVNUM, NULL,
4063 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4064 prev_history = history;
4068 svn_pool_clear(iterpool);
4069 SVN_ERR(history_prev(&prev_history, prev_history, cross_copies,
4070 result_pool, iterpool));
4074 fhd = prev_history->fsap_data;
4075 if (fhd->is_interesting)
4079 svn_pool_destroy(iterpool);
4082 *prev_history_p = prev_history;
4083 return SVN_NO_ERROR;
4087 /* Set *PATH and *REVISION to the path and revision for the HISTORY
4088 object. Use POOL for all allocations. */
4089 static svn_error_t *
4090 fs_history_location(const char **path,
4091 svn_revnum_t *revision,
4092 svn_fs_history_t *history,
4095 fs_history_data_t *fhd = history->fsap_data;
4097 *path = apr_pstrdup(pool, fhd->path);
4098 *revision = fhd->revision;
4099 return SVN_NO_ERROR;
4102 static history_vtable_t history_vtable = {
4107 /* Return a new history object (marked as "interesting") for PATH and
4108 REVISION, allocated in POOL, and with its members set to the values
4109 of the parameters provided. Note that PATH and PATH_HINT get
4110 normalized and duplicated in POOL. */
4111 static svn_fs_history_t *
4112 assemble_history(svn_fs_t *fs,
4114 svn_revnum_t revision,
4115 svn_boolean_t is_interesting,
4116 const char *path_hint,
4117 svn_revnum_t rev_hint,
4118 svn_revnum_t next_copy,
4119 const svn_fs_id_t *current_id,
4122 svn_fs_history_t *history = apr_pcalloc(pool, sizeof(*history));
4123 fs_history_data_t *fhd = apr_pcalloc(pool, sizeof(*fhd));
4124 fhd->path = svn_fs__canonicalize_abspath(path, pool);
4125 fhd->revision = revision;
4126 fhd->is_interesting = is_interesting;
4127 fhd->path_hint = path_hint ? svn_fs__canonicalize_abspath(path_hint, pool)
4129 fhd->rev_hint = rev_hint;
4130 fhd->next_copy = next_copy;
4131 fhd->current_id = current_id ? svn_fs_fs__id_copy(current_id, pool) : NULL;
4134 history->vtable = &history_vtable;
4135 history->fsap_data = fhd;
4140 /* mergeinfo queries */
4143 /* DIR_DAG is a directory DAG node which has mergeinfo in its
4144 descendants. This function iterates over its children. For each
4145 child with immediate mergeinfo, call RECEIVER with it and BATON.
4146 For each child with descendants with mergeinfo, it recurses. Note
4147 that it does *not* call the action on the path for DIR_DAG itself.
4149 SCRATCH_POOL is used for temporary allocations, including the mergeinfo
4150 hashes passed to actions.
4152 static svn_error_t *
4153 crawl_directory_dag_for_mergeinfo(svn_fs_root_t *root,
4154 const char *this_path,
4155 dag_node_t *dir_dag,
4156 svn_fs_mergeinfo_receiver_t receiver,
4158 apr_pool_t *scratch_pool)
4160 apr_array_header_t *entries;
4162 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4164 SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, dir_dag, scratch_pool));
4165 for (i = 0; i < entries->nelts; ++i)
4167 svn_fs_dirent_t *dirent = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *);
4168 const char *kid_path;
4169 dag_node_t *kid_dag;
4170 svn_boolean_t has_mergeinfo, go_down;
4172 svn_pool_clear(iterpool);
4174 kid_path = svn_fspath__join(this_path, dirent->name, iterpool);
4175 SVN_ERR(get_dag(&kid_dag, root, kid_path, iterpool));
4177 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, kid_dag));
4178 SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down, kid_dag));
4182 /* Save this particular node's mergeinfo. */
4183 apr_hash_t *proplist;
4184 svn_mergeinfo_t kid_mergeinfo;
4185 svn_string_t *mergeinfo_string;
4188 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, kid_dag, iterpool));
4189 mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
4190 if (!mergeinfo_string)
4192 svn_string_t *idstr = svn_fs_fs__id_unparse(dirent->id, iterpool);
4193 return svn_error_createf
4194 (SVN_ERR_FS_CORRUPT, NULL,
4195 _("Node-revision #'%s' claims to have mergeinfo but doesn't"),
4199 /* Issue #3896: If a node has syntactically invalid mergeinfo, then
4200 treat it as if no mergeinfo is present rather than raising a parse
4202 err = svn_mergeinfo_parse(&kid_mergeinfo,
4203 mergeinfo_string->data,
4207 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
4208 svn_error_clear(err);
4210 return svn_error_trace(err);
4214 SVN_ERR(receiver(kid_path, kid_mergeinfo, baton, iterpool));
4219 SVN_ERR(crawl_directory_dag_for_mergeinfo(root,
4227 svn_pool_destroy(iterpool);
4228 return SVN_NO_ERROR;
4231 /* Return the cache key as a combination of REV_ROOT->REV, the inheritance
4232 flags INHERIT and ADJUST_INHERITED_MERGEINFO, and the PATH. The result
4233 will be allocated in POOL..
4236 mergeinfo_cache_key(const char *path,
4237 svn_fs_root_t *rev_root,
4238 svn_mergeinfo_inheritance_t inherit,
4239 svn_boolean_t adjust_inherited_mergeinfo,
4242 apr_int64_t number = rev_root->rev;
4244 + (inherit == svn_mergeinfo_nearest_ancestor ? 2 : 0)
4245 + (adjust_inherited_mergeinfo ? 1 : 0);
4247 return svn_fs_fs__combine_number_and_string(number, path, pool);
4250 /* Calculates the mergeinfo for PATH under REV_ROOT using inheritance
4251 type INHERIT. Returns it in *MERGEINFO, or NULL if there is none.
4252 The result is allocated in RESULT_POOL; SCRATCH_POOL is
4253 used for temporary allocations.
4255 static svn_error_t *
4256 get_mergeinfo_for_path_internal(svn_mergeinfo_t *mergeinfo,
4257 svn_fs_root_t *rev_root,
4259 svn_mergeinfo_inheritance_t inherit,
4260 svn_boolean_t adjust_inherited_mergeinfo,
4261 apr_pool_t *result_pool,
4262 apr_pool_t *scratch_pool)
4264 parent_path_t *parent_path, *nearest_ancestor;
4265 apr_hash_t *proplist;
4266 svn_string_t *mergeinfo_string;
4268 path = svn_fs__canonicalize_abspath(path, scratch_pool);
4270 SVN_ERR(open_path(&parent_path, rev_root, path, 0, FALSE, scratch_pool));
4272 if (inherit == svn_mergeinfo_nearest_ancestor && ! parent_path->parent)
4273 return SVN_NO_ERROR;
4275 if (inherit == svn_mergeinfo_nearest_ancestor)
4276 nearest_ancestor = parent_path->parent;
4278 nearest_ancestor = parent_path;
4282 svn_boolean_t has_mergeinfo;
4284 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo,
4285 nearest_ancestor->node));
4289 /* No need to loop if we're looking for explicit mergeinfo. */
4290 if (inherit == svn_mergeinfo_explicit)
4292 return SVN_NO_ERROR;
4295 nearest_ancestor = nearest_ancestor->parent;
4297 /* Run out? There's no mergeinfo. */
4298 if (!nearest_ancestor)
4300 return SVN_NO_ERROR;
4304 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, nearest_ancestor->node,
4306 mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
4307 if (!mergeinfo_string)
4308 return svn_error_createf
4309 (SVN_ERR_FS_CORRUPT, NULL,
4310 _("Node-revision '%s@%ld' claims to have mergeinfo but doesn't"),
4311 parent_path_path(nearest_ancestor, scratch_pool), rev_root->rev);
4313 /* Parse the mergeinfo; store the result in *MERGEINFO. */
4315 /* Issue #3896: If a node has syntactically invalid mergeinfo, then
4316 treat it as if no mergeinfo is present rather than raising a parse
4318 svn_error_t *err = svn_mergeinfo_parse(mergeinfo,
4319 mergeinfo_string->data,
4323 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
4325 svn_error_clear(err);
4329 return svn_error_trace(err);
4333 /* If our nearest ancestor is the very path we inquired about, we
4334 can return the mergeinfo results directly. Otherwise, we're
4335 inheriting the mergeinfo, so we need to a) remove non-inheritable
4336 ranges and b) telescope the merged-from paths. */
4337 if (adjust_inherited_mergeinfo && (nearest_ancestor != parent_path))
4339 svn_mergeinfo_t tmp_mergeinfo;
4341 SVN_ERR(svn_mergeinfo_inheritable2(&tmp_mergeinfo, *mergeinfo,
4342 NULL, SVN_INVALID_REVNUM,
4343 SVN_INVALID_REVNUM, TRUE,
4344 scratch_pool, scratch_pool));
4345 SVN_ERR(svn_fs__append_to_merged_froms(mergeinfo, tmp_mergeinfo,
4346 parent_path_relpath(
4347 parent_path, nearest_ancestor,
4352 return SVN_NO_ERROR;
4355 /* Caching wrapper around get_mergeinfo_for_path_internal().
4357 static svn_error_t *
4358 get_mergeinfo_for_path(svn_mergeinfo_t *mergeinfo,
4359 svn_fs_root_t *rev_root,
4361 svn_mergeinfo_inheritance_t inherit,
4362 svn_boolean_t adjust_inherited_mergeinfo,
4363 apr_pool_t *result_pool,
4364 apr_pool_t *scratch_pool)
4366 fs_fs_data_t *ffd = rev_root->fs->fsap_data;
4367 const char *cache_key;
4368 svn_boolean_t found = FALSE;
4369 svn_stringbuf_t *mergeinfo_exists;
4373 cache_key = mergeinfo_cache_key(path, rev_root, inherit,
4374 adjust_inherited_mergeinfo, scratch_pool);
4375 if (ffd->mergeinfo_existence_cache)
4377 SVN_ERR(svn_cache__get((void **)&mergeinfo_exists, &found,
4378 ffd->mergeinfo_existence_cache,
4379 cache_key, result_pool));
4380 if (found && mergeinfo_exists->data[0] == '1')
4381 SVN_ERR(svn_cache__get((void **)mergeinfo, &found,
4382 ffd->mergeinfo_cache,
4383 cache_key, result_pool));
4388 SVN_ERR(get_mergeinfo_for_path_internal(mergeinfo, rev_root, path,
4390 adjust_inherited_mergeinfo,
4391 result_pool, scratch_pool));
4392 if (ffd->mergeinfo_existence_cache)
4394 mergeinfo_exists = svn_stringbuf_create(*mergeinfo ? "1" : "0",
4396 SVN_ERR(svn_cache__set(ffd->mergeinfo_existence_cache,
4397 cache_key, mergeinfo_exists, scratch_pool));
4399 SVN_ERR(svn_cache__set(ffd->mergeinfo_cache,
4400 cache_key, *mergeinfo, scratch_pool));
4404 return SVN_NO_ERROR;
4407 /* Invoke RECEIVER with BATON for each mergeinfo found on descendants of
4408 PATH (but not PATH itself). Use SCRATCH_POOL for temporary values. */
4409 static svn_error_t *
4410 add_descendant_mergeinfo(svn_fs_root_t *root,
4412 svn_fs_mergeinfo_receiver_t receiver,
4414 apr_pool_t *scratch_pool)
4416 dag_node_t *this_dag;
4417 svn_boolean_t go_down;
4419 SVN_ERR(get_dag(&this_dag, root, path, scratch_pool));
4420 SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down,
4423 SVN_ERR(crawl_directory_dag_for_mergeinfo(root,
4429 return SVN_NO_ERROR;
4433 /* Find all the mergeinfo for a set of PATHS under ROOT and report it
4434 through RECEIVER with BATON. INHERITED, INCLUDE_DESCENDANTS and
4435 ADJUST_INHERITED_MERGEINFO are the same as in the FS API.
4437 Allocate temporary values are allocated in SCRATCH_POOL. */
4438 static svn_error_t *
4439 get_mergeinfos_for_paths(svn_fs_root_t *root,
4440 const apr_array_header_t *paths,
4441 svn_mergeinfo_inheritance_t inherit,
4442 svn_boolean_t include_descendants,
4443 svn_boolean_t adjust_inherited_mergeinfo,
4444 svn_fs_mergeinfo_receiver_t receiver,
4446 apr_pool_t *scratch_pool)
4448 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4451 for (i = 0; i < paths->nelts; i++)
4454 svn_mergeinfo_t path_mergeinfo;
4455 const char *path = APR_ARRAY_IDX(paths, i, const char *);
4457 svn_pool_clear(iterpool);
4459 err = get_mergeinfo_for_path(&path_mergeinfo, root, path,
4460 inherit, adjust_inherited_mergeinfo,
4461 iterpool, iterpool);
4464 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
4466 svn_error_clear(err);
4468 path_mergeinfo = NULL;
4472 return svn_error_trace(err);
4477 SVN_ERR(receiver(path, path_mergeinfo, baton, iterpool));
4478 if (include_descendants)
4479 SVN_ERR(add_descendant_mergeinfo(root, path, receiver, baton,
4482 svn_pool_destroy(iterpool);
4484 return SVN_NO_ERROR;
4488 /* Implements svn_fs_get_mergeinfo. */
4489 static svn_error_t *
4490 fs_get_mergeinfo(svn_fs_root_t *root,
4491 const apr_array_header_t *paths,
4492 svn_mergeinfo_inheritance_t inherit,
4493 svn_boolean_t include_descendants,
4494 svn_boolean_t adjust_inherited_mergeinfo,
4495 svn_fs_mergeinfo_receiver_t receiver,
4497 apr_pool_t *scratch_pool)
4499 fs_fs_data_t *ffd = root->fs->fsap_data;
4501 /* We require a revision root. */
4502 if (root->is_txn_root)
4503 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
4505 /* We have to actually be able to find the mergeinfo metadata! */
4506 if (! svn_fs_fs__fs_supports_mergeinfo(root->fs))
4507 return svn_error_createf
4508 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
4509 _("Querying mergeinfo requires version %d of the FSFS filesystem "
4510 "schema; filesystem '%s' uses only version %d"),
4511 SVN_FS_FS__MIN_MERGEINFO_FORMAT, root->fs->path, ffd->format);
4513 /* Retrieve a path -> mergeinfo hash mapping. */
4514 return get_mergeinfos_for_paths(root, paths, inherit,
4515 include_descendants,
4516 adjust_inherited_mergeinfo,
4522 /* The vtable associated with root objects. */
4523 static root_vtable_t root_vtable = {
4526 svn_fs_fs__check_path,
4530 svn_fs_fs__node_created_rev,
4532 fs_node_created_path,
4541 fs_change_node_prop,
4544 fs_dir_optimal_order,
4549 fs_try_process_file_contents,
4553 fs_contents_changed,
4554 fs_get_file_delta_stream,
4559 /* Construct a new root object in FS, allocated from POOL. */
4560 static svn_fs_root_t *
4561 make_root(svn_fs_t *fs,
4564 svn_fs_root_t *root = apr_pcalloc(pool, sizeof(*root));
4568 root->vtable = &root_vtable;
4574 /* Construct a root object referring to the root of REVISION in FS,
4575 whose root directory is ROOT_DIR. Create the new root in POOL. */
4576 static svn_fs_root_t *
4577 make_revision_root(svn_fs_t *fs,
4579 dag_node_t *root_dir,
4582 svn_fs_root_t *root = make_root(fs, pool);
4584 root->is_txn_root = FALSE;
4586 root->fsap_data = root_dir;
4592 /* Construct a root object referring to the root of the transaction
4593 named TXN and based on revision BASE_REV in FS, with FLAGS to
4594 describe transaction's behavior. Create the new root in POOL. */
4595 static svn_error_t *
4596 make_txn_root(svn_fs_root_t **root_p,
4598 const svn_fs_fs__id_part_t *txn,
4599 svn_revnum_t base_rev,
4603 svn_fs_root_t *root = make_root(fs, pool);
4604 fs_txn_root_data_t *frd = apr_pcalloc(root->pool, sizeof(*frd));
4607 root->is_txn_root = TRUE;
4608 root->txn = svn_fs_fs__id_txn_unparse(txn, root->pool);
4609 root->txn_flags = flags;
4610 root->rev = base_rev;
4612 /* Because this cache actually tries to invalidate elements, keep
4613 the number of elements per page down.
4615 Note that since dag_node_cache_invalidate uses svn_cache__iter,
4616 this *cannot* be a memcache-based cache. */
4617 SVN_ERR(svn_cache__create_inprocess(&(frd->txn_node_cache),
4618 svn_fs_fs__dag_serialize,
4619 svn_fs_fs__dag_deserialize,
4620 APR_HASH_KEY_STRING,
4622 apr_pstrcat(pool, txn, ":TXN",
4626 /* Initialize transaction-local caches in FS.
4628 Note that we cannot put those caches in frd because that content
4629 fs root object is not available where we would need it. */
4630 SVN_ERR(svn_fs_fs__initialize_txn_caches(fs, root->txn, root->pool));
4632 root->fsap_data = frd;
4635 return SVN_NO_ERROR;
4642 stringify_node(dag_node_t *node,
4645 /* ### TODO: print some PATH@REV to it, too. */
4646 return svn_fs_fs__id_unparse(svn_fs_fs__dag_get_id(node), pool)->data;
4649 /* Check metadata sanity on NODE, and on its children. Manually verify
4650 information for DAG nodes in revision REV, and trust the metadata
4651 accuracy for nodes belonging to older revisions. To detect cycles,
4652 provide all parent dag_node_t * in PARENT_NODES. */
4653 static svn_error_t *
4654 verify_node(dag_node_t *node,
4656 apr_array_header_t *parent_nodes,
4659 svn_boolean_t has_mergeinfo;
4660 apr_int64_t mergeinfo_count;
4661 const svn_fs_id_t *pred_id;
4662 svn_fs_t *fs = svn_fs_fs__dag_get_fs(node);
4664 svn_node_kind_t kind;
4665 apr_pool_t *iterpool = svn_pool_create(pool);
4668 /* Detect (non-)DAG cycles. */
4669 for (i = 0; i < parent_nodes->nelts; ++i)
4671 dag_node_t *parent = APR_ARRAY_IDX(parent_nodes, i, dag_node_t *);
4672 if (svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(parent),
4673 svn_fs_fs__dag_get_id(node)))
4674 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4675 "Node is its own direct or indirect parent '%s'",
4676 stringify_node(node, iterpool));
4679 /* Fetch some data. */
4680 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, node));
4681 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_count, node));
4682 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
4683 SVN_ERR(svn_fs_fs__dag_get_predecessor_count(&pred_count, node));
4684 kind = svn_fs_fs__dag_node_kind(node);
4687 if (mergeinfo_count < 0)
4688 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4689 "Negative mergeinfo-count %" APR_INT64_T_FMT
4691 mergeinfo_count, stringify_node(node, iterpool));
4693 /* Issue #4129. (This check will explicitly catch non-root instances too.) */
4697 int pred_pred_count;
4698 SVN_ERR(svn_fs_fs__dag_get_node(&pred, fs, pred_id, iterpool));
4699 SVN_ERR(svn_fs_fs__dag_get_predecessor_count(&pred_pred_count, pred));
4700 if (pred_pred_count+1 != pred_count)
4701 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4702 "Predecessor count mismatch: "
4703 "%s has %d, but %s has %d",
4704 stringify_node(node, iterpool), pred_count,
4705 stringify_node(pred, iterpool),
4709 /* Kind-dependent verifications. */
4710 if (kind == svn_node_none)
4712 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4713 "Node '%s' has kind 'none'",
4714 stringify_node(node, iterpool));
4716 if (kind == svn_node_file)
4718 if (has_mergeinfo != mergeinfo_count) /* comparing int to bool */
4719 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4720 "File node '%s' has inconsistent mergeinfo: "
4721 "has_mergeinfo=%d, "
4722 "mergeinfo_count=%" APR_INT64_T_FMT,
4723 stringify_node(node, iterpool),
4724 has_mergeinfo, mergeinfo_count);
4726 if (kind == svn_node_dir)
4728 apr_array_header_t *entries;
4729 apr_int64_t children_mergeinfo = 0;
4730 APR_ARRAY_PUSH(parent_nodes, dag_node_t*) = node;
4732 SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool));
4734 /* Compute CHILDREN_MERGEINFO. */
4735 for (i = 0; i < entries->nelts; ++i)
4737 svn_fs_dirent_t *dirent
4738 = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *);
4740 apr_int64_t child_mergeinfo;
4742 svn_pool_clear(iterpool);
4744 /* Compute CHILD_REV. */
4745 if (svn_fs_fs__id_rev(dirent->id) == rev)
4747 SVN_ERR(svn_fs_fs__dag_get_node(&child, fs, dirent->id,
4749 SVN_ERR(verify_node(child, rev, parent_nodes, iterpool));
4750 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&child_mergeinfo,
4755 /* access mergeinfo counter with minimal overhead */
4756 node_revision_t *noderev;
4757 SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, dirent->id,
4758 iterpool, iterpool));
4759 child_mergeinfo = noderev->mergeinfo_count;
4762 children_mergeinfo += child_mergeinfo;
4765 /* Side-effect of issue #4129. */
4766 if (children_mergeinfo+has_mergeinfo != mergeinfo_count)
4767 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4768 "Mergeinfo-count discrepancy on '%s': "
4769 "expected %" APR_INT64_T_FMT "+%d, "
4770 "counted %" APR_INT64_T_FMT,
4771 stringify_node(node, iterpool),
4772 mergeinfo_count, has_mergeinfo,
4773 children_mergeinfo);
4775 /* If we don't make it here, there was an error / corruption.
4776 * In that case, nobody will need PARENT_NODES anymore. */
4777 apr_array_pop(parent_nodes);
4780 svn_pool_destroy(iterpool);
4781 return SVN_NO_ERROR;
4785 svn_fs_fs__verify_root(svn_fs_root_t *root,
4788 svn_fs_t *fs = root->fs;
4789 dag_node_t *root_dir;
4790 apr_array_header_t *parent_nodes;
4792 /* Issue #4129: bogus pred-counts and minfo-cnt's on the root node-rev
4793 (and elsewhere). This code makes more thorough checks than the
4794 commit-time checks in validate_root_noderev(). */
4796 /* Callers should disable caches by setting SVN_FS_CONFIG_FSFS_CACHE_NS;
4799 When this code is called in the library, we want to ensure we
4800 use the on-disk data --- rather than some data that was read
4801 in the possibly-distance past and cached since. */
4803 if (root->is_txn_root)
4805 fs_txn_root_data_t *frd = root->fsap_data;
4806 SVN_ERR(svn_fs_fs__dag_txn_root(&root_dir, fs, &frd->txn_id, pool));
4810 root_dir = root->fsap_data;
4813 /* Recursively verify ROOT_DIR. */
4814 parent_nodes = apr_array_make(pool, 16, sizeof(dag_node_t *));
4815 SVN_ERR(verify_node(root_dir, root->rev, parent_nodes, pool));
4817 /* Verify explicitly the predecessor of the root. */
4819 const svn_fs_id_t *pred_id;
4821 /* Only r0 should have no predecessor. */
4822 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, root_dir));
4823 if (! root->is_txn_root && !!pred_id != !!root->rev)
4824 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4825 "r%ld's root node's predecessor is "
4826 "unexpectedly '%s'",
4829 ? svn_fs_fs__id_unparse(pred_id, pool)->data
4831 if (root->is_txn_root && !pred_id)
4832 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4833 "Transaction '%s''s root node's predecessor is "
4834 "unexpectedly NULL",
4837 /* Check the predecessor's revision. */
4840 svn_revnum_t pred_rev = svn_fs_fs__id_rev(pred_id);
4841 if (! root->is_txn_root && pred_rev+1 != root->rev)
4843 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4844 "r%ld's root node's predecessor is r%ld"
4845 " but should be r%ld",
4846 root->rev, pred_rev, root->rev - 1);
4847 if (root->is_txn_root && pred_rev != root->rev)
4848 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4849 "Transaction '%s''s root node's predecessor"
4851 " but should be r%ld",
4852 root->txn, pred_rev, root->rev);
4856 return SVN_NO_ERROR;