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"
60 #include "temp_serializer.h"
62 #include "private/svn_mergeinfo_private.h"
63 #include "private/svn_subr_private.h"
64 #include "private/svn_fs_util.h"
65 #include "private/svn_fspath.h"
66 #include "../libsvn_fs/fs-loader.h"
69 /* ### I believe this constant will become internal to reps-strings.c.
70 ### see the comment in window_consumer() for more information. */
72 /* ### the comment also seems to need tweaking: the log file stuff
73 ### is no longer an issue... */
74 /* Data written to the filesystem through the svn_fs_apply_textdelta()
75 interface is cached in memory until the end of the data stream, or
76 until a size trigger is hit. Define that trigger here (in bytes).
77 Setting the value to 0 will result in no filesystem buffering at
78 all. The value only really matters when dealing with file contents
79 bigger than the value itself. Above that point, large values here
80 allow the filesystem to buffer more data in memory before flushing
81 to the database, which increases memory usage but greatly decreases
82 the amount of disk access (and log-file generation) in database.
83 Smaller values will limit your overall memory consumption, but can
84 drastically hurt throughput by necessitating more write operations
85 to the database (which also generates more log-files). */
86 #define WRITE_BUFFER_SIZE 512000
90 /* The root structures.
92 Why do they contain different data? Well, transactions are mutable
93 enough that it isn't safe to cache the DAG node for the root
94 directory or the hash of copyfrom data: somebody else might modify
95 them concurrently on disk! (Why is the DAG node cache safer than
96 the root DAG node? When cloning transaction DAG nodes in and out
97 of the cache, all of the possibly-mutable data from the
98 node_revision_t inside the dag_node_t is dropped.) Additionally,
99 revisions are immutable enough that their DAG node cache can be
100 kept in the FS object and shared among multiple revision root
103 typedef struct fs_rev_root_data_t
105 /* A dag node for the revision's root directory. */
106 dag_node_t *root_dir;
108 /* Cache structure for mapping const char * PATH to const char
109 *COPYFROM_STRING, so that paths_changed can remember all the
110 copyfrom information in the changes file.
111 COPYFROM_STRING has the format "REV PATH", or is the empty string if
112 the path was added without history. */
113 apr_hash_t *copyfrom_cache;
115 } fs_rev_root_data_t;
117 typedef struct fs_txn_root_data_t
121 /* Cache of txn DAG nodes (without their nested noderevs, because
122 * it's mutable). Same keys/values as ffd->rev_node_cache. */
123 svn_cache__t *txn_node_cache;
124 } fs_txn_root_data_t;
126 /* Declared here to resolve the circular dependencies. */
127 static svn_error_t * get_dag(dag_node_t **dag_node_p,
130 svn_boolean_t needs_lock_cache,
133 static svn_fs_root_t *make_revision_root(svn_fs_t *fs, svn_revnum_t rev,
134 dag_node_t *root_dir,
137 static svn_error_t *make_txn_root(svn_fs_root_t **root_p,
138 svn_fs_t *fs, const char *txn,
139 svn_revnum_t base_rev, apr_uint32_t flags,
143 /*** Node Caching ***/
145 /* 1st level cache */
147 /* An entry in the first-level cache. REVISION and PATH form the key that
148 will ultimately be matched.
150 typedef struct cache_entry_t
152 /* hash value derived from PATH, REVISION.
153 Used to short-circuit failed lookups. */
154 apr_uint32_t hash_value;
156 /* revision to which the NODE belongs */
157 svn_revnum_t revision;
159 /* path of the NODE */
162 /* cached value of strlen(PATH). */
165 /* the node allocated in the cache's pool. NULL for empty entries. */
169 /* Number of entries in the cache. Keep this low to keep pressure on the
170 CPU caches low as well. A binary value is most efficient. If we walk
171 a directory tree, we want enough entries to store nodes for all files
172 without overwriting the nodes for the parent folder. That way, there
173 will be no unnecessary misses (except for a few random ones caused by
176 The actual number of instances may be higher but entries that got
177 overwritten are no longer visible.
179 enum { BUCKET_COUNT = 256 };
181 /* Each pool that has received a DAG node, will hold at least on lock on
182 our cache to ensure that the node remains valid despite being allocated
183 in the cache's pool. This is the structure to represent the lock.
185 typedef struct cache_lock_t
187 /* pool holding the lock */
190 /* cache being locked */
191 fs_fs_dag_cache_t *cache;
193 /* next lock. NULL at EOL */
194 struct cache_lock_t *next;
196 /* previous lock. NULL at list head. Only then this==cache->first_lock */
197 struct cache_lock_t *prev;
200 /* The actual cache structure. All nodes will be allocated in POOL.
201 When the number of INSERTIONS (i.e. objects created form that pool)
202 exceeds a certain threshold, the pool will be cleared and the cache
205 To ensure that nodes returned from this structure remain valid, the
206 cache will get locked for the lifetime of the _receiving_ pools (i.e.
207 those in which we would allocate the node if there was no cache.).
208 The cache will only be cleared FIRST_LOCK is 0.
210 struct fs_fs_dag_cache_t
212 /* fixed number of (possibly empty) cache entries */
213 cache_entry_t buckets[BUCKET_COUNT];
215 /* pool used for all node allocation */
218 /* number of entries created from POOL since the last cleanup */
219 apr_size_t insertions;
221 /* Property lookups etc. have a very high locality (75% re-hit).
222 Thus, remember the last hit location for optimistic lookup. */
225 /* List of receiving pools that are still alive. */
226 cache_lock_t *first_lock;
229 /* Cleanup function to be called when a receiving pool gets cleared.
230 Unlocks the cache once.
233 unlock_cache(void *baton_void)
235 cache_lock_t *lock = baton_void;
237 /* remove lock from chain. Update the head */
239 lock->next->prev = lock->prev;
241 lock->prev->next = lock->next;
243 lock->cache->first_lock = lock->next;
248 /* Cleanup function to be called when the cache itself gets destroyed.
249 In that case, we must unregister all unlock requests.
252 unregister_locks(void *baton_void)
254 fs_fs_dag_cache_t *cache = baton_void;
257 for (lock = cache->first_lock; lock; lock = lock->next)
258 apr_pool_cleanup_kill(lock->pool,
266 svn_fs_fs__create_dag_cache(apr_pool_t *pool)
268 fs_fs_dag_cache_t *result = apr_pcalloc(pool, sizeof(*result));
269 result->pool = svn_pool_create(pool);
271 apr_pool_cleanup_register(pool,
274 apr_pool_cleanup_null);
279 /* Prevent the entries in CACHE from being destroyed, for as long as the
283 lock_cache(fs_fs_dag_cache_t* cache, apr_pool_t *pool)
285 /* we only need to lock / unlock once per pool. Since we will often ask
286 for multiple nodes with the same pool, we can reduce the overhead.
287 However, if e.g. pools are being used in an alternating pattern,
288 we may lock the cache more than once for the same pool (and register
289 just as many cleanup actions).
291 cache_lock_t *lock = cache->first_lock;
293 /* try to find an existing lock for POOL.
294 But limit the time spent on chasing pointers. */
296 while (lock && --limiter)
297 if (lock->pool == pool)
300 /* create a new lock and put it at the beginning of the lock chain */
301 lock = apr_palloc(pool, sizeof(*lock));
304 lock->next = cache->first_lock;
307 if (cache->first_lock)
308 cache->first_lock->prev = lock;
309 cache->first_lock = lock;
311 /* instruct POOL to remove the look upon cleanup */
312 apr_pool_cleanup_register(pool,
315 apr_pool_cleanup_null);
318 /* Clears the CACHE at regular intervals (destroying all cached nodes)
321 auto_clear_dag_cache(fs_fs_dag_cache_t* cache)
323 if (cache->first_lock == NULL && cache->insertions > BUCKET_COUNT)
325 svn_pool_clear(cache->pool);
327 memset(cache->buckets, 0, sizeof(cache->buckets));
328 cache->insertions = 0;
332 /* For the given REVISION and PATH, return the respective entry in CACHE.
333 If the entry is empty, its NODE member will be NULL and the caller
334 may then set it to the corresponding DAG node allocated in CACHE->POOL.
336 static cache_entry_t *
337 cache_lookup( fs_fs_dag_cache_t *cache
338 , svn_revnum_t revision
341 apr_size_t i, bucket_index;
342 apr_size_t path_len = strlen(path);
343 apr_uint32_t hash_value = (apr_uint32_t)revision;
345 #if SVN_UNALIGNED_ACCESS_IS_OK
346 /* "randomizing" / distributing factor used in our hash function */
347 const apr_uint32_t factor = 0xd1f3da69;
350 /* optimistic lookup: hit the same bucket again? */
351 cache_entry_t *result = &cache->buckets[cache->last_hit];
352 if ( (result->revision == revision)
353 && (result->path_len == path_len)
354 && !memcmp(result->path, path, path_len))
359 /* need to do a full lookup. Calculate the hash value
360 (HASH_VALUE has been initialized to REVISION). */
362 #if SVN_UNALIGNED_ACCESS_IS_OK
363 /* We relax the dependency chain between iterations by processing
364 two chunks from the input per hash_value self-multiplication.
365 The HASH_VALUE update latency is now 1 MUL latency + 1 ADD latency
366 per 2 chunks instead of 1 chunk.
368 for (; i + 8 <= path_len; i += 8)
369 hash_value = hash_value * factor * factor
370 + ( *(const apr_uint32_t*)(path + i) * factor
371 + *(const apr_uint32_t*)(path + i + 4));
374 for (; i < path_len; ++i)
375 /* Help GCC to minimize the HASH_VALUE update latency by splitting the
376 MUL 33 of the naive implementation: h = h * 33 + path[i]. This
377 shortens the dependency chain from 1 shift + 2 ADDs to 1 shift + 1 ADD.
379 hash_value = hash_value * 32 + (hash_value + (unsigned char)path[i]);
381 bucket_index = hash_value + (hash_value >> 16);
382 bucket_index = (bucket_index + (bucket_index >> 8)) % BUCKET_COUNT;
384 /* access the corresponding bucket and remember its location */
385 result = &cache->buckets[bucket_index];
386 cache->last_hit = bucket_index;
388 /* if it is *NOT* a match, clear the bucket, expect the caller to fill
389 in the node and count it as an insertion */
390 if ( (result->hash_value != hash_value)
391 || (result->revision != revision)
392 || (result->path_len != path_len)
393 || memcmp(result->path, path, path_len))
395 result->hash_value = hash_value;
396 result->revision = revision;
397 if (result->path_len < path_len)
398 result->path = apr_palloc(cache->pool, path_len + 1);
399 result->path_len = path_len;
400 memcpy(result->path, path, path_len + 1);
410 /* 2nd level cache */
412 /* Find and return the DAG node cache for ROOT and the key that
413 should be used for PATH. */
415 locate_cache(svn_cache__t **cache,
421 if (root->is_txn_root)
423 fs_txn_root_data_t *frd = root->fsap_data;
424 if (cache) *cache = frd->txn_node_cache;
425 if (key && path) *key = path;
429 fs_fs_data_t *ffd = root->fs->fsap_data;
430 if (cache) *cache = ffd->rev_node_cache;
431 if (key && path) *key
432 = svn_fs_fs__combine_number_and_string(root->rev, path, pool);
436 /* Return NODE for PATH from ROOT's node cache, or NULL if the node
437 isn't cached; read it from the FS. *NODE remains valid until either
438 POOL or the FS gets cleared or destroyed (whichever comes first).
440 Since locking can be expensive and POOL may be long-living, for
441 nodes that will not need to survive the next call to this function,
442 set NEEDS_LOCK_CACHE to FALSE. */
444 dag_node_cache_get(dag_node_t **node_p,
447 svn_boolean_t needs_lock_cache,
451 dag_node_t *node = NULL;
455 SVN_ERR_ASSERT(*path == '/');
457 if (!root->is_txn_root)
459 /* immutable DAG node. use the global caches for it */
461 fs_fs_data_t *ffd = root->fs->fsap_data;
462 cache_entry_t *bucket;
464 auto_clear_dag_cache(ffd->dag_node_cache);
465 bucket = cache_lookup(ffd->dag_node_cache, root->rev, path);
466 if (bucket->node == NULL)
468 locate_cache(&cache, &key, root, path, pool);
469 SVN_ERR(svn_cache__get((void **)&node, &found, cache, key,
470 ffd->dag_node_cache->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);
484 /* if we found a node, make sure it remains valid at least as long
485 as it would when allocated in POOL. */
486 if (node && needs_lock_cache)
487 lock_cache(ffd->dag_node_cache, pool);
491 /* DAG is mutable / may become invalid. Use the TXN-local cache */
493 locate_cache(&cache, &key, root, path, pool);
495 SVN_ERR(svn_cache__get((void **) &node, &found, cache, key, pool));
498 /* Patch up the FS, since this might have come from an old FS
500 svn_fs_fs__dag_set_fs(node, root->fs);
510 /* Add the NODE for PATH to ROOT's node cache. */
512 dag_node_cache_set(svn_fs_root_t *root,
520 SVN_ERR_ASSERT(*path == '/');
522 /* Do *not* attempt to dup and put the node into L1.
523 * dup() is twice as expensive as an L2 lookup (which will set also L1).
525 locate_cache(&cache, &key, root, path, pool);
527 return svn_cache__set(cache, key, node, pool);
531 /* Baton for find_descendents_in_cache. */
534 apr_array_header_t *list;
538 /* If the given item is a descendent of BATON->PATH, push
539 * it onto BATON->LIST (copying into BATON->POOL). Implements
540 * the svn_iter_apr_hash_cb_t prototype. */
542 find_descendents_in_cache(void *baton,
548 struct fdic_baton *b = baton;
549 const char *item_path = key;
551 if (svn_fspath__skip_ancestor(b->path, item_path))
552 APR_ARRAY_PUSH(b->list, const char *) = apr_pstrdup(b->pool, item_path);
557 /* Invalidate cache entries for PATH and any of its children. This
558 should *only* be called on a transaction root! */
560 dag_node_cache_invalidate(svn_fs_root_t *root,
566 apr_pool_t *iterpool;
570 b.pool = svn_pool_create(pool);
571 b.list = apr_array_make(b.pool, 1, sizeof(const char *));
573 SVN_ERR_ASSERT(root->is_txn_root);
574 locate_cache(&cache, NULL, root, NULL, b.pool);
577 SVN_ERR(svn_cache__iter(NULL, cache, find_descendents_in_cache,
580 iterpool = svn_pool_create(b.pool);
582 for (i = 0; i < b.list->nelts; i++)
584 const char *descendent = APR_ARRAY_IDX(b.list, i, const char *);
585 svn_pool_clear(iterpool);
586 SVN_ERR(svn_cache__set(cache, descendent, NULL, iterpool));
589 svn_pool_destroy(iterpool);
590 svn_pool_destroy(b.pool);
596 /* Creating transaction and revision root nodes. */
599 svn_fs_fs__txn_root(svn_fs_root_t **root_p,
603 apr_uint32_t flags = 0;
604 apr_hash_t *txnprops;
606 /* Look for the temporary txn props representing 'flags'. */
607 SVN_ERR(svn_fs_fs__txn_proplist(&txnprops, txn, pool));
610 if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD))
611 flags |= SVN_FS_TXN_CHECK_OOD;
613 if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS))
614 flags |= SVN_FS_TXN_CHECK_LOCKS;
617 return make_txn_root(root_p, txn->fs, txn->id, txn->base_rev, flags, pool);
622 svn_fs_fs__revision_root(svn_fs_root_t **root_p,
627 dag_node_t *root_dir;
629 SVN_ERR(svn_fs__check_fs(fs, TRUE));
631 SVN_ERR(svn_fs_fs__dag_revision_root(&root_dir, fs, rev, pool));
633 *root_p = make_revision_root(fs, rev, root_dir, pool);
640 /* Getting dag nodes for roots. */
643 /* Set *NODE_P to a freshly opened dag node referring to the root
644 directory of ROOT, allocating from POOL. */
646 root_node(dag_node_t **node_p,
650 if (root->is_txn_root)
652 /* It's a transaction root. Open a fresh copy. */
653 return svn_fs_fs__dag_txn_root(node_p, root->fs, root->txn, pool);
657 /* It's a revision root, so we already have its root directory
659 fs_rev_root_data_t *frd = root->fsap_data;
660 *node_p = svn_fs_fs__dag_dup(frd->root_dir, pool);
666 /* Set *NODE_P to a mutable root directory for ROOT, cloning if
667 necessary, allocating in POOL. ROOT must be a transaction root.
668 Use ERROR_PATH in error messages. */
670 mutable_root_node(dag_node_t **node_p,
672 const char *error_path,
675 if (root->is_txn_root)
676 return svn_fs_fs__dag_clone_root(node_p, root->fs, root->txn, pool);
678 /* If it's not a transaction root, we can't change its contents. */
679 return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path);
684 /* Traversing directory paths. */
686 typedef enum copy_id_inherit_t
688 copy_id_inherit_unknown = 0,
689 copy_id_inherit_self,
690 copy_id_inherit_parent,
695 /* A linked list representing the path from a node up to a root
696 directory. We use this for cloning, and for operations that need
697 to deal with both a node and its parent directory. For example, a
698 `delete' operation needs to know that the node actually exists, but
699 also needs to change the parent directory. */
700 typedef struct parent_path_t
703 /* A node along the path. This could be the final node, one of its
704 parents, or the root. Every parent path ends with an element for
705 the root directory. */
708 /* The name NODE has in its parent directory. This is zero for the
709 root directory, which (obviously) has no name in its parent. */
712 /* The parent of NODE, or zero if NODE is the root directory. */
713 struct parent_path_t *parent;
715 /* The copy ID inheritance style. */
716 copy_id_inherit_t copy_inherit;
718 /* If copy ID inheritance style is copy_id_inherit_new, this is the
719 path which should be implicitly copied; otherwise, this is NULL. */
720 const char *copy_src_path;
724 /* Return a text string describing the absolute path of parent_path
725 PARENT_PATH. It will be allocated in POOL. */
727 parent_path_path(parent_path_t *parent_path,
730 const char *path_so_far = "/";
731 if (parent_path->parent)
732 path_so_far = parent_path_path(parent_path->parent, pool);
733 return parent_path->entry
734 ? svn_fspath__join(path_so_far, parent_path->entry, pool)
739 /* Return the FS path for the parent path chain object CHILD relative
740 to its ANCESTOR in the same chain, allocated in POOL. */
742 parent_path_relpath(parent_path_t *child,
743 parent_path_t *ancestor,
746 const char *path_so_far = "";
747 parent_path_t *this_node = child;
748 while (this_node != ancestor)
750 assert(this_node != NULL);
751 path_so_far = svn_relpath_join(this_node->entry, path_so_far, pool);
752 this_node = this_node->parent;
759 /* Choose a copy ID inheritance method *INHERIT_P to be used in the
760 event that immutable node CHILD in FS needs to be made mutable. If
761 the inheritance method is copy_id_inherit_new, also return a
762 *COPY_SRC_PATH on which to base the new copy ID (else return NULL
763 for that path). CHILD must have a parent (it cannot be the root
764 node). TXN_ID is the transaction in which these items might be
765 mutable. Allocations are taken from POOL. */
767 get_copy_inheritance(copy_id_inherit_t *inherit_p,
768 const char **copy_src_path,
770 parent_path_t *child,
774 const svn_fs_id_t *child_id, *parent_id, *copyroot_id;
775 const char *child_copy_id, *parent_copy_id;
776 const char *id_path = NULL;
777 svn_fs_root_t *copyroot_root;
778 dag_node_t *copyroot_node;
779 svn_revnum_t copyroot_rev;
780 const char *copyroot_path;
782 SVN_ERR_ASSERT(child && child->parent && txn_id);
784 /* Initialize some convenience variables. */
785 child_id = svn_fs_fs__dag_get_id(child->node);
786 parent_id = svn_fs_fs__dag_get_id(child->parent->node);
787 child_copy_id = svn_fs_fs__id_copy_id(child_id);
788 parent_copy_id = svn_fs_fs__id_copy_id(parent_id);
790 /* If this child is already mutable, we have nothing to do. */
791 if (svn_fs_fs__id_txn_id(child_id))
793 *inherit_p = copy_id_inherit_self;
794 *copy_src_path = NULL;
798 /* From this point on, we'll assume that the child will just take
799 its copy ID from its parent. */
800 *inherit_p = copy_id_inherit_parent;
801 *copy_src_path = NULL;
803 /* Special case: if the child's copy ID is '0', use the parent's
805 if (strcmp(child_copy_id, "0") == 0)
808 /* Compare the copy IDs of the child and its parent. If they are
809 the same, then the child is already on the same branch as the
810 parent, and should use the same mutability copy ID that the
812 if (svn_fs_fs__key_compare(child_copy_id, parent_copy_id) == 0)
815 /* If the child is on the same branch that the parent is on, the
816 child should just use the same copy ID that the parent would use.
817 Else, the child needs to generate a new copy ID to use should it
818 need to be made mutable. We will claim that child is on the same
819 branch as its parent if the child itself is not a branch point,
820 or if it is a branch point that we are accessing via its original
821 copy destination path. */
822 SVN_ERR(svn_fs_fs__dag_get_copyroot(©root_rev, ©root_path,
824 SVN_ERR(svn_fs_fs__revision_root(©root_root, fs, copyroot_rev, pool));
825 SVN_ERR(get_dag(©root_node, copyroot_root, copyroot_path, FALSE, pool));
826 copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
828 if (svn_fs_fs__id_compare(copyroot_id, child_id) == -1)
831 /* Determine if we are looking at the child via its original path or
832 as a subtree item of a copied tree. */
833 id_path = svn_fs_fs__dag_get_created_path(child->node);
834 if (strcmp(id_path, parent_path_path(child, pool)) == 0)
836 *inherit_p = copy_id_inherit_self;
840 /* We are pretty sure that the child node is an unedited nested
841 branched node. When it needs to be made mutable, it should claim
843 *inherit_p = copy_id_inherit_new;
844 *copy_src_path = id_path;
848 /* Allocate a new parent_path_t node from POOL, referring to NODE,
849 ENTRY, PARENT, and COPY_ID. */
850 static parent_path_t *
851 make_parent_path(dag_node_t *node,
853 parent_path_t *parent,
856 parent_path_t *parent_path = apr_pcalloc(pool, sizeof(*parent_path));
857 parent_path->node = node;
858 parent_path->entry = entry;
859 parent_path->parent = parent;
860 parent_path->copy_inherit = copy_id_inherit_unknown;
861 parent_path->copy_src_path = NULL;
866 /* Flags for open_path. */
867 typedef enum open_path_flags_t {
869 /* The last component of the PATH need not exist. (All parent
870 directories must exist, as usual.) If the last component doesn't
871 exist, simply leave the `node' member of the bottom parent_path
873 open_path_last_optional = 1,
875 /* When this flag is set, don't bother to lookup the DAG node in
876 our caches because we already tried this. Ignoring this flag
877 has no functional impact. */
878 open_path_uncached = 2,
880 /* The caller does not care about the parent node chain but only
881 the final DAG node. */
882 open_path_node_only = 4
886 /* Open the node identified by PATH in ROOT, allocating in POOL. Set
887 *PARENT_PATH_P to a path from the node up to ROOT. The resulting
888 **PARENT_PATH_P value is guaranteed to contain at least one
889 *element, for the root directory. PATH must be in canonical form.
891 If resulting *PARENT_PATH_P will eventually be made mutable and
892 modified, or if copy ID inheritance information is otherwise
893 needed, TXN_ID should be the ID of the mutability transaction. If
894 TXN_ID is NULL, no copy ID inheritance information will be
895 calculated for the *PARENT_PATH_P chain.
897 If FLAGS & open_path_last_optional is zero, return the error
898 SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist. If
899 non-zero, require all the parent directories to exist as normal,
900 but if the final path component doesn't exist, simply return a path
901 whose bottom `node' member is zero. This option is useful for
902 callers that create new nodes --- we find the parent directory for
903 them, and tell them whether the entry exists already.
905 The remaining bits in FLAGS are hints that allow this function
906 to take shortcuts based on knowledge that the caller provides,
907 such as the caller is not actually being interested in PARENT_PATH_P,
908 but only in (*PARENT_PATH_P)->NODE.
910 NOTE: Public interfaces which only *read* from the filesystem
911 should not call this function directly, but should instead use
915 open_path(parent_path_t **parent_path_p,
922 svn_fs_t *fs = root->fs;
923 dag_node_t *here = NULL; /* The directory we're currently looking at. */
924 parent_path_t *parent_path; /* The path from HERE up to the root. */
925 const char *rest; /* The portion of PATH we haven't traversed yet. */
927 /* ensure a canonical path representation */
928 const char *path_so_far = "/";
929 apr_pool_t *iterpool = svn_pool_create(pool);
931 /* callers often traverse the tree in some path-based order. That means
932 a sibling of PATH has been presently accessed. Try to start the lookup
933 directly at the parent node, if the caller did not requested the full
935 const char *directory;
936 assert(svn_fs__is_canonical_abspath(path));
937 if (flags & open_path_node_only)
939 directory = svn_dirent_dirname(path, pool);
940 if (directory[1] != 0) /* root nodes are covered anyway */
941 SVN_ERR(dag_node_cache_get(&here, root, directory, TRUE, pool));
944 /* did the shortcut work? */
947 path_so_far = directory;
948 rest = path + strlen(directory) + 1;
952 /* Make a parent_path item for the root node, using its own current
954 SVN_ERR(root_node(&here, root, pool));
955 rest = path + 1; /* skip the leading '/', it saves in iteration */
958 parent_path = make_parent_path(here, 0, 0, pool);
959 parent_path->copy_inherit = copy_id_inherit_self;
961 /* Whenever we are at the top of this loop:
962 - HERE is our current directory,
963 - ID is the node revision ID of HERE,
964 - REST is the path we're going to find in HERE, and
965 - PARENT_PATH includes HERE and all its parents. */
972 svn_pool_clear(iterpool);
974 /* Parse out the next entry from the path. */
975 entry = svn_fs__next_entry_name(&next, rest, pool);
977 /* Calculate the path traversed thus far. */
978 path_so_far = svn_fspath__join(path_so_far, entry, pool);
982 /* Given the behavior of svn_fs__next_entry_name(), this
983 happens when the path either starts or ends with a slash.
984 In either case, we stay put: the current directory stays
985 the same, and we add nothing to the parent path. */
990 copy_id_inherit_t inherit;
991 const char *copy_path = NULL;
992 svn_error_t *err = SVN_NO_ERROR;
993 dag_node_t *cached_node = NULL;
995 /* If we found a directory entry, follow it. First, we
996 check our node cache, and, failing that, we hit the DAG
997 layer. Don't bother to contact the cache for the last
998 element if we already know the lookup to fail for the
1000 if (next || !(flags & open_path_uncached))
1001 SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far,
1004 child = cached_node;
1006 err = svn_fs_fs__dag_open(&child, here, entry, pool, iterpool);
1008 /* "file not found" requires special handling. */
1009 if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
1011 /* If this was the last path component, and the caller
1012 said it was optional, then don't return an error;
1013 just put a NULL node pointer in the path. */
1015 svn_error_clear(err);
1017 if ((flags & open_path_last_optional)
1018 && (! next || *next == '\0'))
1020 parent_path = make_parent_path(NULL, entry, parent_path,
1026 /* Build a better error message than svn_fs_fs__dag_open
1027 can provide, giving the root and full path name. */
1028 return SVN_FS__NOT_FOUND(root, path);
1032 /* Other errors we return normally. */
1035 if (flags & open_path_node_only)
1037 /* Shortcut: the caller only wan'ts the final DAG node. */
1038 parent_path->node = child;
1042 /* Now, make a parent_path item for CHILD. */
1043 parent_path = make_parent_path(child, entry, parent_path, pool);
1046 SVN_ERR(get_copy_inheritance(&inherit, ©_path, fs,
1047 parent_path, txn_id, iterpool));
1048 parent_path->copy_inherit = inherit;
1049 parent_path->copy_src_path = apr_pstrdup(pool, copy_path);
1053 /* Cache the node we found (if it wasn't already cached). */
1055 SVN_ERR(dag_node_cache_set(root, path_so_far, child, iterpool));
1058 /* Are we finished traversing the path? */
1062 /* The path isn't finished yet; we'd better be in a directory. */
1063 if (svn_fs_fs__dag_node_kind(child) != svn_node_dir)
1064 SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far),
1065 apr_psprintf(iterpool, _("Failure opening '%s'"), path));
1071 svn_pool_destroy(iterpool);
1072 *parent_path_p = parent_path;
1073 return SVN_NO_ERROR;
1077 /* Make the node referred to by PARENT_PATH mutable, if it isn't
1078 already, allocating from POOL. ROOT must be the root from which
1079 PARENT_PATH descends. Clone any parent directories as needed.
1080 Adjust the dag nodes in PARENT_PATH to refer to the clones. Use
1081 ERROR_PATH in error messages. */
1082 static svn_error_t *
1083 make_path_mutable(svn_fs_root_t *root,
1084 parent_path_t *parent_path,
1085 const char *error_path,
1089 const char *txn_id = root->txn;
1091 /* Is the node mutable already? */
1092 if (svn_fs_fs__dag_check_mutable(parent_path->node))
1093 return SVN_NO_ERROR;
1095 /* Are we trying to clone the root, or somebody's child node? */
1096 if (parent_path->parent)
1098 const svn_fs_id_t *parent_id, *child_id, *copyroot_id;
1099 const char *copy_id = NULL;
1100 copy_id_inherit_t inherit = parent_path->copy_inherit;
1101 const char *clone_path, *copyroot_path;
1102 svn_revnum_t copyroot_rev;
1103 svn_boolean_t is_parent_copyroot = FALSE;
1104 svn_fs_root_t *copyroot_root;
1105 dag_node_t *copyroot_node;
1107 /* We're trying to clone somebody's child. Make sure our parent
1109 SVN_ERR(make_path_mutable(root, parent_path->parent,
1114 case copy_id_inherit_parent:
1115 parent_id = svn_fs_fs__dag_get_id(parent_path->parent->node);
1116 copy_id = svn_fs_fs__id_copy_id(parent_id);
1119 case copy_id_inherit_new:
1120 SVN_ERR(svn_fs_fs__reserve_copy_id(©_id, root->fs, txn_id,
1124 case copy_id_inherit_self:
1128 case copy_id_inherit_unknown:
1130 SVN_ERR_MALFUNCTION(); /* uh-oh -- somebody didn't calculate copy-ID
1131 inheritance data. */
1134 /* Determine what copyroot our new child node should use. */
1135 SVN_ERR(svn_fs_fs__dag_get_copyroot(©root_rev, ©root_path,
1136 parent_path->node));
1137 SVN_ERR(svn_fs_fs__revision_root(©root_root, root->fs,
1138 copyroot_rev, pool));
1139 SVN_ERR(get_dag(©root_node, copyroot_root, copyroot_path,
1142 child_id = svn_fs_fs__dag_get_id(parent_path->node);
1143 copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
1144 if (strcmp(svn_fs_fs__id_node_id(child_id),
1145 svn_fs_fs__id_node_id(copyroot_id)) != 0)
1146 is_parent_copyroot = TRUE;
1148 /* Now make this node mutable. */
1149 clone_path = parent_path_path(parent_path->parent, pool);
1150 SVN_ERR(svn_fs_fs__dag_clone_child(&clone,
1151 parent_path->parent->node,
1158 /* Update the path cache. */
1159 SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool),
1164 /* We're trying to clone the root directory. */
1165 SVN_ERR(mutable_root_node(&clone, root, error_path, pool));
1168 /* Update the PARENT_PATH link to refer to the clone. */
1169 parent_path->node = clone;
1171 return SVN_NO_ERROR;
1175 /* Open the node identified by PATH in ROOT. Set DAG_NODE_P to the
1176 node we find, allocated in POOL. Return the error
1177 SVN_ERR_FS_NOT_FOUND if this node doesn't exist.
1179 Since locking can be expensive and POOL may be long-living, for
1180 nodes that will not need to survive the next call to this function,
1181 set NEEDS_LOCK_CACHE to FALSE. */
1182 static svn_error_t *
1183 get_dag(dag_node_t **dag_node_p,
1184 svn_fs_root_t *root,
1186 svn_boolean_t needs_lock_cache,
1189 parent_path_t *parent_path;
1190 dag_node_t *node = NULL;
1192 /* First we look for the DAG in our cache
1193 (if the path may be canonical). */
1195 SVN_ERR(dag_node_cache_get(&node, root, path, needs_lock_cache, pool));
1199 /* Canonicalize the input PATH. */
1200 if (! svn_fs__is_canonical_abspath(path))
1202 path = svn_fs__canonicalize_abspath(path, pool);
1204 /* Try again with the corrected path. */
1205 SVN_ERR(dag_node_cache_get(&node, root, path, needs_lock_cache,
1211 /* Call open_path with no flags, as we want this to return an
1212 * error if the node for which we are searching doesn't exist. */
1213 SVN_ERR(open_path(&parent_path, root, path,
1214 open_path_uncached | open_path_node_only,
1216 node = parent_path->node;
1218 /* No need to cache our find -- open_path() will do that for us. */
1223 return SVN_NO_ERROR;
1228 /* Populating the `changes' table. */
1230 /* Add a change to the changes table in FS, keyed on transaction id
1231 TXN_ID, and indicated that a change of kind CHANGE_KIND occurred on
1232 PATH (whose node revision id is--or was, in the case of a
1233 deletion--NODEREV_ID), and optionally that TEXT_MODs or PROP_MODs
1234 occurred. If the change resulted from a copy, COPYFROM_REV and
1235 COPYFROM_PATH specify under which revision and path the node was
1236 copied from. If this was not part of a copy, COPYFROM_REV should
1237 be SVN_INVALID_REVNUM. Do all this as part of POOL. */
1238 static svn_error_t *
1239 add_change(svn_fs_t *fs,
1242 const svn_fs_id_t *noderev_id,
1243 svn_fs_path_change_kind_t change_kind,
1244 svn_boolean_t text_mod,
1245 svn_boolean_t prop_mod,
1246 svn_node_kind_t node_kind,
1247 svn_revnum_t copyfrom_rev,
1248 const char *copyfrom_path,
1251 return svn_fs_fs__add_change(fs, txn_id,
1252 svn_fs__canonicalize_abspath(path, pool),
1253 noderev_id, change_kind, text_mod, prop_mod,
1254 node_kind, copyfrom_rev, copyfrom_path,
1260 /* Generic node operations. */
1262 /* Get the id of a node referenced by path PATH in ROOT. Return the
1263 id in *ID_P allocated in POOL. */
1265 svn_fs_fs__node_id(const svn_fs_id_t **id_p,
1266 svn_fs_root_t *root,
1270 if ((! root->is_txn_root)
1271 && (path[0] == '\0' || ((path[0] == '/') && (path[1] == '\0'))))
1273 /* Optimize the case where we don't need any db access at all.
1274 The root directory ("" or "/") node is stored in the
1275 svn_fs_root_t object, and never changes when it's a revision
1276 root, so we can just reach in and grab it directly. */
1277 fs_rev_root_data_t *frd = root->fsap_data;
1278 *id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(frd->root_dir), pool);
1284 SVN_ERR(get_dag(&node, root, path, FALSE, pool));
1285 *id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(node), pool);
1287 return SVN_NO_ERROR;
1292 svn_fs_fs__node_created_rev(svn_revnum_t *revision,
1293 svn_fs_root_t *root,
1299 SVN_ERR(get_dag(&node, root, path, FALSE, pool));
1300 return svn_fs_fs__dag_get_revision(revision, node, pool);
1304 /* Set *CREATED_PATH to the path at which PATH under ROOT was created.
1305 Return a string allocated in POOL. */
1306 static svn_error_t *
1307 fs_node_created_path(const char **created_path,
1308 svn_fs_root_t *root,
1314 SVN_ERR(get_dag(&node, root, path, TRUE, pool));
1315 *created_path = svn_fs_fs__dag_get_created_path(node);
1317 return SVN_NO_ERROR;
1321 /* Set *KIND_P to the type of node located at PATH under ROOT.
1322 Perform temporary allocations in POOL. */
1323 static svn_error_t *
1324 node_kind(svn_node_kind_t *kind_p,
1325 svn_fs_root_t *root,
1329 const svn_fs_id_t *node_id;
1332 /* Get the node id. */
1333 SVN_ERR(svn_fs_fs__node_id(&node_id, root, path, pool));
1335 /* Use the node id to get the real kind. */
1336 SVN_ERR(svn_fs_fs__dag_get_node(&node, root->fs, node_id, pool));
1337 *kind_p = svn_fs_fs__dag_node_kind(node);
1339 return SVN_NO_ERROR;
1343 /* Set *KIND_P to the type of node present at PATH under ROOT. If
1344 PATH does not exist under ROOT, set *KIND_P to svn_node_none. Use
1345 POOL for temporary allocation. */
1347 svn_fs_fs__check_path(svn_node_kind_t *kind_p,
1348 svn_fs_root_t *root,
1352 svn_error_t *err = node_kind(kind_p, root, path, pool);
1354 ((err->apr_err == SVN_ERR_FS_NOT_FOUND)
1355 || (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY)))
1357 svn_error_clear(err);
1359 *kind_p = svn_node_none;
1362 return svn_error_trace(err);
1365 /* Set *VALUE_P to the value of the property named PROPNAME of PATH in
1366 ROOT. If the node has no property by that name, set *VALUE_P to
1367 zero. Allocate the result in POOL. */
1368 static svn_error_t *
1369 fs_node_prop(svn_string_t **value_p,
1370 svn_fs_root_t *root,
1372 const char *propname,
1376 apr_hash_t *proplist;
1378 SVN_ERR(get_dag(&node, root, path, FALSE, pool));
1379 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, node, pool));
1382 *value_p = svn_hash_gets(proplist, propname);
1384 return SVN_NO_ERROR;
1388 /* Set *TABLE_P to the entire property list of PATH under ROOT, as an
1389 APR hash table allocated in POOL. The resulting property table
1390 maps property names to pointers to svn_string_t objects containing
1391 the property value. */
1392 static svn_error_t *
1393 fs_node_proplist(apr_hash_t **table_p,
1394 svn_fs_root_t *root,
1401 SVN_ERR(get_dag(&node, root, path, FALSE, pool));
1402 SVN_ERR(svn_fs_fs__dag_get_proplist(&table, node, pool));
1403 *table_p = table ? table : apr_hash_make(pool);
1405 return SVN_NO_ERROR;
1409 static svn_error_t *
1410 increment_mergeinfo_up_tree(parent_path_t *pp,
1411 apr_int64_t increment,
1414 for (; pp; pp = pp->parent)
1415 SVN_ERR(svn_fs_fs__dag_increment_mergeinfo_count(pp->node,
1419 return SVN_NO_ERROR;
1422 /* Change, add, or delete a node's property value. The affected node
1423 is PATH under ROOT, the property value to modify is NAME, and VALUE
1424 points to either a string value to set the new contents to, or NULL
1425 if the property should be deleted. Perform temporary allocations
1427 static svn_error_t *
1428 fs_change_node_prop(svn_fs_root_t *root,
1431 const svn_string_t *value,
1434 parent_path_t *parent_path;
1435 apr_hash_t *proplist;
1438 if (! root->is_txn_root)
1439 return SVN_FS__NOT_TXN(root);
1442 path = svn_fs__canonicalize_abspath(path, pool);
1443 SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, pool));
1445 /* Check (non-recursively) to see if path is locked; if so, check
1446 that we can use it. */
1447 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
1448 SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, FALSE, FALSE,
1451 SVN_ERR(make_path_mutable(root, parent_path, path, pool));
1452 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, parent_path->node, pool));
1454 /* If there's no proplist, but we're just deleting a property, exit now. */
1455 if ((! proplist) && (! value))
1456 return SVN_NO_ERROR;
1458 /* Now, if there's no proplist, we know we need to make one. */
1460 proplist = apr_hash_make(pool);
1462 if (svn_fs_fs__fs_supports_mergeinfo(root->fs)
1463 && strcmp (name, SVN_PROP_MERGEINFO) == 0)
1465 apr_int64_t increment = 0;
1466 svn_boolean_t had_mergeinfo;
1467 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&had_mergeinfo, parent_path->node));
1469 if (value && !had_mergeinfo)
1471 else if (!value && had_mergeinfo)
1476 SVN_ERR(increment_mergeinfo_up_tree(parent_path, increment, pool));
1477 SVN_ERR(svn_fs_fs__dag_set_has_mergeinfo(parent_path->node,
1478 (value != NULL), pool));
1482 /* Set the property. */
1483 svn_hash_sets(proplist, name, value);
1485 /* Overwrite the node's proplist. */
1486 SVN_ERR(svn_fs_fs__dag_set_proplist(parent_path->node, proplist,
1489 /* Make a record of this modification in the changes table. */
1490 return add_change(root->fs, txn_id, path,
1491 svn_fs_fs__dag_get_id(parent_path->node),
1492 svn_fs_path_change_modify, FALSE, TRUE,
1493 svn_fs_fs__dag_node_kind(parent_path->node),
1494 SVN_INVALID_REVNUM, NULL, pool);
1498 /* Determine if the properties of two path/root combinations are
1499 different. Set *CHANGED_P to TRUE if the properties at PATH1 under
1500 ROOT1 differ from those at PATH2 under ROOT2, or FALSE otherwise.
1501 Both roots must be in the same filesystem. */
1502 static svn_error_t *
1503 fs_props_changed(svn_boolean_t *changed_p,
1504 svn_fs_root_t *root1,
1506 svn_fs_root_t *root2,
1510 dag_node_t *node1, *node2;
1512 /* Check that roots are in the same fs. */
1513 if (root1->fs != root2->fs)
1514 return svn_error_create
1515 (SVN_ERR_FS_GENERAL, NULL,
1516 _("Cannot compare property value between two different filesystems"));
1518 SVN_ERR(get_dag(&node1, root1, path1, TRUE, pool));
1519 SVN_ERR(get_dag(&node2, root2, path2, TRUE, pool));
1520 return svn_fs_fs__dag_things_different(changed_p, NULL,
1526 /* Merges and commits. */
1528 /* Set *NODE to the root node of ROOT. */
1529 static svn_error_t *
1530 get_root(dag_node_t **node, svn_fs_root_t *root, apr_pool_t *pool)
1532 return get_dag(node, root, "/", TRUE, pool);
1536 /* Set the contents of CONFLICT_PATH to PATH, and return an
1537 SVN_ERR_FS_CONFLICT error that indicates that there was a conflict
1538 at PATH. Perform all allocations in POOL (except the allocation of
1539 CONFLICT_PATH, which should be handled outside this function). */
1540 static svn_error_t *
1541 conflict_err(svn_stringbuf_t *conflict_path,
1544 svn_stringbuf_set(conflict_path, path);
1545 return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL,
1546 _("Conflict at '%s'"), path);
1550 /* Merge changes between ANCESTOR and SOURCE into TARGET. ANCESTOR
1551 * and TARGET must be distinct node revisions. TARGET_PATH should
1552 * correspond to TARGET's full path in its filesystem, and is used for
1553 * reporting conflict location.
1555 * SOURCE, TARGET, and ANCESTOR are generally directories; this
1556 * function recursively merges the directories' contents. If any are
1557 * files, this function simply returns an error whenever SOURCE,
1558 * TARGET, and ANCESTOR are all distinct node revisions.
1560 * If there are differences between ANCESTOR and SOURCE that conflict
1561 * with changes between ANCESTOR and TARGET, this function returns an
1562 * SVN_ERR_FS_CONFLICT error, and updates CONFLICT_P to the name of the
1563 * conflicting node in TARGET, with TARGET_PATH prepended as a path.
1565 * If there are no conflicting differences, CONFLICT_P is updated to
1568 * CONFLICT_P must point to a valid svn_stringbuf_t.
1570 * Do any necessary temporary allocation in POOL.
1572 static svn_error_t *
1573 merge(svn_stringbuf_t *conflict_p,
1574 const char *target_path,
1577 dag_node_t *ancestor,
1579 apr_int64_t *mergeinfo_increment_out,
1582 const svn_fs_id_t *source_id, *target_id, *ancestor_id;
1583 apr_hash_t *s_entries, *t_entries, *a_entries;
1584 apr_hash_index_t *hi;
1586 apr_pool_t *iterpool;
1587 apr_int64_t mergeinfo_increment = 0;
1588 svn_boolean_t fs_supports_mergeinfo;
1590 /* Make sure everyone comes from the same filesystem. */
1591 fs = svn_fs_fs__dag_get_fs(ancestor);
1592 if ((fs != svn_fs_fs__dag_get_fs(source))
1593 || (fs != svn_fs_fs__dag_get_fs(target)))
1595 return svn_error_create
1596 (SVN_ERR_FS_CORRUPT, NULL,
1597 _("Bad merge; ancestor, source, and target not all in same fs"));
1600 /* We have the same fs, now check it. */
1601 SVN_ERR(svn_fs__check_fs(fs, TRUE));
1603 source_id = svn_fs_fs__dag_get_id(source);
1604 target_id = svn_fs_fs__dag_get_id(target);
1605 ancestor_id = svn_fs_fs__dag_get_id(ancestor);
1607 /* It's improper to call this function with ancestor == target. */
1608 if (svn_fs_fs__id_eq(ancestor_id, target_id))
1610 svn_string_t *id_str = svn_fs_fs__id_unparse(target_id, pool);
1611 return svn_error_createf
1612 (SVN_ERR_FS_GENERAL, NULL,
1613 _("Bad merge; target '%s' has id '%s', same as ancestor"),
1614 target_path, id_str->data);
1617 svn_stringbuf_setempty(conflict_p);
1620 * Either no change made in source, or same change as made in target.
1621 * Both mean nothing to merge here.
1623 if (svn_fs_fs__id_eq(ancestor_id, source_id)
1624 || (svn_fs_fs__id_eq(source_id, target_id)))
1625 return SVN_NO_ERROR;
1627 /* Else proceed, knowing all three are distinct node revisions.
1629 * How to merge from this point:
1631 * if (not all 3 are directories)
1633 * early exit with conflict;
1636 * // Property changes may only be made to up-to-date
1637 * // directories, because once the client commits the prop
1638 * // change, it bumps the directory's revision, and therefore
1639 * // must be able to depend on there being no other changes to
1640 * // that directory in the repository.
1641 * if (target's property list differs from ancestor's)
1644 * For each entry NAME in the directory ANCESTOR:
1646 * Let ANCESTOR-ENTRY, SOURCE-ENTRY, and TARGET-ENTRY be the IDs of
1647 * the name within ANCESTOR, SOURCE, and TARGET respectively.
1648 * (Possibly null if NAME does not exist in SOURCE or TARGET.)
1650 * If ANCESTOR-ENTRY == SOURCE-ENTRY, then:
1651 * No changes were made to this entry while the transaction was in
1652 * progress, so do nothing to the target.
1654 * Else if ANCESTOR-ENTRY == TARGET-ENTRY, then:
1655 * A change was made to this entry while the transaction was in
1656 * process, but the transaction did not touch this entry. Replace
1657 * TARGET-ENTRY with SOURCE-ENTRY.
1660 * Changes were made to this entry both within the transaction and
1661 * to the repository while the transaction was in progress. They
1662 * must be merged or declared to be in conflict.
1664 * If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
1665 * double delete; flag a conflict.
1667 * If any of the three entries is of type file, declare a conflict.
1669 * If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
1670 * modification of ANCESTOR-ENTRY (determine by comparing the
1671 * node-id fields), declare a conflict. A replacement is
1672 * incompatible with a modification or other replacement--even
1673 * an identical replacement.
1675 * Direct modifications were made to the directory ANCESTOR-ENTRY
1676 * in both SOURCE and TARGET. Recursively merge these
1679 * For each leftover entry NAME in the directory SOURCE:
1681 * If NAME exists in TARGET, declare a conflict. Even if SOURCE and
1682 * TARGET are adding exactly the same thing, two additions are not
1683 * auto-mergeable with each other.
1685 * Add NAME to TARGET with the entry from SOURCE.
1687 * Now that we are done merging the changes from SOURCE into the
1688 * directory TARGET, update TARGET's predecessor to be SOURCE.
1691 if ((svn_fs_fs__dag_node_kind(source) != svn_node_dir)
1692 || (svn_fs_fs__dag_node_kind(target) != svn_node_dir)
1693 || (svn_fs_fs__dag_node_kind(ancestor) != svn_node_dir))
1695 return conflict_err(conflict_p, target_path);
1699 /* Possible early merge failure: if target and ancestor have
1700 different property lists, then the merge should fail.
1701 Propchanges can *only* be committed on an up-to-date directory.
1702 ### TODO: see issue #418 about the inelegance of this.
1704 Another possible, similar, early merge failure: if source and
1705 ancestor have different property lists (meaning someone else
1706 changed directory properties while our commit transaction was
1707 happening), the merge should fail. See issue #2751.
1710 node_revision_t *tgt_nr, *anc_nr, *src_nr;
1712 /* Get node revisions for our id's. */
1713 SVN_ERR(svn_fs_fs__get_node_revision(&tgt_nr, fs, target_id, pool));
1714 SVN_ERR(svn_fs_fs__get_node_revision(&anc_nr, fs, ancestor_id, pool));
1715 SVN_ERR(svn_fs_fs__get_node_revision(&src_nr, fs, source_id, pool));
1717 /* Now compare the prop-keys of the skels. Note that just because
1718 the keys are different -doesn't- mean the proplists have
1719 different contents. But merge() isn't concerned with contents;
1720 it doesn't do a brute-force comparison on textual contents, so
1721 it won't do that here either. Checking to see if the propkey
1722 atoms are `equal' is enough. */
1723 if (! svn_fs_fs__noderev_same_rep_key(tgt_nr->prop_rep, anc_nr->prop_rep))
1724 return conflict_err(conflict_p, target_path);
1725 if (! svn_fs_fs__noderev_same_rep_key(src_nr->prop_rep, anc_nr->prop_rep))
1726 return conflict_err(conflict_p, target_path);
1729 /* ### todo: it would be more efficient to simply check for a NULL
1730 entries hash where necessary below than to allocate an empty hash
1731 here, but another day, another day... */
1732 SVN_ERR(svn_fs_fs__dag_dir_entries(&s_entries, source, pool));
1733 SVN_ERR(svn_fs_fs__dag_dir_entries(&t_entries, target, pool));
1734 SVN_ERR(svn_fs_fs__dag_dir_entries(&a_entries, ancestor, pool));
1736 fs_supports_mergeinfo = svn_fs_fs__fs_supports_mergeinfo(fs);
1738 /* for each entry E in a_entries... */
1739 iterpool = svn_pool_create(pool);
1740 for (hi = apr_hash_first(pool, a_entries);
1742 hi = apr_hash_next(hi))
1744 svn_fs_dirent_t *s_entry, *t_entry, *a_entry;
1748 svn_pool_clear(iterpool);
1750 name = svn__apr_hash_index_key(hi);
1751 klen = svn__apr_hash_index_klen(hi);
1752 a_entry = svn__apr_hash_index_val(hi);
1754 s_entry = apr_hash_get(s_entries, name, klen);
1755 t_entry = apr_hash_get(t_entries, name, klen);
1757 /* No changes were made to this entry while the transaction was
1758 in progress, so do nothing to the target. */
1759 if (s_entry && svn_fs_fs__id_eq(a_entry->id, s_entry->id))
1762 /* A change was made to this entry while the transaction was in
1763 process, but the transaction did not touch this entry. */
1764 else if (t_entry && svn_fs_fs__id_eq(a_entry->id, t_entry->id))
1766 dag_node_t *t_ent_node;
1767 SVN_ERR(svn_fs_fs__dag_get_node(&t_ent_node, fs,
1768 t_entry->id, iterpool));
1769 if (fs_supports_mergeinfo)
1771 apr_int64_t mergeinfo_start;
1772 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start,
1774 mergeinfo_increment -= mergeinfo_start;
1779 dag_node_t *s_ent_node;
1780 SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
1781 s_entry->id, iterpool));
1783 if (fs_supports_mergeinfo)
1785 apr_int64_t mergeinfo_end;
1786 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end,
1788 mergeinfo_increment += mergeinfo_end;
1791 SVN_ERR(svn_fs_fs__dag_set_entry(target, name,
1799 SVN_ERR(svn_fs_fs__dag_delete(target, name, txn_id, iterpool));
1803 /* Changes were made to this entry both within the transaction
1804 and to the repository while the transaction was in progress.
1805 They must be merged or declared to be in conflict. */
1808 dag_node_t *s_ent_node, *t_ent_node, *a_ent_node;
1809 const char *new_tpath;
1810 apr_int64_t sub_mergeinfo_increment;
1812 /* If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
1813 double delete; if one of them is null, that's a delete versus
1814 a modification. In any of these cases, flag a conflict. */
1815 if (s_entry == NULL || t_entry == NULL)
1816 return conflict_err(conflict_p,
1817 svn_fspath__join(target_path,
1821 /* If any of the three entries is of type file, flag a conflict. */
1822 if (s_entry->kind == svn_node_file
1823 || t_entry->kind == svn_node_file
1824 || a_entry->kind == svn_node_file)
1825 return conflict_err(conflict_p,
1826 svn_fspath__join(target_path,
1830 /* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
1831 modification of ANCESTOR-ENTRY, declare a conflict. */
1832 if (strcmp(svn_fs_fs__id_node_id(s_entry->id),
1833 svn_fs_fs__id_node_id(a_entry->id)) != 0
1834 || strcmp(svn_fs_fs__id_copy_id(s_entry->id),
1835 svn_fs_fs__id_copy_id(a_entry->id)) != 0
1836 || strcmp(svn_fs_fs__id_node_id(t_entry->id),
1837 svn_fs_fs__id_node_id(a_entry->id)) != 0
1838 || strcmp(svn_fs_fs__id_copy_id(t_entry->id),
1839 svn_fs_fs__id_copy_id(a_entry->id)) != 0)
1840 return conflict_err(conflict_p,
1841 svn_fspath__join(target_path,
1845 /* Direct modifications were made to the directory
1846 ANCESTOR-ENTRY in both SOURCE and TARGET. Recursively
1847 merge these modifications. */
1848 SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
1849 s_entry->id, iterpool));
1850 SVN_ERR(svn_fs_fs__dag_get_node(&t_ent_node, fs,
1851 t_entry->id, iterpool));
1852 SVN_ERR(svn_fs_fs__dag_get_node(&a_ent_node, fs,
1853 a_entry->id, iterpool));
1854 new_tpath = svn_fspath__join(target_path, t_entry->name, iterpool);
1855 SVN_ERR(merge(conflict_p, new_tpath,
1856 t_ent_node, s_ent_node, a_ent_node,
1858 &sub_mergeinfo_increment,
1860 if (fs_supports_mergeinfo)
1861 mergeinfo_increment += sub_mergeinfo_increment;
1864 /* We've taken care of any possible implications E could have.
1865 Remove it from source_entries, so it's easy later to loop
1866 over all the source entries that didn't exist in
1867 ancestor_entries. */
1869 apr_hash_set(s_entries, name, klen, NULL);
1872 /* For each entry E in source but not in ancestor */
1873 for (hi = apr_hash_first(pool, s_entries);
1875 hi = apr_hash_next(hi))
1877 svn_fs_dirent_t *s_entry, *t_entry;
1878 const char *name = svn__apr_hash_index_key(hi);
1879 apr_ssize_t klen = svn__apr_hash_index_klen(hi);
1880 dag_node_t *s_ent_node;
1882 svn_pool_clear(iterpool);
1884 s_entry = svn__apr_hash_index_val(hi);
1885 t_entry = apr_hash_get(t_entries, name, klen);
1887 /* If NAME exists in TARGET, declare a conflict. */
1889 return conflict_err(conflict_p,
1890 svn_fspath__join(target_path,
1894 SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
1895 s_entry->id, iterpool));
1896 if (fs_supports_mergeinfo)
1898 apr_int64_t mergeinfo_s;
1899 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_s,
1901 mergeinfo_increment += mergeinfo_s;
1904 SVN_ERR(svn_fs_fs__dag_set_entry
1905 (target, s_entry->name, s_entry->id, s_entry->kind,
1908 svn_pool_destroy(iterpool);
1910 SVN_ERR(svn_fs_fs__dag_update_ancestry(target, source, pool));
1912 if (fs_supports_mergeinfo)
1913 SVN_ERR(svn_fs_fs__dag_increment_mergeinfo_count(target,
1914 mergeinfo_increment,
1917 if (mergeinfo_increment_out)
1918 *mergeinfo_increment_out = mergeinfo_increment;
1920 return SVN_NO_ERROR;
1923 /* Merge changes between an ancestor and SOURCE_NODE into
1924 TXN. The ancestor is either ANCESTOR_NODE, or if
1925 that is null, TXN's base node.
1927 If the merge is successful, TXN's base will become
1928 SOURCE_NODE, and its root node will have a new ID, a
1929 successor of SOURCE_NODE.
1931 If a conflict results, update *CONFLICT to the path in the txn that
1932 conflicted; see the CONFLICT_P parameter of merge() for details. */
1933 static svn_error_t *
1934 merge_changes(dag_node_t *ancestor_node,
1935 dag_node_t *source_node,
1937 svn_stringbuf_t *conflict,
1940 dag_node_t *txn_root_node;
1941 svn_fs_t *fs = txn->fs;
1942 const char *txn_id = txn->id;
1944 SVN_ERR(svn_fs_fs__dag_txn_root(&txn_root_node, fs, txn_id, pool));
1946 if (ancestor_node == NULL)
1948 SVN_ERR(svn_fs_fs__dag_txn_base_root(&ancestor_node, fs,
1952 if (svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(ancestor_node),
1953 svn_fs_fs__dag_get_id(txn_root_node)))
1955 /* If no changes have been made in TXN since its current base,
1956 then it can't conflict with any changes since that base.
1957 The caller isn't supposed to call us in that case. */
1958 SVN_ERR_MALFUNCTION();
1961 SVN_ERR(merge(conflict, "/", txn_root_node,
1962 source_node, ancestor_node, txn_id, NULL, pool));
1964 return SVN_NO_ERROR;
1969 svn_fs_fs__commit_txn(const char **conflict_p,
1970 svn_revnum_t *new_rev,
1974 /* How do commits work in Subversion?
1976 * When you're ready to commit, here's what you have:
1978 * 1. A transaction, with a mutable tree hanging off it.
1979 * 2. A base revision, against which TXN_TREE was made.
1980 * 3. A latest revision, which may be newer than the base rev.
1982 * The problem is that if latest != base, then one can't simply
1983 * attach the txn root as the root of the new revision, because that
1984 * would lose all the changes between base and latest. It is also
1985 * not acceptable to insist that base == latest; in a busy
1986 * repository, commits happen too fast to insist that everyone keep
1987 * their entire tree up-to-date at all times. Non-overlapping
1988 * changes should not interfere with each other.
1990 * The solution is to merge the changes between base and latest into
1991 * the txn tree [see the function merge()]. The txn tree is the
1992 * only one of the three trees that is mutable, so it has to be the
1995 * You might have to adjust it more than once, if a new latest
1996 * revision gets committed while you were merging in the previous
1999 * 1. Jane starts txn T, based at revision 6.
2000 * 2. Someone commits (or already committed) revision 7.
2001 * 3. Jane's starts merging the changes between 6 and 7 into T.
2002 * 4. Meanwhile, someone commits revision 8.
2003 * 5. Jane finishes the 6-->7 merge. T could now be committed
2004 * against a latest revision of 7, if only that were still the
2005 * latest. Unfortunately, 8 is now the latest, so...
2006 * 6. Jane starts merging the changes between 7 and 8 into T.
2007 * 7. Meanwhile, no one commits any new revisions. Whew.
2008 * 8. Jane commits T, creating revision 9, whose tree is exactly
2009 * T's tree, except immutable now.
2011 * Lather, rinse, repeat.
2014 svn_error_t *err = SVN_NO_ERROR;
2015 svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool);
2016 svn_fs_t *fs = txn->fs;
2018 /* Limit memory usage when the repository has a high commit rate and
2019 needs to run the following while loop multiple times. The memory
2020 growth without an iteration pool is very noticeable when the
2021 transaction modifies a node that has 20,000 sibling nodes. */
2022 apr_pool_t *iterpool = svn_pool_create(pool);
2024 /* Initialize output params. */
2025 *new_rev = SVN_INVALID_REVNUM;
2031 svn_revnum_t youngish_rev;
2032 svn_fs_root_t *youngish_root;
2033 dag_node_t *youngish_root_node;
2035 svn_pool_clear(iterpool);
2037 /* Get the *current* youngest revision. We call it "youngish"
2038 because new revisions might get committed after we've
2041 SVN_ERR(svn_fs_fs__youngest_rev(&youngish_rev, fs, iterpool));
2042 SVN_ERR(svn_fs_fs__revision_root(&youngish_root, fs, youngish_rev,
2045 /* Get the dag node for the youngest revision. Later we'll use
2046 it as the SOURCE argument to a merge, and if the merge
2047 succeeds, this youngest root node will become the new base
2048 root for the svn txn that was the target of the merge (but
2049 note that the youngest rev may have changed by then -- that's
2050 why we're careful to get this root in its own bdb txn
2052 SVN_ERR(get_root(&youngish_root_node, youngish_root, iterpool));
2054 /* Try to merge. If the merge succeeds, the base root node of
2055 TARGET's txn will become the same as youngish_root_node, so
2056 any future merges will only be between that node and whatever
2057 the root node of the youngest rev is by then. */
2058 err = merge_changes(NULL, youngish_root_node, txn, conflict, iterpool);
2061 if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2062 *conflict_p = conflict->data;
2065 txn->base_rev = youngish_rev;
2067 /* Try to commit. */
2068 err = svn_fs_fs__commit(new_rev, fs, txn, iterpool);
2069 if (err && (err->apr_err == SVN_ERR_FS_TXN_OUT_OF_DATE))
2071 /* Did someone else finish committing a new revision while we
2072 were in mid-merge or mid-commit? If so, we'll need to
2073 loop again to merge the new changes in, then try to
2074 commit again. Or if that's not what happened, then just
2075 return the error. */
2076 svn_revnum_t youngest_rev;
2077 SVN_ERR(svn_fs_fs__youngest_rev(&youngest_rev, fs, iterpool));
2078 if (youngest_rev == youngish_rev)
2081 svn_error_clear(err);
2096 svn_fs_fs__reset_txn_caches(fs);
2098 svn_pool_destroy(iterpool);
2099 return svn_error_trace(err);
2103 /* Merge changes between two nodes into a third node. Given nodes
2104 SOURCE_PATH under SOURCE_ROOT, TARGET_PATH under TARGET_ROOT and
2105 ANCESTOR_PATH under ANCESTOR_ROOT, modify target to contain all the
2106 changes between the ancestor and source. If there are conflicts,
2107 return SVN_ERR_FS_CONFLICT and set *CONFLICT_P to a textual
2108 description of the offending changes. Perform any temporary
2109 allocations in POOL. */
2110 static svn_error_t *
2111 fs_merge(const char **conflict_p,
2112 svn_fs_root_t *source_root,
2113 const char *source_path,
2114 svn_fs_root_t *target_root,
2115 const char *target_path,
2116 svn_fs_root_t *ancestor_root,
2117 const char *ancestor_path,
2120 dag_node_t *source, *ancestor;
2123 svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool);
2125 if (! target_root->is_txn_root)
2126 return SVN_FS__NOT_TXN(target_root);
2129 if ((source_root->fs != ancestor_root->fs)
2130 || (target_root->fs != ancestor_root->fs))
2132 return svn_error_create
2133 (SVN_ERR_FS_CORRUPT, NULL,
2134 _("Bad merge; ancestor, source, and target not all in same fs"));
2137 /* ### kff todo: is there any compelling reason to get the nodes in
2138 one db transaction? Right now we don't; txn_body_get_root() gets
2139 one node at a time. This will probably need to change:
2141 Jim Blandy <jimb@zwingli.cygnus.com> writes:
2142 > svn_fs_merge needs to be a single transaction, to protect it against
2143 > people deleting parents of nodes it's working on, etc.
2146 /* Get the ancestor node. */
2147 SVN_ERR(get_root(&ancestor, ancestor_root, pool));
2149 /* Get the source node. */
2150 SVN_ERR(get_root(&source, source_root, pool));
2152 /* Open a txn for the txn root into which we're merging. */
2153 SVN_ERR(svn_fs_fs__open_txn(&txn, ancestor_root->fs, target_root->txn,
2156 /* Merge changes between ANCESTOR and SOURCE into TXN. */
2157 err = merge_changes(ancestor, source, txn, conflict, pool);
2160 if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2161 *conflict_p = conflict->data;
2162 return svn_error_trace(err);
2165 return SVN_NO_ERROR;
2169 svn_fs_fs__deltify(svn_fs_t *fs,
2170 svn_revnum_t revision,
2173 /* Deltify is a no-op for fs_fs. */
2175 return SVN_NO_ERROR;
2182 /* Set *TABLE_P to a newly allocated APR hash table containing the
2183 entries of the directory at PATH in ROOT. The keys of the table
2184 are entry names, as byte strings, excluding the final null
2185 character; the table's values are pointers to svn_fs_dirent_t
2186 structures. Allocate the table and its contents in POOL. */
2187 static svn_error_t *
2188 fs_dir_entries(apr_hash_t **table_p,
2189 svn_fs_root_t *root,
2195 /* Get the entries for this path in the caller's pool. */
2196 SVN_ERR(get_dag(&node, root, path, FALSE, pool));
2197 return svn_fs_fs__dag_dir_entries(table_p, node, pool);
2200 /* Raise an error if PATH contains a newline because FSFS cannot handle
2201 * such paths. See issue #4340. */
2202 static svn_error_t *
2203 check_newline(const char *path, apr_pool_t *pool)
2205 char *c = strchr(path, '\n');
2208 return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
2209 _("Invalid control character '0x%02x' in path '%s'"),
2210 (unsigned char)*c, svn_path_illegal_path_escape(path, pool));
2212 return SVN_NO_ERROR;
2215 /* Create a new directory named PATH in ROOT. The new directory has
2216 no entries, and no properties. ROOT must be the root of a
2217 transaction, not a revision. Do any necessary temporary allocation
2219 static svn_error_t *
2220 fs_make_dir(svn_fs_root_t *root,
2224 parent_path_t *parent_path;
2225 dag_node_t *sub_dir;
2226 const char *txn_id = root->txn;
2228 SVN_ERR(check_newline(path, pool));
2230 path = svn_fs__canonicalize_abspath(path, pool);
2231 SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
2234 /* Check (recursively) to see if some lock is 'reserving' a path at
2235 that location, or even some child-path; if so, check that we can
2237 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2238 SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, TRUE, FALSE,
2241 /* If there's already a sub-directory by that name, complain. This
2242 also catches the case of trying to make a subdirectory named `/'. */
2243 if (parent_path->node)
2244 return SVN_FS__ALREADY_EXISTS(root, path);
2246 /* Create the subdirectory. */
2247 SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
2248 SVN_ERR(svn_fs_fs__dag_make_dir(&sub_dir,
2249 parent_path->parent->node,
2250 parent_path_path(parent_path->parent,
2256 /* Add this directory to the path cache. */
2257 SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool),
2260 /* Make a record of this modification in the changes table. */
2261 return add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(sub_dir),
2262 svn_fs_path_change_add, FALSE, FALSE, svn_node_dir,
2263 SVN_INVALID_REVNUM, NULL, pool);
2267 /* Delete the node at PATH under ROOT. ROOT must be a transaction
2268 root. Perform temporary allocations in POOL. */
2269 static svn_error_t *
2270 fs_delete_node(svn_fs_root_t *root,
2274 parent_path_t *parent_path;
2275 const char *txn_id = root->txn;
2276 apr_int64_t mergeinfo_count = 0;
2277 svn_node_kind_t kind;
2279 if (! root->is_txn_root)
2280 return SVN_FS__NOT_TXN(root);
2282 path = svn_fs__canonicalize_abspath(path, pool);
2283 SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, pool));
2284 kind = svn_fs_fs__dag_node_kind(parent_path->node);
2286 /* We can't remove the root of the filesystem. */
2287 if (! parent_path->parent)
2288 return svn_error_create(SVN_ERR_FS_ROOT_DIR, NULL,
2289 _("The root directory cannot be deleted"));
2291 /* Check to see if path (or any child thereof) is locked; if so,
2292 check that we can use the existing lock(s). */
2293 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2294 SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, TRUE, FALSE,
2297 /* Make the parent directory mutable, and do the deletion. */
2298 SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
2299 if (svn_fs_fs__fs_supports_mergeinfo(root->fs))
2300 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_count,
2301 parent_path->node));
2302 SVN_ERR(svn_fs_fs__dag_delete(parent_path->parent->node,
2306 /* Remove this node and any children from the path cache. */
2307 SVN_ERR(dag_node_cache_invalidate(root, parent_path_path(parent_path, pool),
2310 /* Update mergeinfo counts for parents */
2311 if (mergeinfo_count > 0)
2312 SVN_ERR(increment_mergeinfo_up_tree(parent_path->parent,
2316 /* Make a record of this modification in the changes table. */
2317 return add_change(root->fs, txn_id, path,
2318 svn_fs_fs__dag_get_id(parent_path->node),
2319 svn_fs_path_change_delete, FALSE, FALSE, kind,
2320 SVN_INVALID_REVNUM, NULL, pool);
2324 /* Set *SAME_P to TRUE if FS1 and FS2 have the same UUID, else set to FALSE.
2325 Use POOL for temporary allocation only.
2326 Note: this code is duplicated between libsvn_fs_fs and libsvn_fs_base. */
2327 static svn_error_t *
2328 fs_same_p(svn_boolean_t *same_p,
2333 *same_p = ! strcmp(fs1->uuid, fs2->uuid);
2334 return SVN_NO_ERROR;
2337 /* Copy the node at FROM_PATH under FROM_ROOT to TO_PATH under
2338 TO_ROOT. If PRESERVE_HISTORY is set, then the copy is recorded in
2339 the copies table. Perform temporary allocations in POOL. */
2340 static svn_error_t *
2341 copy_helper(svn_fs_root_t *from_root,
2342 const char *from_path,
2343 svn_fs_root_t *to_root,
2344 const char *to_path,
2345 svn_boolean_t preserve_history,
2348 dag_node_t *from_node;
2349 parent_path_t *to_parent_path;
2350 const char *txn_id = to_root->txn;
2351 svn_boolean_t same_p;
2353 /* Use an error check, not an assert, because even the caller cannot
2354 guarantee that a filesystem's UUID has not changed "on the fly". */
2355 SVN_ERR(fs_same_p(&same_p, from_root->fs, to_root->fs, pool));
2357 return svn_error_createf
2358 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2359 _("Cannot copy between two different filesystems ('%s' and '%s')"),
2360 from_root->fs->path, to_root->fs->path);
2362 if (from_root->is_txn_root)
2363 return svn_error_create
2364 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2365 _("Copy from mutable tree not currently supported"));
2367 /* Get the NODE for FROM_PATH in FROM_ROOT.*/
2368 SVN_ERR(get_dag(&from_node, from_root, from_path, TRUE, pool));
2370 /* Build up the parent path from TO_PATH in TO_ROOT. If the last
2371 component does not exist, it's not that big a deal. We'll just
2373 SVN_ERR(open_path(&to_parent_path, to_root, to_path,
2374 open_path_last_optional, txn_id, pool));
2376 /* Check to see if path (or any child thereof) is locked; if so,
2377 check that we can use the existing lock(s). */
2378 if (to_root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2379 SVN_ERR(svn_fs_fs__allow_locked_operation(to_path, to_root->fs,
2380 TRUE, FALSE, pool));
2382 /* If the destination node already exists as the same node as the
2383 source (in other words, this operation would result in nothing
2384 happening at all), just do nothing an return successfully,
2385 proud that you saved yourself from a tiresome task. */
2386 if (to_parent_path->node &&
2387 svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(from_node),
2388 svn_fs_fs__dag_get_id(to_parent_path->node)))
2389 return SVN_NO_ERROR;
2391 if (! from_root->is_txn_root)
2393 svn_fs_path_change_kind_t kind;
2394 dag_node_t *new_node;
2395 const char *from_canonpath;
2396 apr_int64_t mergeinfo_start;
2397 apr_int64_t mergeinfo_end;
2399 /* If TO_PATH already existed prior to the copy, note that this
2400 operation is a replacement, not an addition. */
2401 if (to_parent_path->node)
2403 kind = svn_fs_path_change_replace;
2404 if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs))
2405 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start,
2406 to_parent_path->node));
2410 kind = svn_fs_path_change_add;
2411 mergeinfo_start = 0;
2414 if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs))
2415 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end,
2418 /* Make sure the target node's parents are mutable. */
2419 SVN_ERR(make_path_mutable(to_root, to_parent_path->parent,
2422 /* Canonicalize the copyfrom path. */
2423 from_canonpath = svn_fs__canonicalize_abspath(from_path, pool);
2425 SVN_ERR(svn_fs_fs__dag_copy(to_parent_path->parent->node,
2426 to_parent_path->entry,
2433 if (kind == svn_fs_path_change_replace)
2434 SVN_ERR(dag_node_cache_invalidate(to_root,
2435 parent_path_path(to_parent_path,
2438 if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs)
2439 && mergeinfo_start != mergeinfo_end)
2440 SVN_ERR(increment_mergeinfo_up_tree(to_parent_path->parent,
2441 mergeinfo_end - mergeinfo_start,
2444 /* Make a record of this modification in the changes table. */
2445 SVN_ERR(get_dag(&new_node, to_root, to_path, TRUE, pool));
2446 SVN_ERR(add_change(to_root->fs, txn_id, to_path,
2447 svn_fs_fs__dag_get_id(new_node), kind, FALSE, FALSE,
2448 svn_fs_fs__dag_node_kind(from_node),
2449 from_root->rev, from_canonpath, pool));
2453 /* See IZ Issue #436 */
2454 /* Copying from transaction roots not currently available.
2456 ### cmpilato todo someday: make this not so. :-) Note that
2457 when copying from mutable trees, you have to make sure that
2458 you aren't creating a cyclic graph filesystem, and a simple
2459 referencing operation won't cut it. Currently, we should not
2460 be able to reach this clause, and the interface reports that
2461 this only works from immutable trees anyway, but JimB has
2462 stated that this requirement need not be necessary in the
2465 SVN_ERR_MALFUNCTION();
2468 return SVN_NO_ERROR;
2472 /* Create a copy of FROM_PATH in FROM_ROOT named TO_PATH in TO_ROOT.
2473 If FROM_PATH is a directory, copy it recursively. Temporary
2474 allocations are from POOL.*/
2475 static svn_error_t *
2476 fs_copy(svn_fs_root_t *from_root,
2477 const char *from_path,
2478 svn_fs_root_t *to_root,
2479 const char *to_path,
2482 SVN_ERR(check_newline(to_path, pool));
2484 return svn_error_trace(copy_helper(from_root,
2485 svn_fs__canonicalize_abspath(from_path,
2488 svn_fs__canonicalize_abspath(to_path,
2494 /* Create a copy of FROM_PATH in FROM_ROOT named TO_PATH in TO_ROOT.
2495 If FROM_PATH is a directory, copy it recursively. No history is
2496 preserved. Temporary allocations are from POOL. */
2497 static svn_error_t *
2498 fs_revision_link(svn_fs_root_t *from_root,
2499 svn_fs_root_t *to_root,
2503 if (! to_root->is_txn_root)
2504 return SVN_FS__NOT_TXN(to_root);
2506 path = svn_fs__canonicalize_abspath(path, pool);
2507 return svn_error_trace(copy_helper(from_root, path, to_root, path,
2512 /* Discover the copy ancestry of PATH under ROOT. Return a relevant
2513 ancestor/revision combination in *PATH_P and *REV_P. Temporary
2514 allocations are in POOL. */
2515 static svn_error_t *
2516 fs_copied_from(svn_revnum_t *rev_p,
2517 const char **path_p,
2518 svn_fs_root_t *root,
2523 const char *copyfrom_path, *copyfrom_str = NULL;
2524 svn_revnum_t copyfrom_rev;
2527 /* Check to see if there is a cached version of this copyfrom
2529 if (! root->is_txn_root) {
2530 fs_rev_root_data_t *frd = root->fsap_data;
2531 copyfrom_str = svn_hash_gets(frd->copyfrom_cache, path);
2536 if (*copyfrom_str == 0)
2538 /* We have a cached entry that says there is no copyfrom
2540 copyfrom_rev = SVN_INVALID_REVNUM;
2541 copyfrom_path = NULL;
2545 /* Parse the copyfrom string for our cached entry. */
2546 buf = apr_pstrdup(pool, copyfrom_str);
2547 str = svn_cstring_tokenize(" ", &buf);
2548 copyfrom_rev = SVN_STR_TO_REV(str);
2549 copyfrom_path = buf;
2554 /* There is no cached entry, look it up the old-fashioned
2556 SVN_ERR(get_dag(&node, root, path, TRUE, pool));
2557 SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(©from_rev, node));
2558 SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(©from_path, node));
2561 *rev_p = copyfrom_rev;
2562 *path_p = copyfrom_path;
2564 return SVN_NO_ERROR;
2571 /* Create the empty file PATH under ROOT. Temporary allocations are
2573 static svn_error_t *
2574 fs_make_file(svn_fs_root_t *root,
2578 parent_path_t *parent_path;
2580 const char *txn_id = root->txn;
2582 SVN_ERR(check_newline(path, pool));
2584 path = svn_fs__canonicalize_abspath(path, pool);
2585 SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
2588 /* If there's already a file by that name, complain.
2589 This also catches the case of trying to make a file named `/'. */
2590 if (parent_path->node)
2591 return SVN_FS__ALREADY_EXISTS(root, path);
2593 /* Check (non-recursively) to see if path is locked; if so, check
2594 that we can use it. */
2595 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2596 SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, FALSE, FALSE,
2599 /* Create the file. */
2600 SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
2601 SVN_ERR(svn_fs_fs__dag_make_file(&child,
2602 parent_path->parent->node,
2603 parent_path_path(parent_path->parent,
2609 /* Add this file to the path cache. */
2610 SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool), child,
2613 /* Make a record of this modification in the changes table. */
2614 return add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(child),
2615 svn_fs_path_change_add, TRUE, FALSE, svn_node_file,
2616 SVN_INVALID_REVNUM, NULL, pool);
2620 /* Set *LENGTH_P to the size of the file PATH under ROOT. Temporary
2621 allocations are in POOL. */
2622 static svn_error_t *
2623 fs_file_length(svn_filesize_t *length_p,
2624 svn_fs_root_t *root,
2630 /* First create a dag_node_t from the root/path pair. */
2631 SVN_ERR(get_dag(&file, root, path, FALSE, pool));
2633 /* Now fetch its length */
2634 return svn_fs_fs__dag_file_length(length_p, file, pool);
2638 /* Set *CHECKSUM to the checksum of type KIND for PATH under ROOT, or
2639 NULL if that information isn't available. Temporary allocations
2641 static svn_error_t *
2642 fs_file_checksum(svn_checksum_t **checksum,
2643 svn_checksum_kind_t kind,
2644 svn_fs_root_t *root,
2650 SVN_ERR(get_dag(&file, root, path, FALSE, pool));
2651 return svn_fs_fs__dag_file_checksum(checksum, file, kind, pool);
2655 /* --- Machinery for svn_fs_file_contents() --- */
2657 /* Set *CONTENTS to a readable stream that will return the contents of
2658 PATH under ROOT. The stream is allocated in POOL. */
2659 static svn_error_t *
2660 fs_file_contents(svn_stream_t **contents,
2661 svn_fs_root_t *root,
2666 svn_stream_t *file_stream;
2668 /* First create a dag_node_t from the root/path pair. */
2669 SVN_ERR(get_dag(&node, root, path, FALSE, pool));
2671 /* Then create a readable stream from the dag_node_t. */
2672 SVN_ERR(svn_fs_fs__dag_get_contents(&file_stream, node, pool));
2674 *contents = file_stream;
2675 return SVN_NO_ERROR;
2678 /* --- End machinery for svn_fs_file_contents() --- */
2681 /* --- Machinery for svn_fs_try_process_file_contents() --- */
2683 static svn_error_t *
2684 fs_try_process_file_contents(svn_boolean_t *success,
2685 svn_fs_root_t *root,
2687 svn_fs_process_contents_func_t processor,
2692 SVN_ERR(get_dag(&node, root, path, FALSE, pool));
2694 return svn_fs_fs__dag_try_process_file_contents(success, node,
2695 processor, baton, pool);
2698 /* --- End machinery for svn_fs_try_process_file_contents() --- */
2701 /* --- Machinery for svn_fs_apply_textdelta() --- */
2704 /* Local baton type for all the helper functions below. */
2705 typedef struct txdelta_baton_t
2707 /* This is the custom-built window consumer given to us by the delta
2708 library; it uniquely knows how to read data from our designated
2709 "source" stream, interpret the window, and write data to our
2710 designated "target" stream (in this case, our repos file.) */
2711 svn_txdelta_window_handler_t interpreter;
2712 void *interpreter_baton;
2714 /* The original file info */
2715 svn_fs_root_t *root;
2718 /* Derived from the file info */
2721 svn_stream_t *source_stream;
2722 svn_stream_t *target_stream;
2723 svn_stream_t *string_stream;
2724 svn_stringbuf_t *target_string;
2726 /* MD5 digest for the base text against which a delta is to be
2727 applied, and for the resultant fulltext, respectively. Either or
2728 both may be null, in which case ignored. */
2729 svn_checksum_t *base_checksum;
2730 svn_checksum_t *result_checksum;
2732 /* Pool used by db txns */
2738 /* ### see comment in window_consumer() regarding this function. */
2740 /* Helper function of generic type `svn_write_fn_t'. Implements a
2741 writable stream which appends to an svn_stringbuf_t. */
2742 static svn_error_t *
2743 write_to_string(void *baton, const char *data, apr_size_t *len)
2745 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
2746 svn_stringbuf_appendbytes(tb->target_string, data, *len);
2747 return SVN_NO_ERROR;
2752 /* The main window handler returned by svn_fs_apply_textdelta. */
2753 static svn_error_t *
2754 window_consumer(svn_txdelta_window_t *window, void *baton)
2756 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
2758 /* Send the window right through to the custom window interpreter.
2759 In theory, the interpreter will then write more data to
2760 cb->target_string. */
2761 SVN_ERR(tb->interpreter(window, tb->interpreter_baton));
2763 /* ### the write_to_string() callback for the txdelta's output stream
2764 ### should be doing all the flush determination logic, not here.
2765 ### in a drastic case, a window could generate a LOT more than the
2766 ### maximum buffer size. we want to flush to the underlying target
2767 ### stream much sooner (e.g. also in a streamy fashion). also, by
2768 ### moving this logic inside the stream, the stream becomes nice
2769 ### and encapsulated: it holds all the logic about buffering and
2772 ### further: I believe the buffering should be removed from tree.c
2773 ### the buffering should go into the target_stream itself, which
2774 ### is defined by reps-string.c. Specifically, I think the
2775 ### rep_write_contents() function will handle the buffering and
2776 ### the spill to the underlying DB. by locating it there, then
2777 ### anybody who gets a writable stream for FS content can take
2778 ### advantage of the buffering capability. this will be important
2779 ### when we export an FS API function for writing a fulltext into
2780 ### the FS, rather than forcing that fulltext thru apply_textdelta.
2783 /* Check to see if we need to purge the portion of the contents that
2784 have been written thus far. */
2785 if ((! window) || (tb->target_string->len > WRITE_BUFFER_SIZE))
2787 apr_size_t len = tb->target_string->len;
2788 SVN_ERR(svn_stream_write(tb->target_stream,
2789 tb->target_string->data,
2791 svn_stringbuf_setempty(tb->target_string);
2794 /* Is the window NULL? If so, we're done. */
2797 /* Close the internal-use stream. ### This used to be inside of
2798 txn_body_fulltext_finalize_edits(), but that invoked a nested
2799 Berkeley DB transaction -- scandalous! */
2800 SVN_ERR(svn_stream_close(tb->target_stream));
2802 SVN_ERR(svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum,
2806 return SVN_NO_ERROR;
2809 /* Helper function for fs_apply_textdelta. BATON is of type
2811 static svn_error_t *
2812 apply_textdelta(void *baton, apr_pool_t *pool)
2814 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
2815 parent_path_t *parent_path;
2816 const char *txn_id = tb->root->txn;
2818 /* Call open_path with no flags, as we want this to return an error
2819 if the node for which we are searching doesn't exist. */
2820 SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id, pool));
2822 /* Check (non-recursively) to see if path is locked; if so, check
2823 that we can use it. */
2824 if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2825 SVN_ERR(svn_fs_fs__allow_locked_operation(tb->path, tb->root->fs,
2826 FALSE, FALSE, pool));
2828 /* Now, make sure this path is mutable. */
2829 SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, pool));
2830 tb->node = parent_path->node;
2832 if (tb->base_checksum)
2834 svn_checksum_t *checksum;
2836 /* Until we finalize the node, its data_key points to the old
2837 contents, in other words, the base text. */
2838 SVN_ERR(svn_fs_fs__dag_file_checksum(&checksum, tb->node,
2839 tb->base_checksum->kind, pool));
2840 if (!svn_checksum_match(tb->base_checksum, checksum))
2841 return svn_checksum_mismatch_err(tb->base_checksum, checksum, pool,
2842 _("Base checksum mismatch on '%s'"),
2846 /* Make a readable "source" stream out of the current contents of
2847 ROOT/PATH; obviously, this must done in the context of a db_txn.
2848 The stream is returned in tb->source_stream. */
2849 SVN_ERR(svn_fs_fs__dag_get_contents(&(tb->source_stream),
2850 tb->node, tb->pool));
2852 /* Make a writable "target" stream */
2853 SVN_ERR(svn_fs_fs__dag_get_edit_stream(&(tb->target_stream), tb->node,
2856 /* Make a writable "string" stream which writes data to
2857 tb->target_string. */
2858 tb->target_string = svn_stringbuf_create_empty(tb->pool);
2859 tb->string_stream = svn_stream_create(tb, tb->pool);
2860 svn_stream_set_write(tb->string_stream, write_to_string);
2862 /* Now, create a custom window handler that uses our two streams. */
2863 svn_txdelta_apply(tb->source_stream,
2869 &(tb->interpreter_baton));
2871 /* Make a record of this modification in the changes table. */
2872 return add_change(tb->root->fs, txn_id, tb->path,
2873 svn_fs_fs__dag_get_id(tb->node),
2874 svn_fs_path_change_modify, TRUE, FALSE, svn_node_file,
2875 SVN_INVALID_REVNUM, NULL, pool);
2879 /* Set *CONTENTS_P and *CONTENTS_BATON_P to a window handler and baton
2880 that will accept text delta windows to modify the contents of PATH
2881 under ROOT. Allocations are in POOL. */
2882 static svn_error_t *
2883 fs_apply_textdelta(svn_txdelta_window_handler_t *contents_p,
2884 void **contents_baton_p,
2885 svn_fs_root_t *root,
2887 svn_checksum_t *base_checksum,
2888 svn_checksum_t *result_checksum,
2891 txdelta_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
2894 tb->path = svn_fs__canonicalize_abspath(path, pool);
2896 tb->base_checksum = svn_checksum_dup(base_checksum, pool);
2897 tb->result_checksum = svn_checksum_dup(result_checksum, pool);
2899 SVN_ERR(apply_textdelta(tb, pool));
2901 *contents_p = window_consumer;
2902 *contents_baton_p = tb;
2903 return SVN_NO_ERROR;
2906 /* --- End machinery for svn_fs_apply_textdelta() --- */
2908 /* --- Machinery for svn_fs_apply_text() --- */
2910 /* Baton for svn_fs_apply_text(). */
2913 /* The original file info */
2914 svn_fs_root_t *root;
2917 /* Derived from the file info */
2920 /* The returned stream that will accept the file's new contents. */
2921 svn_stream_t *stream;
2923 /* The actual fs stream that the returned stream will write to. */
2924 svn_stream_t *file_stream;
2926 /* MD5 digest for the final fulltext written to the file. May
2927 be null, in which case ignored. */
2928 svn_checksum_t *result_checksum;
2930 /* Pool used by db txns */
2935 /* A wrapper around svn_fs_fs__dag_finalize_edits, but for
2936 * fulltext data, not text deltas. Closes BATON->file_stream.
2938 * Note: If you're confused about how this function relates to another
2939 * of similar name, think of it this way:
2941 * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits()
2942 * svn_fs_apply_text() ==> ... ==> txn_body_fulltext_finalize_edits()
2945 /* Write function for the publically returned stream. */
2946 static svn_error_t *
2947 text_stream_writer(void *baton,
2951 struct text_baton_t *tb = baton;
2953 /* Psst, here's some data. Pass it on to the -real- file stream. */
2954 return svn_stream_write(tb->file_stream, data, len);
2957 /* Close function for the publically returned stream. */
2958 static svn_error_t *
2959 text_stream_closer(void *baton)
2961 struct text_baton_t *tb = baton;
2963 /* Close the internal-use stream. ### This used to be inside of
2964 txn_body_fulltext_finalize_edits(), but that invoked a nested
2965 Berkeley DB transaction -- scandalous! */
2966 SVN_ERR(svn_stream_close(tb->file_stream));
2968 /* Need to tell fs that we're done sending text */
2969 return svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum,
2974 /* Helper function for fs_apply_text. BATON is of type
2976 static svn_error_t *
2977 apply_text(void *baton, apr_pool_t *pool)
2979 struct text_baton_t *tb = baton;
2980 parent_path_t *parent_path;
2981 const char *txn_id = tb->root->txn;
2983 /* Call open_path with no flags, as we want this to return an error
2984 if the node for which we are searching doesn't exist. */
2985 SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id, pool));
2987 /* Check (non-recursively) to see if path is locked; if so, check
2988 that we can use it. */
2989 if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2990 SVN_ERR(svn_fs_fs__allow_locked_operation(tb->path, tb->root->fs,
2991 FALSE, FALSE, pool));
2993 /* Now, make sure this path is mutable. */
2994 SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, pool));
2995 tb->node = parent_path->node;
2997 /* Make a writable stream for replacing the file's text. */
2998 SVN_ERR(svn_fs_fs__dag_get_edit_stream(&(tb->file_stream), tb->node,
3001 /* Create a 'returnable' stream which writes to the file_stream. */
3002 tb->stream = svn_stream_create(tb, tb->pool);
3003 svn_stream_set_write(tb->stream, text_stream_writer);
3004 svn_stream_set_close(tb->stream, text_stream_closer);
3006 /* Make a record of this modification in the changes table. */
3007 return add_change(tb->root->fs, txn_id, tb->path,
3008 svn_fs_fs__dag_get_id(tb->node),
3009 svn_fs_path_change_modify, TRUE, FALSE, svn_node_file,
3010 SVN_INVALID_REVNUM, NULL, pool);
3014 /* Return a writable stream that will set the contents of PATH under
3015 ROOT. RESULT_CHECKSUM is the MD5 checksum of the final result.
3016 Temporary allocations are in POOL. */
3017 static svn_error_t *
3018 fs_apply_text(svn_stream_t **contents_p,
3019 svn_fs_root_t *root,
3021 svn_checksum_t *result_checksum,
3024 struct text_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
3027 tb->path = svn_fs__canonicalize_abspath(path, pool);
3029 tb->result_checksum = svn_checksum_dup(result_checksum, pool);
3031 SVN_ERR(apply_text(tb, pool));
3033 *contents_p = tb->stream;
3034 return SVN_NO_ERROR;
3037 /* --- End machinery for svn_fs_apply_text() --- */
3040 /* Check if the contents of PATH1 under ROOT1 are different from the
3041 contents of PATH2 under ROOT2. If they are different set
3042 *CHANGED_P to TRUE, otherwise set it to FALSE. */
3043 static svn_error_t *
3044 fs_contents_changed(svn_boolean_t *changed_p,
3045 svn_fs_root_t *root1,
3047 svn_fs_root_t *root2,
3051 dag_node_t *node1, *node2;
3053 /* Check that roots are in the same fs. */
3054 if (root1->fs != root2->fs)
3055 return svn_error_create
3056 (SVN_ERR_FS_GENERAL, NULL,
3057 _("Cannot compare file contents between two different filesystems"));
3059 /* Check that both paths are files. */
3061 svn_node_kind_t kind;
3063 SVN_ERR(svn_fs_fs__check_path(&kind, root1, path1, pool));
3064 if (kind != svn_node_file)
3065 return svn_error_createf
3066 (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path1);
3068 SVN_ERR(svn_fs_fs__check_path(&kind, root2, path2, pool));
3069 if (kind != svn_node_file)
3070 return svn_error_createf
3071 (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path2);
3074 SVN_ERR(get_dag(&node1, root1, path1, TRUE, pool));
3075 SVN_ERR(get_dag(&node2, root2, path2, TRUE, pool));
3076 return svn_fs_fs__dag_things_different(NULL, changed_p,
3082 /* Public interface to computing file text deltas. */
3084 static svn_error_t *
3085 fs_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
3086 svn_fs_root_t *source_root,
3087 const char *source_path,
3088 svn_fs_root_t *target_root,
3089 const char *target_path,
3092 dag_node_t *source_node, *target_node;
3094 if (source_root && source_path)
3095 SVN_ERR(get_dag(&source_node, source_root, source_path, TRUE, pool));
3098 SVN_ERR(get_dag(&target_node, target_root, target_path, TRUE, pool));
3100 /* Create a delta stream that turns the source into the target. */
3101 return svn_fs_fs__dag_get_file_delta_stream(stream_p, source_node,
3107 /* Finding Changes */
3109 /* Set *CHANGED_PATHS_P to a newly allocated hash containing
3110 descriptions of the paths changed under ROOT. The hash is keyed
3111 with const char * paths and has svn_fs_path_change2_t * values. Use
3112 POOL for all allocations. */
3113 static svn_error_t *
3114 fs_paths_changed(apr_hash_t **changed_paths_p,
3115 svn_fs_root_t *root,
3118 if (root->is_txn_root)
3119 return svn_fs_fs__txn_changes_fetch(changed_paths_p, root->fs, root->txn,
3123 fs_rev_root_data_t *frd = root->fsap_data;
3124 return svn_fs_fs__paths_changed(changed_paths_p, root->fs, root->rev,
3125 frd->copyfrom_cache, pool);
3131 /* Our coolio opaque history object. */
3132 typedef struct fs_history_data_t
3134 /* filesystem object */
3137 /* path and revision of historical location */
3139 svn_revnum_t revision;
3141 /* internal-use hints about where to resume the history search. */
3142 const char *path_hint;
3143 svn_revnum_t rev_hint;
3145 /* FALSE until the first call to svn_fs_history_prev(). */
3146 svn_boolean_t is_interesting;
3147 } fs_history_data_t;
3149 static svn_fs_history_t *
3150 assemble_history(svn_fs_t *fs,
3152 svn_revnum_t revision,
3153 svn_boolean_t is_interesting,
3154 const char *path_hint,
3155 svn_revnum_t rev_hint,
3159 /* Set *HISTORY_P to an opaque node history object which represents
3160 PATH under ROOT. ROOT must be a revision root. Use POOL for all
3162 static svn_error_t *
3163 fs_node_history(svn_fs_history_t **history_p,
3164 svn_fs_root_t *root,
3168 svn_node_kind_t kind;
3170 /* We require a revision root. */
3171 if (root->is_txn_root)
3172 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
3174 /* And we require that the path exist in the root. */
3175 SVN_ERR(svn_fs_fs__check_path(&kind, root, path, pool));
3176 if (kind == svn_node_none)
3177 return SVN_FS__NOT_FOUND(root, path);
3179 /* Okay, all seems well. Build our history object and return it. */
3180 *history_p = assemble_history(root->fs,
3181 svn_fs__canonicalize_abspath(path, pool),
3182 root->rev, FALSE, NULL,
3183 SVN_INVALID_REVNUM, pool);
3184 return SVN_NO_ERROR;
3187 /* Find the youngest copyroot for path PARENT_PATH or its parents in
3188 filesystem FS, and store the copyroot in *REV_P and *PATH_P.
3189 Perform all allocations in POOL. */
3190 static svn_error_t *
3191 find_youngest_copyroot(svn_revnum_t *rev_p,
3192 const char **path_p,
3194 parent_path_t *parent_path,
3197 svn_revnum_t rev_mine;
3198 svn_revnum_t rev_parent = SVN_INVALID_REVNUM;
3199 const char *path_mine;
3200 const char *path_parent = NULL;
3202 /* First find our parent's youngest copyroot. */
3203 if (parent_path->parent)
3204 SVN_ERR(find_youngest_copyroot(&rev_parent, &path_parent, fs,
3205 parent_path->parent, pool));
3207 /* Find our copyroot. */
3208 SVN_ERR(svn_fs_fs__dag_get_copyroot(&rev_mine, &path_mine,
3209 parent_path->node));
3211 /* If a parent and child were copied to in the same revision, prefer
3212 the child copy target, since it is the copy relevant to the
3213 history of the child. */
3214 if (rev_mine >= rev_parent)
3217 *path_p = path_mine;
3221 *rev_p = rev_parent;
3222 *path_p = path_parent;
3225 return SVN_NO_ERROR;
3229 static svn_error_t *fs_closest_copy(svn_fs_root_t **root_p,
3230 const char **path_p,
3231 svn_fs_root_t *root,
3235 svn_fs_t *fs = root->fs;
3236 parent_path_t *parent_path, *copy_dst_parent_path;
3237 svn_revnum_t copy_dst_rev, created_rev;
3238 const char *copy_dst_path;
3239 svn_fs_root_t *copy_dst_root;
3240 dag_node_t *copy_dst_node;
3241 svn_node_kind_t kind;
3243 /* Initialize return values. */
3247 path = svn_fs__canonicalize_abspath(path, pool);
3248 SVN_ERR(open_path(&parent_path, root, path, 0, NULL, pool));
3250 /* Find the youngest copyroot in the path of this node-rev, which
3251 will indicate the target of the innermost copy affecting the
3253 SVN_ERR(find_youngest_copyroot(©_dst_rev, ©_dst_path,
3254 fs, parent_path, pool));
3255 if (copy_dst_rev == 0) /* There are no copies affecting this node-rev. */
3256 return SVN_NO_ERROR;
3258 /* It is possible that this node was created from scratch at some
3259 revision between COPY_DST_REV and REV. Make sure that PATH
3260 exists as of COPY_DST_REV and is related to this node-rev. */
3261 SVN_ERR(svn_fs_fs__revision_root(©_dst_root, fs, copy_dst_rev, pool));
3262 SVN_ERR(svn_fs_fs__check_path(&kind, copy_dst_root, path, pool));
3263 if (kind == svn_node_none)
3264 return SVN_NO_ERROR;
3265 SVN_ERR(open_path(©_dst_parent_path, copy_dst_root, path,
3266 open_path_node_only, NULL, pool));
3267 copy_dst_node = copy_dst_parent_path->node;
3268 if (! svn_fs_fs__id_check_related(svn_fs_fs__dag_get_id(copy_dst_node),
3269 svn_fs_fs__dag_get_id(parent_path->node)))
3270 return SVN_NO_ERROR;
3272 /* One final check must be done here. If you copy a directory and
3273 create a new entity somewhere beneath that directory in the same
3274 txn, then we can't claim that the copy affected the new entity.
3275 For example, if you do:
3278 create dir2/new-thing
3281 then dir2/new-thing was not affected by the copy of dir1 to dir2.
3282 We detect this situation by asking if PATH@COPY_DST_REV's
3283 created-rev is COPY_DST_REV, and that node-revision has no
3284 predecessors, then there is no relevant closest copy.
3286 SVN_ERR(svn_fs_fs__dag_get_revision(&created_rev, copy_dst_node, pool));
3287 if (created_rev == copy_dst_rev)
3289 const svn_fs_id_t *pred;
3290 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred, copy_dst_node));
3292 return SVN_NO_ERROR;
3295 /* The copy destination checks out. Return it. */
3296 *root_p = copy_dst_root;
3297 *path_p = copy_dst_path;
3298 return SVN_NO_ERROR;
3302 /* Set *PREV_PATH and *PREV_REV to the path and revision which
3303 represent the location at which PATH in FS was located immediately
3304 prior to REVISION iff there was a copy operation (to PATH or one of
3305 its parent directories) between that previous location and
3308 If there was no such copy operation in that portion of PATH's
3309 history, set *PREV_PATH to NULL and *PREV_REV to SVN_INVALID_REVNUM. */
3310 static svn_error_t *
3311 prev_location(const char **prev_path,
3312 svn_revnum_t *prev_rev,
3314 svn_fs_root_t *root,
3318 const char *copy_path, *copy_src_path, *remainder_path;
3319 svn_fs_root_t *copy_root;
3320 svn_revnum_t copy_src_rev;
3322 /* Ask about the most recent copy which affected PATH@REVISION. If
3323 there was no such copy, we're done. */
3324 SVN_ERR(fs_closest_copy(©_root, ©_path, root, path, pool));
3327 *prev_rev = SVN_INVALID_REVNUM;
3329 return SVN_NO_ERROR;
3332 /* Ultimately, it's not the path of the closest copy's source that
3333 we care about -- it's our own path's location in the copy source
3334 revision. So we'll tack the relative path that expresses the
3335 difference between the copy destination and our path in the copy
3336 revision onto the copy source path to determine this information.
3338 In other words, if our path is "/branches/my-branch/foo/bar", and
3339 we know that the closest relevant copy was a copy of "/trunk" to
3340 "/branches/my-branch", then that relative path under the copy
3341 destination is "/foo/bar". Tacking that onto the copy source
3342 path tells us that our path was located at "/trunk/foo/bar"
3345 SVN_ERR(fs_copied_from(©_src_rev, ©_src_path,
3346 copy_root, copy_path, pool));
3347 remainder_path = svn_fspath__skip_ancestor(copy_path, path);
3348 *prev_path = svn_fspath__join(copy_src_path, remainder_path, pool);
3349 *prev_rev = copy_src_rev;
3350 return SVN_NO_ERROR;
3354 static svn_error_t *
3355 fs_node_origin_rev(svn_revnum_t *revision,
3356 svn_fs_root_t *root,
3360 svn_fs_t *fs = root->fs;
3361 const svn_fs_id_t *given_noderev_id, *cached_origin_id;
3362 const char *node_id, *dash;
3364 path = svn_fs__canonicalize_abspath(path, pool);
3366 /* Check the cache first. */
3367 SVN_ERR(svn_fs_fs__node_id(&given_noderev_id, root, path, pool));
3368 node_id = svn_fs_fs__id_node_id(given_noderev_id);
3370 /* Is it a brand new uncommitted node? */
3371 if (node_id[0] == '_')
3373 *revision = SVN_INVALID_REVNUM;
3374 return SVN_NO_ERROR;
3377 /* Maybe this is a new-style node ID that just has the revision
3378 sitting right in it. */
3379 dash = strchr(node_id, '-');
3380 if (dash && *(dash+1))
3382 *revision = SVN_STR_TO_REV(dash + 1);
3383 return SVN_NO_ERROR;
3386 /* The root node always has ID 0, created in revision 0 and will never
3387 use the new-style ID format. */
3388 if (strcmp(node_id, "0") == 0)
3391 return SVN_NO_ERROR;
3394 /* OK, it's an old-style ID? Maybe it's cached. */
3395 SVN_ERR(svn_fs_fs__get_node_origin(&cached_origin_id,
3399 if (cached_origin_id != NULL)
3401 *revision = svn_fs_fs__id_rev(cached_origin_id);
3402 return SVN_NO_ERROR;
3406 /* Ah well, the answer isn't in the ID itself or in the cache.
3407 Let's actually calculate it, then. */
3408 svn_fs_root_t *curroot = root;
3409 apr_pool_t *subpool = svn_pool_create(pool);
3410 apr_pool_t *predidpool = svn_pool_create(pool);
3411 svn_stringbuf_t *lastpath = svn_stringbuf_create(path, pool);
3412 svn_revnum_t lastrev = SVN_INVALID_REVNUM;
3414 const svn_fs_id_t *pred_id;
3416 /* Walk the closest-copy chain back to the first copy in our history.
3418 NOTE: We merely *assume* that this is faster than walking the
3419 predecessor chain, because we *assume* that copies of parent
3420 directories happen less often than modifications to a given item. */
3423 svn_revnum_t currev;
3424 const char *curpath = lastpath->data;
3426 svn_pool_clear(subpool);
3428 /* Get a root pointing to LASTREV. (The first time around,
3429 LASTREV is invalid, but that's cool because CURROOT is
3430 already initialized.) */
3431 if (SVN_IS_VALID_REVNUM(lastrev))
3432 SVN_ERR(svn_fs_fs__revision_root(&curroot, fs, lastrev, subpool));
3434 /* Find the previous location using the closest-copy shortcut. */
3435 SVN_ERR(prev_location(&curpath, &currev, fs, curroot, curpath,
3440 /* Update our LASTPATH and LASTREV variables (which survive
3442 svn_stringbuf_set(lastpath, curpath);
3446 /* Walk the predecessor links back to origin. */
3447 SVN_ERR(svn_fs_fs__node_id(&pred_id, curroot, lastpath->data, predidpool));
3450 svn_pool_clear(subpool);
3451 SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, subpool));
3453 /* Why not just fetch the predecessor ID in PREDIDPOOL?
3454 Because svn_fs_fs__dag_get_predecessor_id() doesn't
3455 necessarily honor the passed-in pool, and might return a
3456 value cached in the node (which is allocated in
3457 SUBPOOL... maybe). */
3458 svn_pool_clear(predidpool);
3459 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
3460 pred_id = pred_id ? svn_fs_fs__id_copy(pred_id, predidpool) : NULL;
3464 /* When we get here, NODE should be the first node-revision in our
3466 SVN_ERR(svn_fs_fs__dag_get_revision(revision, node, pool));
3468 /* Wow, I don't want to have to do all that again. Let's cache
3470 if (node_id[0] != '_')
3471 SVN_ERR(svn_fs_fs__set_node_origin(fs, node_id,
3472 svn_fs_fs__dag_get_id(node), pool));
3474 svn_pool_destroy(subpool);
3475 svn_pool_destroy(predidpool);
3476 return SVN_NO_ERROR;
3481 struct history_prev_args
3483 svn_fs_history_t **prev_history_p;
3484 svn_fs_history_t *history;
3485 svn_boolean_t cross_copies;
3490 static svn_error_t *
3491 history_prev(void *baton, apr_pool_t *pool)
3493 struct history_prev_args *args = baton;
3494 svn_fs_history_t **prev_history = args->prev_history_p;
3495 svn_fs_history_t *history = args->history;
3496 fs_history_data_t *fhd = history->fsap_data;
3497 const char *commit_path, *src_path, *path = fhd->path;
3498 svn_revnum_t commit_rev, src_rev, dst_rev;
3499 svn_revnum_t revision = fhd->revision;
3500 apr_pool_t *retpool = args->pool;
3501 svn_fs_t *fs = fhd->fs;
3502 parent_path_t *parent_path;
3504 svn_fs_root_t *root;
3505 svn_boolean_t reported = fhd->is_interesting;
3506 svn_revnum_t copyroot_rev;
3507 const char *copyroot_path;
3509 /* Initialize our return value. */
3510 *prev_history = NULL;
3512 /* If our last history report left us hints about where to pickup
3513 the chase, then our last report was on the destination of a
3514 copy. If we are crossing copies, start from those locations,
3515 otherwise, we're all done here. */
3516 if (fhd->path_hint && SVN_IS_VALID_REVNUM(fhd->rev_hint))
3519 if (! args->cross_copies)
3520 return SVN_NO_ERROR;
3521 path = fhd->path_hint;
3522 revision = fhd->rev_hint;
3525 /* Construct a ROOT for the current revision. */
3526 SVN_ERR(svn_fs_fs__revision_root(&root, fs, revision, pool));
3528 /* Open PATH/REVISION, and get its node and a bunch of other
3530 SVN_ERR(open_path(&parent_path, root, path, 0, NULL, pool));
3531 node = parent_path->node;
3532 commit_path = svn_fs_fs__dag_get_created_path(node);
3533 SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, pool));
3535 /* The Subversion filesystem is written in such a way that a given
3536 line of history may have at most one interesting history point
3537 per filesystem revision. Either that node was edited (and
3538 possibly copied), or it was copied but not edited. And a copy
3539 source cannot be from the same revision as its destination. So,
3540 if our history revision matches its node's commit revision, we
3542 if (revision == commit_rev)
3546 /* ... we either have not yet reported on this revision (and
3547 need now to do so) ... */
3548 *prev_history = assemble_history(fs,
3549 apr_pstrdup(retpool, commit_path),
3550 commit_rev, TRUE, NULL,
3551 SVN_INVALID_REVNUM, retpool);
3552 return SVN_NO_ERROR;
3556 /* ... or we *have* reported on this revision, and must now
3557 progress toward this node's predecessor (unless there is
3558 no predecessor, in which case we're all done!). */
3559 const svn_fs_id_t *pred_id;
3561 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
3563 return SVN_NO_ERROR;
3565 /* Replace NODE and friends with the information from its
3567 SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, pool));
3568 commit_path = svn_fs_fs__dag_get_created_path(node);
3569 SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, pool));
3573 /* Find the youngest copyroot in the path of this node, including
3575 SVN_ERR(find_youngest_copyroot(©root_rev, ©root_path, fs,
3576 parent_path, pool));
3578 /* Initialize some state variables. */
3580 src_rev = SVN_INVALID_REVNUM;
3581 dst_rev = SVN_INVALID_REVNUM;
3583 if (copyroot_rev > commit_rev)
3585 const char *remainder_path;
3586 const char *copy_dst, *copy_src;
3587 svn_fs_root_t *copyroot_root;
3589 SVN_ERR(svn_fs_fs__revision_root(©root_root, fs, copyroot_rev,
3591 SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, FALSE, pool));
3592 copy_dst = svn_fs_fs__dag_get_created_path(node);
3594 /* If our current path was the very destination of the copy,
3595 then our new current path will be the copy source. If our
3596 current path was instead the *child* of the destination of
3597 the copy, then figure out its previous location by taking its
3598 path relative to the copy destination and appending that to
3599 the copy source. Finally, if our current path doesn't meet
3600 one of these other criteria ... ### for now just fallback to
3601 the old copy hunt algorithm. */
3602 remainder_path = svn_fspath__skip_ancestor(copy_dst, path);
3606 /* If we get here, then our current path is the destination
3607 of, or the child of the destination of, a copy. Fill
3608 in the return values and get outta here. */
3609 SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(&src_rev, node));
3610 SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(©_src, node));
3612 dst_rev = copyroot_rev;
3613 src_path = svn_fspath__join(copy_src, remainder_path, pool);
3617 /* If we calculated a copy source path and revision, we'll make a
3618 'copy-style' history object. */
3619 if (src_path && SVN_IS_VALID_REVNUM(src_rev))
3621 svn_boolean_t retry = FALSE;
3623 /* It's possible for us to find a copy location that is the same
3624 as the history point we've just reported. If that happens,
3625 we simply need to take another trip through this history
3627 if ((dst_rev == revision) && reported)
3630 *prev_history = assemble_history(fs, apr_pstrdup(retpool, path),
3632 src_path, src_rev, retpool);
3636 *prev_history = assemble_history(fs, apr_pstrdup(retpool, commit_path),
3637 commit_rev, TRUE, NULL,
3638 SVN_INVALID_REVNUM, retpool);
3641 return SVN_NO_ERROR;
3645 /* Implement svn_fs_history_prev, set *PREV_HISTORY_P to a new
3646 svn_fs_history_t object that represents the predecessory of
3647 HISTORY. If CROSS_COPIES is true, *PREV_HISTORY_P may be related
3648 only through a copy operation. Perform all allocations in POOL. */
3649 static svn_error_t *
3650 fs_history_prev(svn_fs_history_t **prev_history_p,
3651 svn_fs_history_t *history,
3652 svn_boolean_t cross_copies,
3655 svn_fs_history_t *prev_history = NULL;
3656 fs_history_data_t *fhd = history->fsap_data;
3657 svn_fs_t *fs = fhd->fs;
3659 /* Special case: the root directory changes in every single
3660 revision, no exceptions. And, the root can't be the target (or
3661 child of a target -- duh) of a copy. So, if that's our path,
3662 then we need only decrement our revision by 1, and there you go. */
3663 if (strcmp(fhd->path, "/") == 0)
3665 if (! fhd->is_interesting)
3666 prev_history = assemble_history(fs, "/", fhd->revision,
3667 1, NULL, SVN_INVALID_REVNUM, pool);
3668 else if (fhd->revision > 0)
3669 prev_history = assemble_history(fs, "/", fhd->revision - 1,
3670 1, NULL, SVN_INVALID_REVNUM, pool);
3674 struct history_prev_args args;
3675 prev_history = history;
3679 args.prev_history_p = &prev_history;
3680 args.history = prev_history;
3681 args.cross_copies = cross_copies;
3683 SVN_ERR(history_prev(&args, pool));
3687 fhd = prev_history->fsap_data;
3688 if (fhd->is_interesting)
3693 *prev_history_p = prev_history;
3694 return SVN_NO_ERROR;
3698 /* Set *PATH and *REVISION to the path and revision for the HISTORY
3699 object. Use POOL for all allocations. */
3700 static svn_error_t *
3701 fs_history_location(const char **path,
3702 svn_revnum_t *revision,
3703 svn_fs_history_t *history,
3706 fs_history_data_t *fhd = history->fsap_data;
3708 *path = apr_pstrdup(pool, fhd->path);
3709 *revision = fhd->revision;
3710 return SVN_NO_ERROR;
3713 static history_vtable_t history_vtable = {
3718 /* Return a new history object (marked as "interesting") for PATH and
3719 REVISION, allocated in POOL, and with its members set to the values
3720 of the parameters provided. Note that PATH and PATH_HINT are not
3721 duped into POOL -- it is the responsibility of the caller to ensure
3722 that this happens. */
3723 static svn_fs_history_t *
3724 assemble_history(svn_fs_t *fs,
3726 svn_revnum_t revision,
3727 svn_boolean_t is_interesting,
3728 const char *path_hint,
3729 svn_revnum_t rev_hint,
3732 svn_fs_history_t *history = apr_pcalloc(pool, sizeof(*history));
3733 fs_history_data_t *fhd = apr_pcalloc(pool, sizeof(*fhd));
3734 fhd->path = svn_fs__canonicalize_abspath(path, pool);
3735 fhd->revision = revision;
3736 fhd->is_interesting = is_interesting;
3737 fhd->path_hint = path_hint;
3738 fhd->rev_hint = rev_hint;
3741 history->vtable = &history_vtable;
3742 history->fsap_data = fhd;
3747 /* mergeinfo queries */
3750 /* DIR_DAG is a directory DAG node which has mergeinfo in its
3751 descendants. This function iterates over its children. For each
3752 child with immediate mergeinfo, it adds its mergeinfo to
3753 RESULT_CATALOG. appropriate arguments. For each child with
3754 descendants with mergeinfo, it recurses. Note that it does *not*
3755 call the action on the path for DIR_DAG itself.
3757 POOL is used for temporary allocations, including the mergeinfo
3758 hashes passed to actions; RESULT_POOL is used for the mergeinfo added
3761 static svn_error_t *
3762 crawl_directory_dag_for_mergeinfo(svn_fs_root_t *root,
3763 const char *this_path,
3764 dag_node_t *dir_dag,
3765 svn_mergeinfo_catalog_t result_catalog,
3766 apr_pool_t *result_pool,
3767 apr_pool_t *scratch_pool)
3769 apr_hash_t *entries;
3770 apr_hash_index_t *hi;
3771 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3773 SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, dir_dag,
3776 for (hi = apr_hash_first(scratch_pool, entries);
3778 hi = apr_hash_next(hi))
3780 svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
3781 const char *kid_path;
3782 dag_node_t *kid_dag;
3783 svn_boolean_t has_mergeinfo, go_down;
3785 svn_pool_clear(iterpool);
3787 kid_path = svn_fspath__join(this_path, dirent->name, iterpool);
3788 SVN_ERR(get_dag(&kid_dag, root, kid_path, TRUE, iterpool));
3790 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, kid_dag));
3791 SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down, kid_dag));
3795 /* Save this particular node's mergeinfo. */
3796 apr_hash_t *proplist;
3797 svn_mergeinfo_t kid_mergeinfo;
3798 svn_string_t *mergeinfo_string;
3801 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, kid_dag, iterpool));
3802 mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
3803 if (!mergeinfo_string)
3805 svn_string_t *idstr = svn_fs_fs__id_unparse(dirent->id, iterpool);
3806 return svn_error_createf
3807 (SVN_ERR_FS_CORRUPT, NULL,
3808 _("Node-revision #'%s' claims to have mergeinfo but doesn't"),
3812 /* Issue #3896: If a node has syntactically invalid mergeinfo, then
3813 treat it as if no mergeinfo is present rather than raising a parse
3815 err = svn_mergeinfo_parse(&kid_mergeinfo,
3816 mergeinfo_string->data,
3820 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
3821 svn_error_clear(err);
3823 return svn_error_trace(err);
3827 svn_hash_sets(result_catalog, apr_pstrdup(result_pool, kid_path),
3833 SVN_ERR(crawl_directory_dag_for_mergeinfo(root,
3841 svn_pool_destroy(iterpool);
3842 return SVN_NO_ERROR;
3845 /* Return the cache key as a combination of REV_ROOT->REV, the inheritance
3846 flags INHERIT and ADJUST_INHERITED_MERGEINFO, and the PATH. The result
3847 will be allocated in POOL..
3850 mergeinfo_cache_key(const char *path,
3851 svn_fs_root_t *rev_root,
3852 svn_mergeinfo_inheritance_t inherit,
3853 svn_boolean_t adjust_inherited_mergeinfo,
3856 apr_int64_t number = rev_root->rev;
3858 + (inherit == svn_mergeinfo_nearest_ancestor ? 2 : 0)
3859 + (adjust_inherited_mergeinfo ? 1 : 0);
3861 return svn_fs_fs__combine_number_and_string(number, path, pool);
3864 /* Calculates the mergeinfo for PATH under REV_ROOT using inheritance
3865 type INHERIT. Returns it in *MERGEINFO, or NULL if there is none.
3866 The result is allocated in RESULT_POOL; SCRATCH_POOL is
3867 used for temporary allocations.
3869 static svn_error_t *
3870 get_mergeinfo_for_path_internal(svn_mergeinfo_t *mergeinfo,
3871 svn_fs_root_t *rev_root,
3873 svn_mergeinfo_inheritance_t inherit,
3874 svn_boolean_t adjust_inherited_mergeinfo,
3875 apr_pool_t *result_pool,
3876 apr_pool_t *scratch_pool)
3878 parent_path_t *parent_path, *nearest_ancestor;
3879 apr_hash_t *proplist;
3880 svn_string_t *mergeinfo_string;
3882 path = svn_fs__canonicalize_abspath(path, scratch_pool);
3884 SVN_ERR(open_path(&parent_path, rev_root, path, 0, NULL, scratch_pool));
3886 if (inherit == svn_mergeinfo_nearest_ancestor && ! parent_path->parent)
3887 return SVN_NO_ERROR;
3889 if (inherit == svn_mergeinfo_nearest_ancestor)
3890 nearest_ancestor = parent_path->parent;
3892 nearest_ancestor = parent_path;
3896 svn_boolean_t has_mergeinfo;
3898 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo,
3899 nearest_ancestor->node));
3903 /* No need to loop if we're looking for explicit mergeinfo. */
3904 if (inherit == svn_mergeinfo_explicit)
3906 return SVN_NO_ERROR;
3909 nearest_ancestor = nearest_ancestor->parent;
3911 /* Run out? There's no mergeinfo. */
3912 if (!nearest_ancestor)
3914 return SVN_NO_ERROR;
3918 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, nearest_ancestor->node,
3920 mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
3921 if (!mergeinfo_string)
3922 return svn_error_createf
3923 (SVN_ERR_FS_CORRUPT, NULL,
3924 _("Node-revision '%s@%ld' claims to have mergeinfo but doesn't"),
3925 parent_path_path(nearest_ancestor, scratch_pool), rev_root->rev);
3927 /* Parse the mergeinfo; store the result in *MERGEINFO. */
3929 /* Issue #3896: If a node has syntactically invalid mergeinfo, then
3930 treat it as if no mergeinfo is present rather than raising a parse
3932 svn_error_t *err = svn_mergeinfo_parse(mergeinfo,
3933 mergeinfo_string->data,
3937 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
3939 svn_error_clear(err);
3943 return svn_error_trace(err);
3947 /* If our nearest ancestor is the very path we inquired about, we
3948 can return the mergeinfo results directly. Otherwise, we're
3949 inheriting the mergeinfo, so we need to a) remove non-inheritable
3950 ranges and b) telescope the merged-from paths. */
3951 if (adjust_inherited_mergeinfo && (nearest_ancestor != parent_path))
3953 svn_mergeinfo_t tmp_mergeinfo;
3955 SVN_ERR(svn_mergeinfo_inheritable2(&tmp_mergeinfo, *mergeinfo,
3956 NULL, SVN_INVALID_REVNUM,
3957 SVN_INVALID_REVNUM, TRUE,
3958 scratch_pool, scratch_pool));
3959 SVN_ERR(svn_fs__append_to_merged_froms(mergeinfo, tmp_mergeinfo,
3960 parent_path_relpath(
3961 parent_path, nearest_ancestor,
3966 return SVN_NO_ERROR;
3969 /* Caching wrapper around get_mergeinfo_for_path_internal().
3971 static svn_error_t *
3972 get_mergeinfo_for_path(svn_mergeinfo_t *mergeinfo,
3973 svn_fs_root_t *rev_root,
3975 svn_mergeinfo_inheritance_t inherit,
3976 svn_boolean_t adjust_inherited_mergeinfo,
3977 apr_pool_t *result_pool,
3978 apr_pool_t *scratch_pool)
3980 fs_fs_data_t *ffd = rev_root->fs->fsap_data;
3981 const char *cache_key;
3982 svn_boolean_t found = FALSE;
3983 svn_stringbuf_t *mergeinfo_exists;
3987 cache_key = mergeinfo_cache_key(path, rev_root, inherit,
3988 adjust_inherited_mergeinfo, scratch_pool);
3989 if (ffd->mergeinfo_existence_cache)
3991 SVN_ERR(svn_cache__get((void **)&mergeinfo_exists, &found,
3992 ffd->mergeinfo_existence_cache,
3993 cache_key, result_pool));
3994 if (found && mergeinfo_exists->data[0] == '1')
3995 SVN_ERR(svn_cache__get((void **)mergeinfo, &found,
3996 ffd->mergeinfo_cache,
3997 cache_key, result_pool));
4002 SVN_ERR(get_mergeinfo_for_path_internal(mergeinfo, rev_root, path,
4004 adjust_inherited_mergeinfo,
4005 result_pool, scratch_pool));
4006 if (ffd->mergeinfo_existence_cache)
4008 mergeinfo_exists = svn_stringbuf_create(*mergeinfo ? "1" : "0",
4010 SVN_ERR(svn_cache__set(ffd->mergeinfo_existence_cache,
4011 cache_key, mergeinfo_exists, scratch_pool));
4013 SVN_ERR(svn_cache__set(ffd->mergeinfo_cache,
4014 cache_key, *mergeinfo, scratch_pool));
4018 return SVN_NO_ERROR;
4021 /* Adds mergeinfo for each descendant of PATH (but not PATH itself)
4022 under ROOT to RESULT_CATALOG. Returned values are allocated in
4023 RESULT_POOL; temporary values in POOL. */
4024 static svn_error_t *
4025 add_descendant_mergeinfo(svn_mergeinfo_catalog_t result_catalog,
4026 svn_fs_root_t *root,
4028 apr_pool_t *result_pool,
4029 apr_pool_t *scratch_pool)
4031 dag_node_t *this_dag;
4032 svn_boolean_t go_down;
4034 SVN_ERR(get_dag(&this_dag, root, path, TRUE, scratch_pool));
4035 SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down,
4038 SVN_ERR(crawl_directory_dag_for_mergeinfo(root,
4044 return SVN_NO_ERROR;
4048 /* Get the mergeinfo for a set of paths, returned in
4049 *MERGEINFO_CATALOG. Returned values are allocated in
4050 POOL, while temporary values are allocated in a sub-pool. */
4051 static svn_error_t *
4052 get_mergeinfos_for_paths(svn_fs_root_t *root,
4053 svn_mergeinfo_catalog_t *mergeinfo_catalog,
4054 const apr_array_header_t *paths,
4055 svn_mergeinfo_inheritance_t inherit,
4056 svn_boolean_t include_descendants,
4057 svn_boolean_t adjust_inherited_mergeinfo,
4058 apr_pool_t *result_pool,
4059 apr_pool_t *scratch_pool)
4061 svn_mergeinfo_catalog_t result_catalog = svn_hash__make(result_pool);
4062 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4065 for (i = 0; i < paths->nelts; i++)
4068 svn_mergeinfo_t path_mergeinfo;
4069 const char *path = APR_ARRAY_IDX(paths, i, const char *);
4071 svn_pool_clear(iterpool);
4073 err = get_mergeinfo_for_path(&path_mergeinfo, root, path,
4074 inherit, adjust_inherited_mergeinfo,
4075 result_pool, iterpool);
4078 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
4080 svn_error_clear(err);
4082 path_mergeinfo = NULL;
4086 return svn_error_trace(err);
4091 svn_hash_sets(result_catalog, path, path_mergeinfo);
4092 if (include_descendants)
4093 SVN_ERR(add_descendant_mergeinfo(result_catalog, root, path,
4094 result_pool, scratch_pool));
4096 svn_pool_destroy(iterpool);
4098 *mergeinfo_catalog = result_catalog;
4099 return SVN_NO_ERROR;
4103 /* Implements svn_fs_get_mergeinfo. */
4104 static svn_error_t *
4105 fs_get_mergeinfo(svn_mergeinfo_catalog_t *catalog,
4106 svn_fs_root_t *root,
4107 const apr_array_header_t *paths,
4108 svn_mergeinfo_inheritance_t inherit,
4109 svn_boolean_t include_descendants,
4110 svn_boolean_t adjust_inherited_mergeinfo,
4111 apr_pool_t *result_pool,
4112 apr_pool_t *scratch_pool)
4114 fs_fs_data_t *ffd = root->fs->fsap_data;
4116 /* We require a revision root. */
4117 if (root->is_txn_root)
4118 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
4120 /* We have to actually be able to find the mergeinfo metadata! */
4121 if (! svn_fs_fs__fs_supports_mergeinfo(root->fs))
4122 return svn_error_createf
4123 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
4124 _("Querying mergeinfo requires version %d of the FSFS filesystem "
4125 "schema; filesystem '%s' uses only version %d"),
4126 SVN_FS_FS__MIN_MERGEINFO_FORMAT, root->fs->path, ffd->format);
4128 /* Retrieve a path -> mergeinfo hash mapping. */
4129 return get_mergeinfos_for_paths(root, catalog, paths,
4131 include_descendants,
4132 adjust_inherited_mergeinfo,
4133 result_pool, scratch_pool);
4137 /* The vtable associated with root objects. */
4138 static root_vtable_t root_vtable = {
4140 svn_fs_fs__check_path,
4143 svn_fs_fs__node_created_rev,
4145 fs_node_created_path,
4151 fs_change_node_prop,
4160 fs_try_process_file_contents,
4164 fs_contents_changed,
4165 fs_get_file_delta_stream,
4170 /* Construct a new root object in FS, allocated from POOL. */
4171 static svn_fs_root_t *
4172 make_root(svn_fs_t *fs,
4175 svn_fs_root_t *root = apr_pcalloc(pool, sizeof(*root));
4179 root->vtable = &root_vtable;
4185 /* Construct a root object referring to the root of REVISION in FS,
4186 whose root directory is ROOT_DIR. Create the new root in POOL. */
4187 static svn_fs_root_t *
4188 make_revision_root(svn_fs_t *fs,
4190 dag_node_t *root_dir,
4193 svn_fs_root_t *root = make_root(fs, pool);
4194 fs_rev_root_data_t *frd = apr_pcalloc(root->pool, sizeof(*frd));
4196 root->is_txn_root = FALSE;
4199 frd->root_dir = root_dir;
4200 frd->copyfrom_cache = svn_hash__make(root->pool);
4202 root->fsap_data = frd;
4208 /* Construct a root object referring to the root of the transaction
4209 named TXN and based on revision BASE_REV in FS, with FLAGS to
4210 describe transaction's behavior. Create the new root in POOL. */
4211 static svn_error_t *
4212 make_txn_root(svn_fs_root_t **root_p,
4215 svn_revnum_t base_rev,
4219 svn_fs_root_t *root = make_root(fs, pool);
4220 fs_txn_root_data_t *frd = apr_pcalloc(root->pool, sizeof(*frd));
4222 root->is_txn_root = TRUE;
4223 root->txn = apr_pstrdup(root->pool, txn);
4224 root->txn_flags = flags;
4225 root->rev = base_rev;
4229 /* Because this cache actually tries to invalidate elements, keep
4230 the number of elements per page down.
4232 Note that since dag_node_cache_invalidate uses svn_cache__iter,
4233 this *cannot* be a memcache-based cache. */
4234 SVN_ERR(svn_cache__create_inprocess(&(frd->txn_node_cache),
4235 svn_fs_fs__dag_serialize,
4236 svn_fs_fs__dag_deserialize,
4237 APR_HASH_KEY_STRING,
4239 apr_pstrcat(pool, txn, ":TXN",
4243 /* Initialize transaction-local caches in FS.
4245 Note that we cannot put those caches in frd because that content
4246 fs root object is not available where we would need it. */
4247 SVN_ERR(svn_fs_fs__initialize_txn_caches(fs, txn, pool));
4249 root->fsap_data = frd;
4252 return SVN_NO_ERROR;
4258 static APR_INLINE const char *
4259 stringify_node(dag_node_t *node,
4262 /* ### TODO: print some PATH@REV to it, too. */
4263 return svn_fs_fs__id_unparse(svn_fs_fs__dag_get_id(node), pool)->data;
4266 /* Check metadata sanity on NODE, and on its children. Manually verify
4267 information for DAG nodes in revision REV, and trust the metadata
4268 accuracy for nodes belonging to older revisions. */
4269 static svn_error_t *
4270 verify_node(dag_node_t *node,
4274 svn_boolean_t has_mergeinfo;
4275 apr_int64_t mergeinfo_count;
4276 const svn_fs_id_t *pred_id;
4277 svn_fs_t *fs = svn_fs_fs__dag_get_fs(node);
4279 svn_node_kind_t kind;
4280 apr_pool_t *iterpool = svn_pool_create(pool);
4282 /* Fetch some data. */
4283 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, node));
4284 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_count, node));
4285 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
4286 SVN_ERR(svn_fs_fs__dag_get_predecessor_count(&pred_count, node));
4287 kind = svn_fs_fs__dag_node_kind(node);
4290 if (mergeinfo_count < 0)
4291 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4292 "Negative mergeinfo-count %" APR_INT64_T_FMT
4294 mergeinfo_count, stringify_node(node, iterpool));
4296 /* Issue #4129. (This check will explicitly catch non-root instances too.) */
4300 int pred_pred_count;
4301 SVN_ERR(svn_fs_fs__dag_get_node(&pred, fs, pred_id, iterpool));
4302 SVN_ERR(svn_fs_fs__dag_get_predecessor_count(&pred_pred_count, pred));
4303 if (pred_pred_count+1 != pred_count)
4304 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4305 "Predecessor count mismatch: "
4306 "%s has %d, but %s has %d",
4307 stringify_node(node, iterpool), pred_count,
4308 stringify_node(pred, iterpool),
4312 /* Kind-dependent verifications. */
4313 if (kind == svn_node_none)
4315 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4316 "Node '%s' has kind 'none'",
4317 stringify_node(node, iterpool));
4319 if (kind == svn_node_file)
4321 if (has_mergeinfo != mergeinfo_count) /* comparing int to bool */
4322 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4323 "File node '%s' has inconsistent mergeinfo: "
4324 "has_mergeinfo=%d, "
4325 "mergeinfo_count=%" APR_INT64_T_FMT,
4326 stringify_node(node, iterpool),
4327 has_mergeinfo, mergeinfo_count);
4329 if (kind == svn_node_dir)
4331 apr_hash_t *entries;
4332 apr_hash_index_t *hi;
4333 apr_int64_t children_mergeinfo = 0;
4335 SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool));
4337 /* Compute CHILDREN_MERGEINFO. */
4338 for (hi = apr_hash_first(pool, entries);
4340 hi = apr_hash_next(hi))
4342 svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
4344 svn_revnum_t child_rev;
4345 apr_int64_t child_mergeinfo;
4347 svn_pool_clear(iterpool);
4349 /* Compute CHILD_REV. */
4350 SVN_ERR(svn_fs_fs__dag_get_node(&child, fs, dirent->id, iterpool));
4351 SVN_ERR(svn_fs_fs__dag_get_revision(&child_rev, child, iterpool));
4353 if (child_rev == rev)
4354 SVN_ERR(verify_node(child, rev, iterpool));
4356 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&child_mergeinfo, child));
4357 children_mergeinfo += child_mergeinfo;
4360 /* Side-effect of issue #4129. */
4361 if (children_mergeinfo+has_mergeinfo != mergeinfo_count)
4362 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4363 "Mergeinfo-count discrepancy on '%s': "
4364 "expected %" APR_INT64_T_FMT "+%d, "
4365 "counted %" APR_INT64_T_FMT,
4366 stringify_node(node, iterpool),
4367 mergeinfo_count, has_mergeinfo,
4368 children_mergeinfo);
4371 svn_pool_destroy(iterpool);
4372 return SVN_NO_ERROR;
4376 svn_fs_fs__verify_root(svn_fs_root_t *root,
4379 svn_fs_t *fs = root->fs;
4380 dag_node_t *root_dir;
4382 /* Issue #4129: bogus pred-counts and minfo-cnt's on the root node-rev
4383 (and elsewhere). This code makes more thorough checks than the
4384 commit-time checks in validate_root_noderev(). */
4386 /* Callers should disable caches by setting SVN_FS_CONFIG_FSFS_CACHE_NS;
4389 When this code is called in the library, we want to ensure we
4390 use the on-disk data --- rather than some data that was read
4391 in the possibly-distance past and cached since. */
4393 if (root->is_txn_root)
4395 fs_txn_root_data_t *frd = root->fsap_data;
4396 SVN_ERR(svn_fs_fs__dag_txn_root(&root_dir, fs, frd->txn_id, pool));
4400 fs_rev_root_data_t *frd = root->fsap_data;
4401 root_dir = frd->root_dir;
4404 /* Recursively verify ROOT_DIR. */
4405 SVN_ERR(verify_node(root_dir, root->rev, pool));
4407 /* Verify explicitly the predecessor of the root. */
4409 const svn_fs_id_t *pred_id;
4411 /* Only r0 should have no predecessor. */
4412 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, root_dir));
4413 if (! root->is_txn_root && !!pred_id != !!root->rev)
4414 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4415 "r%ld's root node's predecessor is "
4416 "unexpectedly '%s'",
4419 ? svn_fs_fs__id_unparse(pred_id, pool)->data
4421 if (root->is_txn_root && !pred_id)
4422 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4423 "Transaction '%s''s root node's predecessor is "
4424 "unexpectedly NULL",
4427 /* Check the predecessor's revision. */
4430 svn_revnum_t pred_rev = svn_fs_fs__id_rev(pred_id);
4431 if (! root->is_txn_root && pred_rev+1 != root->rev)
4433 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4434 "r%ld's root node's predecessor is r%ld"
4435 " but should be r%ld",
4436 root->rev, pred_rev, root->rev - 1);
4437 if (root->is_txn_root && pred_rev != root->rev)
4438 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4439 "Transaction '%s''s root node's predecessor"
4441 " but should be r%ld",
4442 root->txn, pred_rev, root->rev);
4446 return SVN_NO_ERROR;