]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/subversion/subversion/libsvn_fs_fs/tree.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / subversion / subversion / libsvn_fs_fs / tree.c
1 /* tree.c : tree-like filesystem, built on DAG filesystem
2  *
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
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
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
19  *    under the License.
20  * ====================================================================
21  */
22
23
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.
28
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.  */
36
37
38 #include <stdlib.h>
39 #include <string.h>
40 #include <assert.h>
41 #include <apr_pools.h>
42 #include <apr_hash.h>
43
44 #include "svn_hash.h"
45 #include "svn_private_config.h"
46 #include "svn_pools.h"
47 #include "svn_error.h"
48 #include "svn_path.h"
49 #include "svn_mergeinfo.h"
50 #include "svn_fs.h"
51 #include "svn_props.h"
52
53 #include "fs.h"
54 #include "key-gen.h"
55 #include "dag.h"
56 #include "lock.h"
57 #include "tree.h"
58 #include "fs_fs.h"
59 #include "id.h"
60 #include "temp_serializer.h"
61
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"
67
68
69 /* ### I believe this constant will become internal to reps-strings.c.
70    ### see the comment in window_consumer() for more information. */
71
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
87
88
89 \f
90 /* The root structures.
91
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
101    objects.
102 */
103 typedef struct fs_rev_root_data_t
104 {
105   /* A dag node for the revision's root directory. */
106   dag_node_t *root_dir;
107
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;
114
115 } fs_rev_root_data_t;
116
117 typedef struct fs_txn_root_data_t
118 {
119   const char *txn_id;
120
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;
125
126 /* Declared here to resolve the circular dependencies. */
127 static svn_error_t * get_dag(dag_node_t **dag_node_p,
128                              svn_fs_root_t *root,
129                              const char *path,
130                              svn_boolean_t needs_lock_cache,
131                              apr_pool_t *pool);
132
133 static svn_fs_root_t *make_revision_root(svn_fs_t *fs, svn_revnum_t rev,
134                                          dag_node_t *root_dir,
135                                          apr_pool_t *pool);
136
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,
140                                   apr_pool_t *pool);
141
142 \f
143 /*** Node Caching ***/
144
145 /* 1st level cache */
146
147 /* An entry in the first-level cache.  REVISION and PATH form the key that
148    will ultimately be matched.
149  */
150 typedef struct cache_entry_t
151 {
152   /* hash value derived from PATH, REVISION.
153      Used to short-circuit failed lookups. */
154   apr_uint32_t hash_value;
155
156   /* revision to which the NODE belongs */
157   svn_revnum_t revision;
158
159   /* path of the NODE */
160   char *path;
161
162   /* cached value of strlen(PATH). */
163   apr_size_t path_len;
164
165   /* the node allocated in the cache's pool. NULL for empty entries. */
166   dag_node_t *node;
167 } cache_entry_t;
168
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
174    hash collision).
175
176    The actual number of instances may be higher but entries that got
177    overwritten are no longer visible.
178  */
179 enum { BUCKET_COUNT = 256 };
180
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.
184  */
185 typedef struct cache_lock_t
186 {
187   /* pool holding the lock */
188   apr_pool_t *pool;
189
190   /* cache being locked */
191   fs_fs_dag_cache_t *cache;
192
193   /* next lock. NULL at EOL */
194   struct cache_lock_t *next;
195
196   /* previous lock. NULL at list head. Only then this==cache->first_lock */
197   struct cache_lock_t *prev;
198 } cache_lock_t;
199
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
203    with it.
204
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.
209  */
210 struct fs_fs_dag_cache_t
211 {
212   /* fixed number of (possibly empty) cache entries */
213   cache_entry_t buckets[BUCKET_COUNT];
214
215   /* pool used for all node allocation */
216   apr_pool_t *pool;
217
218   /* number of entries created from POOL since the last cleanup */
219   apr_size_t insertions;
220
221   /* Property lookups etc. have a very high locality (75% re-hit).
222      Thus, remember the last hit location for optimistic lookup. */
223   apr_size_t last_hit;
224
225   /* List of receiving pools that are still alive. */
226   cache_lock_t *first_lock;
227 };
228
229 /* Cleanup function to be called when a receiving pool gets cleared.
230    Unlocks the cache once.
231  */
232 static apr_status_t
233 unlock_cache(void *baton_void)
234 {
235   cache_lock_t *lock = baton_void;
236
237   /* remove lock from chain. Update the head */
238   if (lock->next)
239     lock->next->prev = lock->prev;
240   if (lock->prev)
241     lock->prev->next = lock->next;
242   else
243     lock->cache->first_lock = lock->next;
244
245   return APR_SUCCESS;
246 }
247
248 /* Cleanup function to be called when the cache itself gets destroyed.
249    In that case, we must unregister all unlock requests.
250  */
251 static apr_status_t
252 unregister_locks(void *baton_void)
253 {
254   fs_fs_dag_cache_t *cache = baton_void;
255   cache_lock_t *lock;
256
257   for (lock = cache->first_lock; lock; lock = lock->next)
258     apr_pool_cleanup_kill(lock->pool,
259                           lock,
260                           unlock_cache);
261
262   return APR_SUCCESS;
263 }
264
265 fs_fs_dag_cache_t*
266 svn_fs_fs__create_dag_cache(apr_pool_t *pool)
267 {
268   fs_fs_dag_cache_t *result = apr_pcalloc(pool, sizeof(*result));
269   result->pool = svn_pool_create(pool);
270
271   apr_pool_cleanup_register(pool,
272                             result,
273                             unregister_locks,
274                             apr_pool_cleanup_null);
275
276   return result;
277 }
278
279 /* Prevent the entries in CACHE from being destroyed, for as long as the
280    POOL lives.
281  */
282 static void
283 lock_cache(fs_fs_dag_cache_t* cache, apr_pool_t *pool)
284 {
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).
290    */
291   cache_lock_t *lock = cache->first_lock;
292
293   /* try to find an existing lock for POOL.
294      But limit the time spent on chasing pointers.  */
295   int limiter = 8;
296   while (lock && --limiter)
297       if (lock->pool == pool)
298         return;
299
300   /* create a new lock and put it at the beginning of the lock chain */
301   lock = apr_palloc(pool, sizeof(*lock));
302   lock->cache = cache;
303   lock->pool = pool;
304   lock->next = cache->first_lock;
305   lock->prev = NULL;
306
307   if (cache->first_lock)
308     cache->first_lock->prev = lock;
309   cache->first_lock = lock;
310
311   /* instruct POOL to remove the look upon cleanup */
312   apr_pool_cleanup_register(pool,
313                             lock,
314                             unlock_cache,
315                             apr_pool_cleanup_null);
316 }
317
318 /* Clears the CACHE at regular intervals (destroying all cached nodes)
319  */
320 static void
321 auto_clear_dag_cache(fs_fs_dag_cache_t* cache)
322 {
323   if (cache->first_lock == NULL && cache->insertions > BUCKET_COUNT)
324     {
325       svn_pool_clear(cache->pool);
326
327       memset(cache->buckets, 0, sizeof(cache->buckets));
328       cache->insertions = 0;
329     }
330 }
331
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.
335  */
336 static cache_entry_t *
337 cache_lookup( fs_fs_dag_cache_t *cache
338             , svn_revnum_t revision
339             , const char *path)
340 {
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;
344
345 #if SVN_UNALIGNED_ACCESS_IS_OK
346   /* "randomizing" / distributing factor used in our hash function */
347   const apr_uint32_t factor = 0xd1f3da69;
348 #endif
349
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))
355     {
356       return result;
357     }
358
359   /* need to do a full lookup.  Calculate the hash value
360      (HASH_VALUE has been initialized to REVISION). */
361   i = 0;
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.
367    */
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));
372 #endif
373
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.
378      */
379     hash_value = hash_value * 32 + (hash_value + (unsigned char)path[i]);
380
381   bucket_index = hash_value + (hash_value >> 16);
382   bucket_index = (bucket_index + (bucket_index >> 8)) % BUCKET_COUNT;
383
384   /* access the corresponding bucket and remember its location */
385   result = &cache->buckets[bucket_index];
386   cache->last_hit = bucket_index;
387
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))
394     {
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);
401
402       result->node = NULL;
403
404       cache->insertions++;
405     }
406
407   return result;
408 }
409
410 /* 2nd level cache */
411
412 /* Find and return the DAG node cache for ROOT and the key that
413    should be used for PATH. */
414 static void
415 locate_cache(svn_cache__t **cache,
416              const char **key,
417              svn_fs_root_t *root,
418              const char *path,
419              apr_pool_t *pool)
420 {
421   if (root->is_txn_root)
422     {
423       fs_txn_root_data_t *frd = root->fsap_data;
424       if (cache) *cache = frd->txn_node_cache;
425       if (key && path) *key = path;
426     }
427   else
428     {
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);
433     }
434 }
435
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).
439
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. */
443 static svn_error_t *
444 dag_node_cache_get(dag_node_t **node_p,
445                    svn_fs_root_t *root,
446                    const char *path,
447                    svn_boolean_t needs_lock_cache,
448                    apr_pool_t *pool)
449 {
450   svn_boolean_t found;
451   dag_node_t *node = NULL;
452   svn_cache__t *cache;
453   const char *key;
454
455   SVN_ERR_ASSERT(*path == '/');
456
457   if (!root->is_txn_root)
458     {
459       /* immutable DAG node. use the global caches for it */
460
461       fs_fs_data_t *ffd = root->fs->fsap_data;
462       cache_entry_t *bucket;
463
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)
467         {
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));
471           if (found && node)
472             {
473               /* Patch up the FS, since this might have come from an old FS
474               * object. */
475               svn_fs_fs__dag_set_fs(node, root->fs);
476               bucket->node = node;
477             }
478         }
479       else
480         {
481           node = bucket->node;
482         }
483
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);
488     }
489   else
490     {
491       /* DAG is mutable / may become invalid. Use the TXN-local cache */
492
493       locate_cache(&cache, &key, root, path, pool);
494
495       SVN_ERR(svn_cache__get((void **) &node, &found, cache, key, pool));
496       if (found && node)
497         {
498           /* Patch up the FS, since this might have come from an old FS
499           * object. */
500           svn_fs_fs__dag_set_fs(node, root->fs);
501         }
502     }
503
504   *node_p = node;
505
506   return SVN_NO_ERROR;
507 }
508
509
510 /* Add the NODE for PATH to ROOT's node cache. */
511 static svn_error_t *
512 dag_node_cache_set(svn_fs_root_t *root,
513                    const char *path,
514                    dag_node_t *node,
515                    apr_pool_t *pool)
516 {
517   svn_cache__t *cache;
518   const char *key;
519
520   SVN_ERR_ASSERT(*path == '/');
521
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).
524    */
525   locate_cache(&cache, &key, root, path, pool);
526
527   return svn_cache__set(cache, key, node, pool);
528 }
529
530
531 /* Baton for find_descendents_in_cache. */
532 struct fdic_baton {
533   const char *path;
534   apr_array_header_t *list;
535   apr_pool_t *pool;
536 };
537
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. */
541 static svn_error_t *
542 find_descendents_in_cache(void *baton,
543                           const void *key,
544                           apr_ssize_t klen,
545                           void *val,
546                           apr_pool_t *pool)
547 {
548   struct fdic_baton *b = baton;
549   const char *item_path = key;
550
551   if (svn_fspath__skip_ancestor(b->path, item_path))
552     APR_ARRAY_PUSH(b->list, const char *) = apr_pstrdup(b->pool, item_path);
553
554   return SVN_NO_ERROR;
555 }
556
557 /* Invalidate cache entries for PATH and any of its children.  This
558    should *only* be called on a transaction root! */
559 static svn_error_t *
560 dag_node_cache_invalidate(svn_fs_root_t *root,
561                           const char *path,
562                           apr_pool_t *pool)
563 {
564   struct fdic_baton b;
565   svn_cache__t *cache;
566   apr_pool_t *iterpool;
567   int i;
568
569   b.path = path;
570   b.pool = svn_pool_create(pool);
571   b.list = apr_array_make(b.pool, 1, sizeof(const char *));
572
573   SVN_ERR_ASSERT(root->is_txn_root);
574   locate_cache(&cache, NULL, root, NULL, b.pool);
575
576
577   SVN_ERR(svn_cache__iter(NULL, cache, find_descendents_in_cache,
578                           &b, b.pool));
579
580   iterpool = svn_pool_create(b.pool);
581
582   for (i = 0; i < b.list->nelts; i++)
583     {
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));
587     }
588
589   svn_pool_destroy(iterpool);
590   svn_pool_destroy(b.pool);
591   return SVN_NO_ERROR;
592 }
593
594
595 \f
596 /* Creating transaction and revision root nodes.  */
597
598 svn_error_t *
599 svn_fs_fs__txn_root(svn_fs_root_t **root_p,
600                     svn_fs_txn_t *txn,
601                     apr_pool_t *pool)
602 {
603   apr_uint32_t flags = 0;
604   apr_hash_t *txnprops;
605
606   /* Look for the temporary txn props representing 'flags'. */
607   SVN_ERR(svn_fs_fs__txn_proplist(&txnprops, txn, pool));
608   if (txnprops)
609     {
610       if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD))
611         flags |= SVN_FS_TXN_CHECK_OOD;
612
613       if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS))
614         flags |= SVN_FS_TXN_CHECK_LOCKS;
615     }
616
617   return make_txn_root(root_p, txn->fs, txn->id, txn->base_rev, flags, pool);
618 }
619
620
621 svn_error_t *
622 svn_fs_fs__revision_root(svn_fs_root_t **root_p,
623                          svn_fs_t *fs,
624                          svn_revnum_t rev,
625                          apr_pool_t *pool)
626 {
627   dag_node_t *root_dir;
628
629   SVN_ERR(svn_fs__check_fs(fs, TRUE));
630
631   SVN_ERR(svn_fs_fs__dag_revision_root(&root_dir, fs, rev, pool));
632
633   *root_p = make_revision_root(fs, rev, root_dir, pool);
634
635   return SVN_NO_ERROR;
636 }
637
638
639 \f
640 /* Getting dag nodes for roots.  */
641
642
643 /* Set *NODE_P to a freshly opened dag node referring to the root
644    directory of ROOT, allocating from POOL.  */
645 static svn_error_t *
646 root_node(dag_node_t **node_p,
647           svn_fs_root_t *root,
648           apr_pool_t *pool)
649 {
650   if (root->is_txn_root)
651     {
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);
654     }
655   else
656     {
657       /* It's a revision root, so we already have its root directory
658          opened.  */
659       fs_rev_root_data_t *frd = root->fsap_data;
660       *node_p = svn_fs_fs__dag_dup(frd->root_dir, pool);
661       return SVN_NO_ERROR;
662     }
663 }
664
665
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.  */
669 static svn_error_t *
670 mutable_root_node(dag_node_t **node_p,
671                   svn_fs_root_t *root,
672                   const char *error_path,
673                   apr_pool_t *pool)
674 {
675   if (root->is_txn_root)
676     return svn_fs_fs__dag_clone_root(node_p, root->fs, root->txn, pool);
677   else
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);
680 }
681
682
683 \f
684 /* Traversing directory paths.  */
685
686 typedef enum copy_id_inherit_t
687 {
688   copy_id_inherit_unknown = 0,
689   copy_id_inherit_self,
690   copy_id_inherit_parent,
691   copy_id_inherit_new
692
693 } copy_id_inherit_t;
694
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
701 {
702
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.  */
706   dag_node_t *node;
707
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.  */
710   char *entry;
711
712   /* The parent of NODE, or zero if NODE is the root directory.  */
713   struct parent_path_t *parent;
714
715   /* The copy ID inheritance style. */
716   copy_id_inherit_t copy_inherit;
717
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;
721
722 } parent_path_t;
723
724 /* Return a text string describing the absolute path of parent_path
725    PARENT_PATH.  It will be allocated in POOL. */
726 static const char *
727 parent_path_path(parent_path_t *parent_path,
728                  apr_pool_t *pool)
729 {
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)
735     : path_so_far;
736 }
737
738
739 /* Return the FS path for the parent path chain object CHILD relative
740    to its ANCESTOR in the same chain, allocated in POOL.  */
741 static const char *
742 parent_path_relpath(parent_path_t *child,
743                     parent_path_t *ancestor,
744                     apr_pool_t *pool)
745 {
746   const char *path_so_far = "";
747   parent_path_t *this_node = child;
748   while (this_node != ancestor)
749     {
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;
753     }
754   return path_so_far;
755 }
756
757
758
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. */
766 static svn_error_t *
767 get_copy_inheritance(copy_id_inherit_t *inherit_p,
768                      const char **copy_src_path,
769                      svn_fs_t *fs,
770                      parent_path_t *child,
771                      const char *txn_id,
772                      apr_pool_t *pool)
773 {
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;
781
782   SVN_ERR_ASSERT(child && child->parent && txn_id);
783
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);
789
790   /* If this child is already mutable, we have nothing to do. */
791   if (svn_fs_fs__id_txn_id(child_id))
792     {
793       *inherit_p = copy_id_inherit_self;
794       *copy_src_path = NULL;
795       return SVN_NO_ERROR;
796     }
797
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;
802
803   /* Special case: if the child's copy ID is '0', use the parent's
804      copy ID. */
805   if (strcmp(child_copy_id, "0") == 0)
806     return SVN_NO_ERROR;
807
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
811      parent will use. */
812   if (svn_fs_fs__key_compare(child_copy_id, parent_copy_id) == 0)
813     return SVN_NO_ERROR;
814
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(&copyroot_rev, &copyroot_path,
823                                       child->node));
824   SVN_ERR(svn_fs_fs__revision_root(&copyroot_root, fs, copyroot_rev, pool));
825   SVN_ERR(get_dag(&copyroot_node, copyroot_root, copyroot_path, FALSE, pool));
826   copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
827
828   if (svn_fs_fs__id_compare(copyroot_id, child_id) == -1)
829     return SVN_NO_ERROR;
830
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)
835     {
836       *inherit_p = copy_id_inherit_self;
837       return SVN_NO_ERROR;
838     }
839
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
842      a new copy ID. */
843   *inherit_p = copy_id_inherit_new;
844   *copy_src_path = id_path;
845   return SVN_NO_ERROR;
846 }
847
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,
852                  char *entry,
853                  parent_path_t *parent,
854                  apr_pool_t *pool)
855 {
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;
862   return parent_path;
863 }
864
865
866 /* Flags for open_path.  */
867 typedef enum open_path_flags_t {
868
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
872      component zero.  */
873   open_path_last_optional = 1,
874
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,
879
880   /* The caller does not care about the parent node chain but only
881      the final DAG node. */
882   open_path_node_only = 4
883 } open_path_flags_t;
884
885
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.
890
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.
896
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.
904
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.
909
910    NOTE: Public interfaces which only *read* from the filesystem
911    should not call this function directly, but should instead use
912    get_dag().
913 */
914 static svn_error_t *
915 open_path(parent_path_t **parent_path_p,
916           svn_fs_root_t *root,
917           const char *path,
918           int flags,
919           const char *txn_id,
920           apr_pool_t *pool)
921 {
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.  */
926
927   /* ensure a canonical path representation */
928   const char *path_so_far = "/";
929   apr_pool_t *iterpool = svn_pool_create(pool);
930
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
934      parent chain. */
935   const char *directory;
936   assert(svn_fs__is_canonical_abspath(path));
937   if (flags & open_path_node_only)
938     {
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));
942     }
943
944   /* did the shortcut work? */
945   if (here)
946     {
947       path_so_far = directory;
948       rest = path + strlen(directory) + 1;
949     }
950   else
951     {
952       /* Make a parent_path item for the root node, using its own current
953          copy id.  */
954       SVN_ERR(root_node(&here, root, pool));
955       rest = path + 1; /* skip the leading '/', it saves in iteration */
956     }
957
958   parent_path = make_parent_path(here, 0, 0, pool);
959   parent_path->copy_inherit = copy_id_inherit_self;
960
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.  */
966   for (;;)
967     {
968       const char *next;
969       char *entry;
970       dag_node_t *child;
971
972       svn_pool_clear(iterpool);
973
974       /* Parse out the next entry from the path.  */
975       entry = svn_fs__next_entry_name(&next, rest, pool);
976
977       /* Calculate the path traversed thus far. */
978       path_so_far = svn_fspath__join(path_so_far, entry, pool);
979
980       if (*entry == '\0')
981         {
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. */
986           child = here;
987         }
988       else
989         {
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;
994
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
999              complete path. */
1000           if (next || !(flags & open_path_uncached))
1001             SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far,
1002                                        TRUE, pool));
1003           if (cached_node)
1004             child = cached_node;
1005           else
1006             err = svn_fs_fs__dag_open(&child, here, entry, pool, iterpool);
1007
1008           /* "file not found" requires special handling.  */
1009           if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
1010             {
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.  */
1014
1015               svn_error_clear(err);
1016
1017               if ((flags & open_path_last_optional)
1018                   && (! next || *next == '\0'))
1019                 {
1020                   parent_path = make_parent_path(NULL, entry, parent_path,
1021                                                  pool);
1022                   break;
1023                 }
1024               else
1025                 {
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);
1029                 }
1030             }
1031
1032           /* Other errors we return normally.  */
1033           SVN_ERR(err);
1034
1035           if (flags & open_path_node_only)
1036             {
1037               /* Shortcut: the caller only wan'ts the final DAG node. */
1038               parent_path->node = child;
1039             }
1040           else
1041             {
1042               /* Now, make a parent_path item for CHILD. */
1043               parent_path = make_parent_path(child, entry, parent_path, pool);
1044               if (txn_id)
1045                 {
1046                   SVN_ERR(get_copy_inheritance(&inherit, &copy_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);
1050                 }
1051             }
1052
1053           /* Cache the node we found (if it wasn't already cached). */
1054           if (! cached_node)
1055             SVN_ERR(dag_node_cache_set(root, path_so_far, child, iterpool));
1056         }
1057
1058       /* Are we finished traversing the path?  */
1059       if (! next)
1060         break;
1061
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));
1066
1067       rest = next;
1068       here = child;
1069     }
1070
1071   svn_pool_destroy(iterpool);
1072   *parent_path_p = parent_path;
1073   return SVN_NO_ERROR;
1074 }
1075
1076
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,
1086                   apr_pool_t *pool)
1087 {
1088   dag_node_t *clone;
1089   const char *txn_id = root->txn;
1090
1091   /* Is the node mutable already?  */
1092   if (svn_fs_fs__dag_check_mutable(parent_path->node))
1093     return SVN_NO_ERROR;
1094
1095   /* Are we trying to clone the root, or somebody's child node?  */
1096   if (parent_path->parent)
1097     {
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;
1106
1107       /* We're trying to clone somebody's child.  Make sure our parent
1108          is mutable.  */
1109       SVN_ERR(make_path_mutable(root, parent_path->parent,
1110                                 error_path, pool));
1111
1112       switch (inherit)
1113         {
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);
1117           break;
1118
1119         case copy_id_inherit_new:
1120           SVN_ERR(svn_fs_fs__reserve_copy_id(&copy_id, root->fs, txn_id,
1121                                              pool));
1122           break;
1123
1124         case copy_id_inherit_self:
1125           copy_id = NULL;
1126           break;
1127
1128         case copy_id_inherit_unknown:
1129         default:
1130           SVN_ERR_MALFUNCTION(); /* uh-oh -- somebody didn't calculate copy-ID
1131                       inheritance data. */
1132         }
1133
1134       /* Determine what copyroot our new child node should use. */
1135       SVN_ERR(svn_fs_fs__dag_get_copyroot(&copyroot_rev, &copyroot_path,
1136                                           parent_path->node));
1137       SVN_ERR(svn_fs_fs__revision_root(&copyroot_root, root->fs,
1138                                        copyroot_rev, pool));
1139       SVN_ERR(get_dag(&copyroot_node, copyroot_root, copyroot_path,
1140                       FALSE, pool));
1141
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;
1147
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,
1152                                          clone_path,
1153                                          parent_path->entry,
1154                                          copy_id, txn_id,
1155                                          is_parent_copyroot,
1156                                          pool));
1157
1158       /* Update the path cache. */
1159       SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool),
1160                                  clone, pool));
1161     }
1162   else
1163     {
1164       /* We're trying to clone the root directory.  */
1165       SVN_ERR(mutable_root_node(&clone, root, error_path, pool));
1166     }
1167
1168   /* Update the PARENT_PATH link to refer to the clone.  */
1169   parent_path->node = clone;
1170
1171   return SVN_NO_ERROR;
1172 }
1173
1174
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.
1178
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,
1185         const char *path,
1186         svn_boolean_t needs_lock_cache,
1187         apr_pool_t *pool)
1188 {
1189   parent_path_t *parent_path;
1190   dag_node_t *node = NULL;
1191
1192   /* First we look for the DAG in our cache
1193      (if the path may be canonical). */
1194   if (*path == '/')
1195     SVN_ERR(dag_node_cache_get(&node, root, path, needs_lock_cache, pool));
1196
1197   if (! node)
1198     {
1199       /* Canonicalize the input PATH. */
1200       if (! svn_fs__is_canonical_abspath(path))
1201         {
1202           path = svn_fs__canonicalize_abspath(path, pool);
1203
1204           /* Try again with the corrected path. */
1205           SVN_ERR(dag_node_cache_get(&node, root, path, needs_lock_cache,
1206                                      pool));
1207         }
1208
1209       if (! node)
1210         {
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,
1215                             NULL, pool));
1216           node = parent_path->node;
1217
1218           /* No need to cache our find -- open_path() will do that for us. */
1219         }
1220     }
1221
1222   *dag_node_p = node;
1223   return SVN_NO_ERROR;
1224 }
1225
1226
1227 \f
1228 /* Populating the `changes' table. */
1229
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,
1240            const char *txn_id,
1241            const char *path,
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,
1249            apr_pool_t *pool)
1250 {
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,
1255                                pool);
1256 }
1257
1258
1259 \f
1260 /* Generic node operations.  */
1261
1262 /* Get the id of a node referenced by path PATH in ROOT.  Return the
1263    id in *ID_P allocated in POOL. */
1264 svn_error_t *
1265 svn_fs_fs__node_id(const svn_fs_id_t **id_p,
1266                    svn_fs_root_t *root,
1267                    const char *path,
1268                    apr_pool_t *pool)
1269 {
1270   if ((! root->is_txn_root)
1271       && (path[0] == '\0' || ((path[0] == '/') && (path[1] == '\0'))))
1272     {
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);
1279     }
1280   else
1281     {
1282       dag_node_t *node;
1283
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);
1286     }
1287   return SVN_NO_ERROR;
1288 }
1289
1290
1291 svn_error_t *
1292 svn_fs_fs__node_created_rev(svn_revnum_t *revision,
1293                             svn_fs_root_t *root,
1294                             const char *path,
1295                             apr_pool_t *pool)
1296 {
1297   dag_node_t *node;
1298
1299   SVN_ERR(get_dag(&node, root, path, FALSE, pool));
1300   return svn_fs_fs__dag_get_revision(revision, node, pool);
1301 }
1302
1303
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,
1309                      const char *path,
1310                      apr_pool_t *pool)
1311 {
1312   dag_node_t *node;
1313
1314   SVN_ERR(get_dag(&node, root, path, TRUE, pool));
1315   *created_path = svn_fs_fs__dag_get_created_path(node);
1316
1317   return SVN_NO_ERROR;
1318 }
1319
1320
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,
1326           const char *path,
1327           apr_pool_t *pool)
1328 {
1329   const svn_fs_id_t *node_id;
1330   dag_node_t *node;
1331
1332   /* Get the node id. */
1333   SVN_ERR(svn_fs_fs__node_id(&node_id, root, path, pool));
1334
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);
1338
1339   return SVN_NO_ERROR;
1340 }
1341
1342
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. */
1346 svn_error_t *
1347 svn_fs_fs__check_path(svn_node_kind_t *kind_p,
1348                       svn_fs_root_t *root,
1349                       const char *path,
1350                       apr_pool_t *pool)
1351 {
1352   svn_error_t *err = node_kind(kind_p, root, path, pool);
1353   if (err &&
1354       ((err->apr_err == SVN_ERR_FS_NOT_FOUND)
1355        || (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY)))
1356     {
1357       svn_error_clear(err);
1358       err = SVN_NO_ERROR;
1359       *kind_p = svn_node_none;
1360     }
1361
1362   return svn_error_trace(err);
1363 }
1364
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,
1371              const char *path,
1372              const char *propname,
1373              apr_pool_t *pool)
1374 {
1375   dag_node_t *node;
1376   apr_hash_t *proplist;
1377
1378   SVN_ERR(get_dag(&node, root, path, FALSE, pool));
1379   SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, node, pool));
1380   *value_p = NULL;
1381   if (proplist)
1382     *value_p = svn_hash_gets(proplist, propname);
1383
1384   return SVN_NO_ERROR;
1385 }
1386
1387
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,
1395                  const char *path,
1396                  apr_pool_t *pool)
1397 {
1398   apr_hash_t *table;
1399   dag_node_t *node;
1400
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);
1404
1405   return SVN_NO_ERROR;
1406 }
1407
1408
1409 static svn_error_t *
1410 increment_mergeinfo_up_tree(parent_path_t *pp,
1411                             apr_int64_t increment,
1412                             apr_pool_t *pool)
1413 {
1414   for (; pp; pp = pp->parent)
1415     SVN_ERR(svn_fs_fs__dag_increment_mergeinfo_count(pp->node,
1416                                                      increment,
1417                                                      pool));
1418
1419   return SVN_NO_ERROR;
1420 }
1421
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
1426    in POOL. */
1427 static svn_error_t *
1428 fs_change_node_prop(svn_fs_root_t *root,
1429                     const char *path,
1430                     const char *name,
1431                     const svn_string_t *value,
1432                     apr_pool_t *pool)
1433 {
1434   parent_path_t *parent_path;
1435   apr_hash_t *proplist;
1436   const char *txn_id;
1437
1438   if (! root->is_txn_root)
1439     return SVN_FS__NOT_TXN(root);
1440   txn_id = root->txn;
1441
1442   path = svn_fs__canonicalize_abspath(path, pool);
1443   SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, pool));
1444
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,
1449                                               pool));
1450
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));
1453
1454   /* If there's no proplist, but we're just deleting a property, exit now. */
1455   if ((! proplist) && (! value))
1456     return SVN_NO_ERROR;
1457
1458   /* Now, if there's no proplist, we know we need to make one. */
1459   if (! proplist)
1460     proplist = apr_hash_make(pool);
1461
1462   if (svn_fs_fs__fs_supports_mergeinfo(root->fs)
1463       && strcmp (name, SVN_PROP_MERGEINFO) == 0)
1464     {
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));
1468
1469       if (value && !had_mergeinfo)
1470         increment = 1;
1471       else if (!value && had_mergeinfo)
1472         increment = -1;
1473
1474       if (increment != 0)
1475         {
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));
1479         }
1480     }
1481
1482   /* Set the property. */
1483   svn_hash_sets(proplist, name, value);
1484
1485   /* Overwrite the node's proplist. */
1486   SVN_ERR(svn_fs_fs__dag_set_proplist(parent_path->node, proplist,
1487                                       pool));
1488
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);
1495 }
1496
1497
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,
1505                  const char *path1,
1506                  svn_fs_root_t *root2,
1507                  const char *path2,
1508                  apr_pool_t *pool)
1509 {
1510   dag_node_t *node1, *node2;
1511
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"));
1517
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,
1521                                          node1, node2);
1522 }
1523
1524
1525 \f
1526 /* Merges and commits. */
1527
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)
1531 {
1532   return get_dag(node, root, "/", TRUE, pool);
1533 }
1534
1535
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,
1542              const char *path)
1543 {
1544   svn_stringbuf_set(conflict_path, path);
1545   return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL,
1546                            _("Conflict at '%s'"), path);
1547 }
1548
1549
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.
1554  *
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.
1559  *
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.
1564  *
1565  * If there are no conflicting differences, CONFLICT_P is updated to
1566  * the empty string.
1567  *
1568  * CONFLICT_P must point to a valid svn_stringbuf_t.
1569  *
1570  * Do any necessary temporary allocation in POOL.
1571  */
1572 static svn_error_t *
1573 merge(svn_stringbuf_t *conflict_p,
1574       const char *target_path,
1575       dag_node_t *target,
1576       dag_node_t *source,
1577       dag_node_t *ancestor,
1578       const char *txn_id,
1579       apr_int64_t *mergeinfo_increment_out,
1580       apr_pool_t *pool)
1581 {
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;
1585   svn_fs_t *fs;
1586   apr_pool_t *iterpool;
1587   apr_int64_t mergeinfo_increment = 0;
1588   svn_boolean_t fs_supports_mergeinfo;
1589
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)))
1594     {
1595       return svn_error_create
1596         (SVN_ERR_FS_CORRUPT, NULL,
1597          _("Bad merge; ancestor, source, and target not all in same fs"));
1598     }
1599
1600   /* We have the same fs, now check it. */
1601   SVN_ERR(svn_fs__check_fs(fs, TRUE));
1602
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);
1606
1607   /* It's improper to call this function with ancestor == target. */
1608   if (svn_fs_fs__id_eq(ancestor_id, target_id))
1609     {
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);
1615     }
1616
1617   svn_stringbuf_setempty(conflict_p);
1618
1619   /* Base cases:
1620    * Either no change made in source, or same change as made in target.
1621    * Both mean nothing to merge here.
1622    */
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;
1626
1627   /* Else proceed, knowing all three are distinct node revisions.
1628    *
1629    * How to merge from this point:
1630    *
1631    * if (not all 3 are directories)
1632    *   {
1633    *     early exit with conflict;
1634    *   }
1635    *
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)
1642    *    conflict;
1643    *
1644    * For each entry NAME in the directory ANCESTOR:
1645    *
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.)
1649    *
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.
1653    *
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.
1658    *
1659    *   Else:
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.
1663    *
1664    *     If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
1665    *     double delete; flag a conflict.
1666    *
1667    *     If any of the three entries is of type file, declare a conflict.
1668    *
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.
1674    *
1675    *     Direct modifications were made to the directory ANCESTOR-ENTRY
1676    *     in both SOURCE and TARGET.  Recursively merge these
1677    *     modifications.
1678    *
1679    * For each leftover entry NAME in the directory SOURCE:
1680    *
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.
1684    *
1685    *   Add NAME to TARGET with the entry from SOURCE.
1686    *
1687    * Now that we are done merging the changes from SOURCE into the
1688    * directory TARGET, update TARGET's predecessor to be SOURCE.
1689    */
1690
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))
1694     {
1695       return conflict_err(conflict_p, target_path);
1696     }
1697
1698
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.
1703
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.
1708   */
1709   {
1710     node_revision_t *tgt_nr, *anc_nr, *src_nr;
1711
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));
1716
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);
1727   }
1728
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));
1735
1736   fs_supports_mergeinfo = svn_fs_fs__fs_supports_mergeinfo(fs);
1737
1738   /* for each entry E in a_entries... */
1739   iterpool = svn_pool_create(pool);
1740   for (hi = apr_hash_first(pool, a_entries);
1741        hi;
1742        hi = apr_hash_next(hi))
1743     {
1744       svn_fs_dirent_t *s_entry, *t_entry, *a_entry;
1745       const char *name;
1746       apr_ssize_t klen;
1747
1748       svn_pool_clear(iterpool);
1749
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);
1753
1754       s_entry = apr_hash_get(s_entries, name, klen);
1755       t_entry = apr_hash_get(t_entries, name, klen);
1756
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))
1760         goto end;
1761
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))
1765         {
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)
1770             {
1771               apr_int64_t mergeinfo_start;
1772               SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start,
1773                                                          t_ent_node));
1774               mergeinfo_increment -= mergeinfo_start;
1775             }
1776
1777           if (s_entry)
1778             {
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));
1782
1783               if (fs_supports_mergeinfo)
1784                 {
1785                   apr_int64_t mergeinfo_end;
1786                   SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end,
1787                                                              s_ent_node));
1788                   mergeinfo_increment += mergeinfo_end;
1789                 }
1790
1791               SVN_ERR(svn_fs_fs__dag_set_entry(target, name,
1792                                                s_entry->id,
1793                                                s_entry->kind,
1794                                                txn_id,
1795                                                iterpool));
1796             }
1797           else
1798             {
1799               SVN_ERR(svn_fs_fs__dag_delete(target, name, txn_id, iterpool));
1800             }
1801         }
1802
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. */
1806       else
1807         {
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;
1811
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,
1818                                                  a_entry->name,
1819                                                  iterpool));
1820
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,
1827                                                  a_entry->name,
1828                                                  iterpool));
1829
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,
1842                                                  a_entry->name,
1843                                                  iterpool));
1844
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,
1857                         txn_id,
1858                         &sub_mergeinfo_increment,
1859                         iterpool));
1860           if (fs_supports_mergeinfo)
1861             mergeinfo_increment += sub_mergeinfo_increment;
1862         }
1863
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. */
1868     end:
1869       apr_hash_set(s_entries, name, klen, NULL);
1870     }
1871
1872   /* For each entry E in source but not in ancestor */
1873   for (hi = apr_hash_first(pool, s_entries);
1874        hi;
1875        hi = apr_hash_next(hi))
1876     {
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;
1881
1882       svn_pool_clear(iterpool);
1883
1884       s_entry = svn__apr_hash_index_val(hi);
1885       t_entry = apr_hash_get(t_entries, name, klen);
1886
1887       /* If NAME exists in TARGET, declare a conflict. */
1888       if (t_entry)
1889         return conflict_err(conflict_p,
1890                             svn_fspath__join(target_path,
1891                                              t_entry->name,
1892                                              iterpool));
1893
1894       SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
1895                                       s_entry->id, iterpool));
1896       if (fs_supports_mergeinfo)
1897         {
1898           apr_int64_t mergeinfo_s;
1899           SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_s,
1900                                                      s_ent_node));
1901           mergeinfo_increment += mergeinfo_s;
1902         }
1903
1904       SVN_ERR(svn_fs_fs__dag_set_entry
1905               (target, s_entry->name, s_entry->id, s_entry->kind,
1906                txn_id, iterpool));
1907     }
1908   svn_pool_destroy(iterpool);
1909
1910   SVN_ERR(svn_fs_fs__dag_update_ancestry(target, source, pool));
1911
1912   if (fs_supports_mergeinfo)
1913     SVN_ERR(svn_fs_fs__dag_increment_mergeinfo_count(target,
1914                                                      mergeinfo_increment,
1915                                                      pool));
1916
1917   if (mergeinfo_increment_out)
1918     *mergeinfo_increment_out = mergeinfo_increment;
1919
1920   return SVN_NO_ERROR;
1921 }
1922
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.
1926
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.
1930
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,
1936               svn_fs_txn_t *txn,
1937               svn_stringbuf_t *conflict,
1938               apr_pool_t *pool)
1939 {
1940   dag_node_t *txn_root_node;
1941   svn_fs_t *fs = txn->fs;
1942   const char *txn_id = txn->id;
1943
1944   SVN_ERR(svn_fs_fs__dag_txn_root(&txn_root_node, fs, txn_id, pool));
1945
1946   if (ancestor_node == NULL)
1947     {
1948       SVN_ERR(svn_fs_fs__dag_txn_base_root(&ancestor_node, fs,
1949                                            txn_id, pool));
1950     }
1951
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)))
1954     {
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();
1959     }
1960   else
1961     SVN_ERR(merge(conflict, "/", txn_root_node,
1962                   source_node, ancestor_node, txn_id, NULL, pool));
1963
1964   return SVN_NO_ERROR;
1965 }
1966
1967
1968 svn_error_t *
1969 svn_fs_fs__commit_txn(const char **conflict_p,
1970                       svn_revnum_t *new_rev,
1971                       svn_fs_txn_t *txn,
1972                       apr_pool_t *pool)
1973 {
1974   /* How do commits work in Subversion?
1975    *
1976    * When you're ready to commit, here's what you have:
1977    *
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.
1981    *
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.
1989    *
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
1993    * one to adjust.
1994    *
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
1997    * one.  For example:
1998    *
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.
2010    *
2011    * Lather, rinse, repeat.
2012    */
2013
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;
2017
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);
2023
2024   /* Initialize output params. */
2025   *new_rev = SVN_INVALID_REVNUM;
2026   if (conflict_p)
2027     *conflict_p = NULL;
2028
2029   while (1729)
2030     {
2031       svn_revnum_t youngish_rev;
2032       svn_fs_root_t *youngish_root;
2033       dag_node_t *youngish_root_node;
2034
2035       svn_pool_clear(iterpool);
2036
2037       /* Get the *current* youngest revision.  We call it "youngish"
2038          because new revisions might get committed after we've
2039          obtained it. */
2040
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,
2043                                        iterpool));
2044
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
2051          here). */
2052       SVN_ERR(get_root(&youngish_root_node, youngish_root, iterpool));
2053
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);
2059       if (err)
2060         {
2061           if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2062             *conflict_p = conflict->data;
2063           goto cleanup;
2064         }
2065       txn->base_rev = youngish_rev;
2066
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))
2070         {
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)
2079             goto cleanup;
2080           else
2081             svn_error_clear(err);
2082         }
2083       else if (err)
2084         {
2085           goto cleanup;
2086         }
2087       else
2088         {
2089           err = SVN_NO_ERROR;
2090           goto cleanup;
2091         }
2092     }
2093
2094  cleanup:
2095
2096   svn_fs_fs__reset_txn_caches(fs);
2097
2098   svn_pool_destroy(iterpool);
2099   return svn_error_trace(err);
2100 }
2101
2102
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,
2118          apr_pool_t *pool)
2119 {
2120   dag_node_t *source, *ancestor;
2121   svn_fs_txn_t *txn;
2122   svn_error_t *err;
2123   svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool);
2124
2125   if (! target_root->is_txn_root)
2126     return SVN_FS__NOT_TXN(target_root);
2127
2128   /* Paranoia. */
2129   if ((source_root->fs != ancestor_root->fs)
2130       || (target_root->fs != ancestor_root->fs))
2131     {
2132       return svn_error_create
2133         (SVN_ERR_FS_CORRUPT, NULL,
2134          _("Bad merge; ancestor, source, and target not all in same fs"));
2135     }
2136
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:
2140
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.
2144   */
2145
2146   /* Get the ancestor node. */
2147   SVN_ERR(get_root(&ancestor, ancestor_root, pool));
2148
2149   /* Get the source node. */
2150   SVN_ERR(get_root(&source, source_root, pool));
2151
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,
2154                               pool));
2155
2156   /* Merge changes between ANCESTOR and SOURCE into TXN. */
2157   err = merge_changes(ancestor, source, txn, conflict, pool);
2158   if (err)
2159     {
2160       if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2161         *conflict_p = conflict->data;
2162       return svn_error_trace(err);
2163     }
2164
2165   return SVN_NO_ERROR;
2166 }
2167
2168 svn_error_t *
2169 svn_fs_fs__deltify(svn_fs_t *fs,
2170                    svn_revnum_t revision,
2171                    apr_pool_t *pool)
2172 {
2173   /* Deltify is a no-op for fs_fs. */
2174
2175   return SVN_NO_ERROR;
2176 }
2177
2178
2179 \f
2180 /* Directories.  */
2181
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,
2190                const char *path,
2191                apr_pool_t *pool)
2192 {
2193   dag_node_t *node;
2194
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);
2198 }
2199
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)
2204 {
2205   char *c = strchr(path, '\n');
2206
2207   if (c)
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));
2211
2212   return SVN_NO_ERROR;
2213 }
2214
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
2218    in POOL.  */
2219 static svn_error_t *
2220 fs_make_dir(svn_fs_root_t *root,
2221             const char *path,
2222             apr_pool_t *pool)
2223 {
2224   parent_path_t *parent_path;
2225   dag_node_t *sub_dir;
2226   const char *txn_id = root->txn;
2227
2228   SVN_ERR(check_newline(path, pool));
2229
2230   path = svn_fs__canonicalize_abspath(path, pool);
2231   SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
2232                     txn_id, pool));
2233
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
2236      use it. */
2237   if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2238     SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, TRUE, FALSE,
2239                                               pool));
2240
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);
2245
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,
2251                                                    pool),
2252                                   parent_path->entry,
2253                                   txn_id,
2254                                   pool));
2255
2256   /* Add this directory to the path cache. */
2257   SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool),
2258                              sub_dir, pool));
2259
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);
2264 }
2265
2266
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,
2271                const char *path,
2272                apr_pool_t *pool)
2273 {
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;
2278
2279   if (! root->is_txn_root)
2280     return SVN_FS__NOT_TXN(root);
2281
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);
2285
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"));
2290
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,
2295                                               pool));
2296
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,
2303                                 parent_path->entry,
2304                                 txn_id, pool));
2305
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),
2308                                     pool));
2309
2310   /* Update mergeinfo counts for parents */
2311   if (mergeinfo_count > 0)
2312     SVN_ERR(increment_mergeinfo_up_tree(parent_path->parent,
2313                                         -mergeinfo_count,
2314                                         pool));
2315
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);
2321 }
2322
2323
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,
2329           svn_fs_t *fs1,
2330           svn_fs_t *fs2,
2331           apr_pool_t *pool)
2332 {
2333   *same_p = ! strcmp(fs1->uuid, fs2->uuid);
2334   return SVN_NO_ERROR;
2335 }
2336
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,
2346             apr_pool_t *pool)
2347 {
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;
2352
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));
2356   if (! same_p)
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);
2361
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"));
2366
2367   /* Get the NODE for FROM_PATH in FROM_ROOT.*/
2368   SVN_ERR(get_dag(&from_node, from_root, from_path, TRUE, pool));
2369
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
2372      make one there. */
2373   SVN_ERR(open_path(&to_parent_path, to_root, to_path,
2374                     open_path_last_optional, txn_id, pool));
2375
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));
2381
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;
2390
2391   if (! from_root->is_txn_root)
2392     {
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;
2398
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)
2402         {
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));
2407         }
2408       else
2409         {
2410           kind = svn_fs_path_change_add;
2411           mergeinfo_start = 0;
2412         }
2413
2414       if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs))
2415         SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end,
2416                                                    from_node));
2417
2418       /* Make sure the target node's parents are mutable.  */
2419       SVN_ERR(make_path_mutable(to_root, to_parent_path->parent,
2420                                 to_path, pool));
2421
2422       /* Canonicalize the copyfrom path. */
2423       from_canonpath = svn_fs__canonicalize_abspath(from_path, pool);
2424
2425       SVN_ERR(svn_fs_fs__dag_copy(to_parent_path->parent->node,
2426                                   to_parent_path->entry,
2427                                   from_node,
2428                                   preserve_history,
2429                                   from_root->rev,
2430                                   from_canonpath,
2431                                   txn_id, pool));
2432
2433       if (kind == svn_fs_path_change_replace)
2434         SVN_ERR(dag_node_cache_invalidate(to_root,
2435                                           parent_path_path(to_parent_path,
2436                                                            pool), pool));
2437
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,
2442                                             pool));
2443
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));
2450     }
2451   else
2452     {
2453       /* See IZ Issue #436 */
2454       /* Copying from transaction roots not currently available.
2455
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
2463          future. */
2464
2465       SVN_ERR_MALFUNCTION();
2466     }
2467
2468   return SVN_NO_ERROR;
2469 }
2470
2471
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,
2480         apr_pool_t *pool)
2481 {
2482   SVN_ERR(check_newline(to_path, pool));
2483
2484   return svn_error_trace(copy_helper(from_root,
2485                                      svn_fs__canonicalize_abspath(from_path,
2486                                                                   pool),
2487                                      to_root,
2488                                      svn_fs__canonicalize_abspath(to_path,
2489                                                                   pool),
2490                                      TRUE, pool));
2491 }
2492
2493
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,
2500                  const char *path,
2501                  apr_pool_t *pool)
2502 {
2503   if (! to_root->is_txn_root)
2504     return SVN_FS__NOT_TXN(to_root);
2505
2506   path = svn_fs__canonicalize_abspath(path, pool);
2507   return svn_error_trace(copy_helper(from_root, path, to_root, path,
2508                                      FALSE, pool));
2509 }
2510
2511
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,
2519                const char *path,
2520                apr_pool_t *pool)
2521 {
2522   dag_node_t *node;
2523   const char *copyfrom_path, *copyfrom_str = NULL;
2524   svn_revnum_t copyfrom_rev;
2525   char *str, *buf;
2526
2527   /* Check to see if there is a cached version of this copyfrom
2528      entry. */
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);
2532   }
2533
2534   if (copyfrom_str)
2535     {
2536       if (*copyfrom_str == 0)
2537         {
2538           /* We have a cached entry that says there is no copyfrom
2539              here. */
2540           copyfrom_rev = SVN_INVALID_REVNUM;
2541           copyfrom_path = NULL;
2542         }
2543       else
2544         {
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;
2550         }
2551     }
2552   else
2553     {
2554       /* There is no cached entry, look it up the old-fashioned
2555          way. */
2556       SVN_ERR(get_dag(&node, root, path, TRUE, pool));
2557       SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(&copyfrom_rev, node));
2558       SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(&copyfrom_path, node));
2559     }
2560
2561   *rev_p  = copyfrom_rev;
2562   *path_p = copyfrom_path;
2563
2564   return SVN_NO_ERROR;
2565 }
2566
2567
2568 \f
2569 /* Files.  */
2570
2571 /* Create the empty file PATH under ROOT.  Temporary allocations are
2572    in POOL. */
2573 static svn_error_t *
2574 fs_make_file(svn_fs_root_t *root,
2575              const char *path,
2576              apr_pool_t *pool)
2577 {
2578   parent_path_t *parent_path;
2579   dag_node_t *child;
2580   const char *txn_id = root->txn;
2581
2582   SVN_ERR(check_newline(path, pool));
2583
2584   path = svn_fs__canonicalize_abspath(path, pool);
2585   SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
2586                    txn_id, pool));
2587
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);
2592
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,
2597                                               pool));
2598
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,
2604                                                     pool),
2605                                    parent_path->entry,
2606                                    txn_id,
2607                                    pool));
2608
2609   /* Add this file to the path cache. */
2610   SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool), child,
2611                              pool));
2612
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);
2617 }
2618
2619
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,
2625                const char *path,
2626                apr_pool_t *pool)
2627 {
2628   dag_node_t *file;
2629
2630   /* First create a dag_node_t from the root/path pair. */
2631   SVN_ERR(get_dag(&file, root, path, FALSE, pool));
2632
2633   /* Now fetch its length */
2634   return svn_fs_fs__dag_file_length(length_p, file, pool);
2635 }
2636
2637
2638 /* Set *CHECKSUM to the checksum of type KIND for PATH under ROOT, or
2639    NULL if that information isn't available.  Temporary allocations
2640    are from POOL. */
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,
2645                  const char *path,
2646                  apr_pool_t *pool)
2647 {
2648   dag_node_t *file;
2649
2650   SVN_ERR(get_dag(&file, root, path, FALSE, pool));
2651   return svn_fs_fs__dag_file_checksum(checksum, file, kind, pool);
2652 }
2653
2654
2655 /* --- Machinery for svn_fs_file_contents() ---  */
2656
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,
2662                  const char *path,
2663                  apr_pool_t *pool)
2664 {
2665   dag_node_t *node;
2666   svn_stream_t *file_stream;
2667
2668   /* First create a dag_node_t from the root/path pair. */
2669   SVN_ERR(get_dag(&node, root, path, FALSE, pool));
2670
2671   /* Then create a readable stream from the dag_node_t. */
2672   SVN_ERR(svn_fs_fs__dag_get_contents(&file_stream, node, pool));
2673
2674   *contents = file_stream;
2675   return SVN_NO_ERROR;
2676 }
2677
2678 /* --- End machinery for svn_fs_file_contents() ---  */
2679
2680
2681 /* --- Machinery for svn_fs_try_process_file_contents() ---  */
2682
2683 static svn_error_t *
2684 fs_try_process_file_contents(svn_boolean_t *success,
2685                              svn_fs_root_t *root,
2686                              const char *path,
2687                              svn_fs_process_contents_func_t processor,
2688                              void* baton,
2689                              apr_pool_t *pool)
2690 {
2691   dag_node_t *node;
2692   SVN_ERR(get_dag(&node, root, path, FALSE, pool));
2693
2694   return svn_fs_fs__dag_try_process_file_contents(success, node,
2695                                                   processor, baton, pool);
2696 }
2697
2698 /* --- End machinery for svn_fs_try_process_file_contents() ---  */
2699
2700
2701 /* --- Machinery for svn_fs_apply_textdelta() ---  */
2702
2703
2704 /* Local baton type for all the helper functions below. */
2705 typedef struct txdelta_baton_t
2706 {
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;
2713
2714   /* The original file info */
2715   svn_fs_root_t *root;
2716   const char *path;
2717
2718   /* Derived from the file info */
2719   dag_node_t *node;
2720
2721   svn_stream_t *source_stream;
2722   svn_stream_t *target_stream;
2723   svn_stream_t *string_stream;
2724   svn_stringbuf_t *target_string;
2725
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;
2731
2732   /* Pool used by db txns */
2733   apr_pool_t *pool;
2734
2735 } txdelta_baton_t;
2736
2737
2738 /* ### see comment in window_consumer() regarding this function. */
2739
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)
2744 {
2745   txdelta_baton_t *tb = (txdelta_baton_t *) baton;
2746   svn_stringbuf_appendbytes(tb->target_string, data, *len);
2747   return SVN_NO_ERROR;
2748 }
2749
2750
2751
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)
2755 {
2756   txdelta_baton_t *tb = (txdelta_baton_t *) baton;
2757
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));
2762
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
2770      ### flushing.
2771      ###
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.
2781   */
2782
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))
2786     {
2787       apr_size_t len = tb->target_string->len;
2788       SVN_ERR(svn_stream_write(tb->target_stream,
2789                                tb->target_string->data,
2790                                &len));
2791       svn_stringbuf_setempty(tb->target_string);
2792     }
2793
2794   /* Is the window NULL?  If so, we're done. */
2795   if (! window)
2796     {
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));
2801
2802       SVN_ERR(svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum,
2803                                             tb->pool));
2804     }
2805
2806   return SVN_NO_ERROR;
2807 }
2808
2809 /* Helper function for fs_apply_textdelta.  BATON is of type
2810    txdelta_baton_t. */
2811 static svn_error_t *
2812 apply_textdelta(void *baton, apr_pool_t *pool)
2813 {
2814   txdelta_baton_t *tb = (txdelta_baton_t *) baton;
2815   parent_path_t *parent_path;
2816   const char *txn_id = tb->root->txn;
2817
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));
2821
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));
2827
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;
2831
2832   if (tb->base_checksum)
2833     {
2834       svn_checksum_t *checksum;
2835
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'"),
2843                                          tb->path);
2844     }
2845
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));
2851
2852   /* Make a writable "target" stream */
2853   SVN_ERR(svn_fs_fs__dag_get_edit_stream(&(tb->target_stream), tb->node,
2854                                          tb->pool));
2855
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);
2861
2862   /* Now, create a custom window handler that uses our two streams. */
2863   svn_txdelta_apply(tb->source_stream,
2864                     tb->string_stream,
2865                     NULL,
2866                     tb->path,
2867                     tb->pool,
2868                     &(tb->interpreter),
2869                     &(tb->interpreter_baton));
2870
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);
2876 }
2877
2878
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,
2886                    const char *path,
2887                    svn_checksum_t *base_checksum,
2888                    svn_checksum_t *result_checksum,
2889                    apr_pool_t *pool)
2890 {
2891   txdelta_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
2892
2893   tb->root = root;
2894   tb->path = svn_fs__canonicalize_abspath(path, pool);
2895   tb->pool = pool;
2896   tb->base_checksum = svn_checksum_dup(base_checksum, pool);
2897   tb->result_checksum = svn_checksum_dup(result_checksum, pool);
2898
2899   SVN_ERR(apply_textdelta(tb, pool));
2900
2901   *contents_p = window_consumer;
2902   *contents_baton_p = tb;
2903   return SVN_NO_ERROR;
2904 }
2905
2906 /* --- End machinery for svn_fs_apply_textdelta() ---  */
2907
2908 /* --- Machinery for svn_fs_apply_text() ---  */
2909
2910 /* Baton for svn_fs_apply_text(). */
2911 struct text_baton_t
2912 {
2913   /* The original file info */
2914   svn_fs_root_t *root;
2915   const char *path;
2916
2917   /* Derived from the file info */
2918   dag_node_t *node;
2919
2920   /* The returned stream that will accept the file's new contents. */
2921   svn_stream_t *stream;
2922
2923   /* The actual fs stream that the returned stream will write to. */
2924   svn_stream_t *file_stream;
2925
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;
2929
2930   /* Pool used by db txns */
2931   apr_pool_t *pool;
2932 };
2933
2934
2935 /* A wrapper around svn_fs_fs__dag_finalize_edits, but for
2936  * fulltext data, not text deltas.  Closes BATON->file_stream.
2937  *
2938  * Note: If you're confused about how this function relates to another
2939  * of similar name, think of it this way:
2940  *
2941  * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits()
2942  * svn_fs_apply_text()      ==> ... ==> txn_body_fulltext_finalize_edits()
2943  */
2944
2945 /* Write function for the publically returned stream. */
2946 static svn_error_t *
2947 text_stream_writer(void *baton,
2948                    const char *data,
2949                    apr_size_t *len)
2950 {
2951   struct text_baton_t *tb = baton;
2952
2953   /* Psst, here's some data.  Pass it on to the -real- file stream. */
2954   return svn_stream_write(tb->file_stream, data, len);
2955 }
2956
2957 /* Close function for the publically returned stream. */
2958 static svn_error_t *
2959 text_stream_closer(void *baton)
2960 {
2961   struct text_baton_t *tb = baton;
2962
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));
2967
2968   /* Need to tell fs that we're done sending text */
2969   return svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum,
2970                                        tb->pool);
2971 }
2972
2973
2974 /* Helper function for fs_apply_text.  BATON is of type
2975    text_baton_t. */
2976 static svn_error_t *
2977 apply_text(void *baton, apr_pool_t *pool)
2978 {
2979   struct text_baton_t *tb = baton;
2980   parent_path_t *parent_path;
2981   const char *txn_id = tb->root->txn;
2982
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));
2986
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));
2992
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;
2996
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,
2999                                          tb->pool));
3000
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);
3005
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);
3011 }
3012
3013
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,
3020               const char *path,
3021               svn_checksum_t *result_checksum,
3022               apr_pool_t *pool)
3023 {
3024   struct text_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
3025
3026   tb->root = root;
3027   tb->path = svn_fs__canonicalize_abspath(path, pool);
3028   tb->pool = pool;
3029   tb->result_checksum = svn_checksum_dup(result_checksum, pool);
3030
3031   SVN_ERR(apply_text(tb, pool));
3032
3033   *contents_p = tb->stream;
3034   return SVN_NO_ERROR;
3035 }
3036
3037 /* --- End machinery for svn_fs_apply_text() ---  */
3038
3039
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,
3046                     const char *path1,
3047                     svn_fs_root_t *root2,
3048                     const char *path2,
3049                     apr_pool_t *pool)
3050 {
3051   dag_node_t *node1, *node2;
3052
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"));
3058
3059   /* Check that both paths are files. */
3060   {
3061     svn_node_kind_t kind;
3062
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);
3067
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);
3072   }
3073
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,
3077                                          node1, node2);
3078 }
3079
3080
3081 \f
3082 /* Public interface to computing file text deltas.  */
3083
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,
3090                          apr_pool_t *pool)
3091 {
3092   dag_node_t *source_node, *target_node;
3093
3094   if (source_root && source_path)
3095     SVN_ERR(get_dag(&source_node, source_root, source_path, TRUE, pool));
3096   else
3097     source_node = NULL;
3098   SVN_ERR(get_dag(&target_node, target_root, target_path, TRUE, pool));
3099
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,
3102                                               target_node, pool);
3103 }
3104
3105
3106 \f
3107 /* Finding Changes */
3108
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,
3116                  apr_pool_t *pool)
3117 {
3118   if (root->is_txn_root)
3119     return svn_fs_fs__txn_changes_fetch(changed_paths_p, root->fs, root->txn,
3120                                         pool);
3121   else
3122     {
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);
3126     }
3127 }
3128
3129
3130 \f
3131 /* Our coolio opaque history object. */
3132 typedef struct fs_history_data_t
3133 {
3134   /* filesystem object */
3135   svn_fs_t *fs;
3136
3137   /* path and revision of historical location */
3138   const char *path;
3139   svn_revnum_t revision;
3140
3141   /* internal-use hints about where to resume the history search. */
3142   const char *path_hint;
3143   svn_revnum_t rev_hint;
3144
3145   /* FALSE until the first call to svn_fs_history_prev(). */
3146   svn_boolean_t is_interesting;
3147 } fs_history_data_t;
3148
3149 static svn_fs_history_t *
3150 assemble_history(svn_fs_t *fs,
3151                  const char *path,
3152                  svn_revnum_t revision,
3153                  svn_boolean_t is_interesting,
3154                  const char *path_hint,
3155                  svn_revnum_t rev_hint,
3156                  apr_pool_t *pool);
3157
3158
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
3161    allocations. */
3162 static svn_error_t *
3163 fs_node_history(svn_fs_history_t **history_p,
3164                 svn_fs_root_t *root,
3165                 const char *path,
3166                 apr_pool_t *pool)
3167 {
3168   svn_node_kind_t kind;
3169
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);
3173
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);
3178
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;
3185 }
3186
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,
3193                        svn_fs_t *fs,
3194                        parent_path_t *parent_path,
3195                        apr_pool_t *pool)
3196 {
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;
3201
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));
3206
3207   /* Find our copyroot. */
3208   SVN_ERR(svn_fs_fs__dag_get_copyroot(&rev_mine, &path_mine,
3209                                       parent_path->node));
3210
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)
3215     {
3216       *rev_p = rev_mine;
3217       *path_p = path_mine;
3218     }
3219   else
3220     {
3221       *rev_p = rev_parent;
3222       *path_p = path_parent;
3223     }
3224
3225   return SVN_NO_ERROR;
3226 }
3227
3228
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,
3232                                     const char *path,
3233                                     apr_pool_t *pool)
3234 {
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;
3242
3243   /* Initialize return values. */
3244   *root_p = NULL;
3245   *path_p = NULL;
3246
3247   path = svn_fs__canonicalize_abspath(path, pool);
3248   SVN_ERR(open_path(&parent_path, root, path, 0, NULL, pool));
3249
3250   /* Find the youngest copyroot in the path of this node-rev, which
3251      will indicate the target of the innermost copy affecting the
3252      node-rev. */
3253   SVN_ERR(find_youngest_copyroot(&copy_dst_rev, &copy_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;
3257
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(&copy_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(&copy_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;
3271
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:
3276
3277         copy dir1 dir2
3278         create dir2/new-thing
3279         commit
3280
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.
3285   */
3286   SVN_ERR(svn_fs_fs__dag_get_revision(&created_rev, copy_dst_node, pool));
3287   if (created_rev == copy_dst_rev)
3288     {
3289       const svn_fs_id_t *pred;
3290       SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred, copy_dst_node));
3291       if (! pred)
3292         return SVN_NO_ERROR;
3293     }
3294
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;
3299 }
3300
3301
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
3306    PATH@REVISION.
3307
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,
3313               svn_fs_t *fs,
3314               svn_fs_root_t *root,
3315               const char *path,
3316               apr_pool_t *pool)
3317 {
3318   const char *copy_path, *copy_src_path, *remainder_path;
3319   svn_fs_root_t *copy_root;
3320   svn_revnum_t copy_src_rev;
3321
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(&copy_root, &copy_path, root, path, pool));
3325   if (! copy_root)
3326     {
3327       *prev_rev = SVN_INVALID_REVNUM;
3328       *prev_path = NULL;
3329       return SVN_NO_ERROR;
3330     }
3331
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.
3337
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"
3343      before the copy.
3344   */
3345   SVN_ERR(fs_copied_from(&copy_src_rev, &copy_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;
3351 }
3352
3353
3354 static svn_error_t *
3355 fs_node_origin_rev(svn_revnum_t *revision,
3356                    svn_fs_root_t *root,
3357                    const char *path,
3358                    apr_pool_t *pool)
3359 {
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;
3363
3364   path = svn_fs__canonicalize_abspath(path, pool);
3365
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);
3369
3370   /* Is it a brand new uncommitted node? */
3371   if (node_id[0] == '_')
3372     {
3373       *revision = SVN_INVALID_REVNUM;
3374       return SVN_NO_ERROR;
3375     }
3376
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))
3381     {
3382       *revision = SVN_STR_TO_REV(dash + 1);
3383       return SVN_NO_ERROR;
3384     }
3385
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)
3389     {
3390       *revision = 0;
3391       return SVN_NO_ERROR;
3392     }
3393
3394   /* OK, it's an old-style ID?  Maybe it's cached. */
3395   SVN_ERR(svn_fs_fs__get_node_origin(&cached_origin_id,
3396                                      fs,
3397                                      node_id,
3398                                      pool));
3399   if (cached_origin_id != NULL)
3400     {
3401       *revision = svn_fs_fs__id_rev(cached_origin_id);
3402       return SVN_NO_ERROR;
3403     }
3404
3405   {
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;
3413     dag_node_t *node;
3414     const svn_fs_id_t *pred_id;
3415
3416     /* Walk the closest-copy chain back to the first copy in our history.
3417
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. */
3421     while (1)
3422       {
3423         svn_revnum_t currev;
3424         const char *curpath = lastpath->data;
3425
3426         svn_pool_clear(subpool);
3427
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));
3433
3434         /* Find the previous location using the closest-copy shortcut. */
3435         SVN_ERR(prev_location(&curpath, &currev, fs, curroot, curpath,
3436                               subpool));
3437         if (! curpath)
3438           break;
3439
3440         /* Update our LASTPATH and LASTREV variables (which survive
3441            SUBPOOL). */
3442         svn_stringbuf_set(lastpath, curpath);
3443         lastrev = currev;
3444       }
3445
3446     /* Walk the predecessor links back to origin. */
3447     SVN_ERR(svn_fs_fs__node_id(&pred_id, curroot, lastpath->data, predidpool));
3448     do
3449       {
3450         svn_pool_clear(subpool);
3451         SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, subpool));
3452
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;
3461       }
3462     while (pred_id);
3463
3464     /* When we get here, NODE should be the first node-revision in our
3465        chain. */
3466     SVN_ERR(svn_fs_fs__dag_get_revision(revision, node, pool));
3467
3468     /* Wow, I don't want to have to do all that again.  Let's cache
3469        the result. */
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));
3473
3474     svn_pool_destroy(subpool);
3475     svn_pool_destroy(predidpool);
3476     return SVN_NO_ERROR;
3477   }
3478 }
3479
3480
3481 struct history_prev_args
3482 {
3483   svn_fs_history_t **prev_history_p;
3484   svn_fs_history_t *history;
3485   svn_boolean_t cross_copies;
3486   apr_pool_t *pool;
3487 };
3488
3489
3490 static svn_error_t *
3491 history_prev(void *baton, apr_pool_t *pool)
3492 {
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;
3503   dag_node_t *node;
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;
3508
3509   /* Initialize our return value. */
3510   *prev_history = NULL;
3511
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))
3517     {
3518       reported = FALSE;
3519       if (! args->cross_copies)
3520         return SVN_NO_ERROR;
3521       path = fhd->path_hint;
3522       revision = fhd->rev_hint;
3523     }
3524
3525   /* Construct a ROOT for the current revision. */
3526   SVN_ERR(svn_fs_fs__revision_root(&root, fs, revision, pool));
3527
3528   /* Open PATH/REVISION, and get its node and a bunch of other
3529      goodies.  */
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));
3534
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
3541      know that ... */
3542   if (revision == commit_rev)
3543     {
3544       if (! reported)
3545         {
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;
3553         }
3554       else
3555         {
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;
3560
3561           SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
3562           if (! pred_id)
3563             return SVN_NO_ERROR;
3564
3565           /* Replace NODE and friends with the information from its
3566              predecessor. */
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));
3570         }
3571     }
3572
3573   /* Find the youngest copyroot in the path of this node, including
3574      itself. */
3575   SVN_ERR(find_youngest_copyroot(&copyroot_rev, &copyroot_path, fs,
3576                                  parent_path, pool));
3577
3578   /* Initialize some state variables. */
3579   src_path = NULL;
3580   src_rev = SVN_INVALID_REVNUM;
3581   dst_rev = SVN_INVALID_REVNUM;
3582
3583   if (copyroot_rev > commit_rev)
3584     {
3585       const char *remainder_path;
3586       const char *copy_dst, *copy_src;
3587       svn_fs_root_t *copyroot_root;
3588
3589       SVN_ERR(svn_fs_fs__revision_root(&copyroot_root, fs, copyroot_rev,
3590                                        pool));
3591       SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, FALSE, pool));
3592       copy_dst = svn_fs_fs__dag_get_created_path(node);
3593
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);
3603
3604       if (remainder_path)
3605         {
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(&copy_src, node));
3611
3612           dst_rev = copyroot_rev;
3613           src_path = svn_fspath__join(copy_src, remainder_path, pool);
3614         }
3615     }
3616
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))
3620     {
3621       svn_boolean_t retry = FALSE;
3622
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
3626          search. */
3627       if ((dst_rev == revision) && reported)
3628         retry = TRUE;
3629
3630       *prev_history = assemble_history(fs, apr_pstrdup(retpool, path),
3631                                        dst_rev, ! retry,
3632                                        src_path, src_rev, retpool);
3633     }
3634   else
3635     {
3636       *prev_history = assemble_history(fs, apr_pstrdup(retpool, commit_path),
3637                                        commit_rev, TRUE, NULL,
3638                                        SVN_INVALID_REVNUM, retpool);
3639     }
3640
3641   return SVN_NO_ERROR;
3642 }
3643
3644
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,
3653                 apr_pool_t *pool)
3654 {
3655   svn_fs_history_t *prev_history = NULL;
3656   fs_history_data_t *fhd = history->fsap_data;
3657   svn_fs_t *fs = fhd->fs;
3658
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)
3664     {
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);
3671     }
3672   else
3673     {
3674       struct history_prev_args args;
3675       prev_history = history;
3676
3677       while (1)
3678         {
3679           args.prev_history_p = &prev_history;
3680           args.history = prev_history;
3681           args.cross_copies = cross_copies;
3682           args.pool = pool;
3683           SVN_ERR(history_prev(&args, pool));
3684
3685           if (! prev_history)
3686             break;
3687           fhd = prev_history->fsap_data;
3688           if (fhd->is_interesting)
3689             break;
3690         }
3691     }
3692
3693   *prev_history_p = prev_history;
3694   return SVN_NO_ERROR;
3695 }
3696
3697
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,
3704                     apr_pool_t *pool)
3705 {
3706   fs_history_data_t *fhd = history->fsap_data;
3707
3708   *path = apr_pstrdup(pool, fhd->path);
3709   *revision = fhd->revision;
3710   return SVN_NO_ERROR;
3711 }
3712
3713 static history_vtable_t history_vtable = {
3714   fs_history_prev,
3715   fs_history_location
3716 };
3717
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,
3725                  const char *path,
3726                  svn_revnum_t revision,
3727                  svn_boolean_t is_interesting,
3728                  const char *path_hint,
3729                  svn_revnum_t rev_hint,
3730                  apr_pool_t *pool)
3731 {
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;
3739   fhd->fs = fs;
3740
3741   history->vtable = &history_vtable;
3742   history->fsap_data = fhd;
3743   return history;
3744 }
3745
3746 \f
3747 /* mergeinfo queries */
3748
3749
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.
3756
3757    POOL is used for temporary allocations, including the mergeinfo
3758    hashes passed to actions; RESULT_POOL is used for the mergeinfo added
3759    to RESULT_CATALOG.
3760  */
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)
3768 {
3769   apr_hash_t *entries;
3770   apr_hash_index_t *hi;
3771   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3772
3773   SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, dir_dag,
3774                                      scratch_pool));
3775
3776   for (hi = apr_hash_first(scratch_pool, entries);
3777        hi;
3778        hi = apr_hash_next(hi))
3779     {
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;
3784
3785       svn_pool_clear(iterpool);
3786
3787       kid_path = svn_fspath__join(this_path, dirent->name, iterpool);
3788       SVN_ERR(get_dag(&kid_dag, root, kid_path, TRUE, iterpool));
3789
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));
3792
3793       if (has_mergeinfo)
3794         {
3795           /* Save this particular node's mergeinfo. */
3796           apr_hash_t *proplist;
3797           svn_mergeinfo_t kid_mergeinfo;
3798           svn_string_t *mergeinfo_string;
3799           svn_error_t *err;
3800
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)
3804             {
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"),
3809                  idstr->data);
3810             }
3811
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
3814              error. */
3815           err = svn_mergeinfo_parse(&kid_mergeinfo,
3816                                     mergeinfo_string->data,
3817                                     result_pool);
3818           if (err)
3819             {
3820               if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
3821                 svn_error_clear(err);
3822               else
3823                 return svn_error_trace(err);
3824               }
3825           else
3826             {
3827               svn_hash_sets(result_catalog, apr_pstrdup(result_pool, kid_path),
3828                             kid_mergeinfo);
3829             }
3830         }
3831
3832       if (go_down)
3833         SVN_ERR(crawl_directory_dag_for_mergeinfo(root,
3834                                                   kid_path,
3835                                                   kid_dag,
3836                                                   result_catalog,
3837                                                   result_pool,
3838                                                   iterpool));
3839     }
3840
3841   svn_pool_destroy(iterpool);
3842   return SVN_NO_ERROR;
3843 }
3844
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..
3848  */
3849 static const char *
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,
3854                     apr_pool_t *pool)
3855 {
3856   apr_int64_t number = rev_root->rev;
3857   number = number * 4
3858          + (inherit == svn_mergeinfo_nearest_ancestor ? 2 : 0)
3859          + (adjust_inherited_mergeinfo ? 1 : 0);
3860
3861   return svn_fs_fs__combine_number_and_string(number, path, pool);
3862 }
3863
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.
3868  */
3869 static svn_error_t *
3870 get_mergeinfo_for_path_internal(svn_mergeinfo_t *mergeinfo,
3871                                 svn_fs_root_t *rev_root,
3872                                 const char *path,
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)
3877 {
3878   parent_path_t *parent_path, *nearest_ancestor;
3879   apr_hash_t *proplist;
3880   svn_string_t *mergeinfo_string;
3881
3882   path = svn_fs__canonicalize_abspath(path, scratch_pool);
3883
3884   SVN_ERR(open_path(&parent_path, rev_root, path, 0, NULL, scratch_pool));
3885
3886   if (inherit == svn_mergeinfo_nearest_ancestor && ! parent_path->parent)
3887     return SVN_NO_ERROR;
3888
3889   if (inherit == svn_mergeinfo_nearest_ancestor)
3890     nearest_ancestor = parent_path->parent;
3891   else
3892     nearest_ancestor = parent_path;
3893
3894   while (TRUE)
3895     {
3896       svn_boolean_t has_mergeinfo;
3897
3898       SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo,
3899                                            nearest_ancestor->node));
3900       if (has_mergeinfo)
3901         break;
3902
3903       /* No need to loop if we're looking for explicit mergeinfo. */
3904       if (inherit == svn_mergeinfo_explicit)
3905         {
3906           return SVN_NO_ERROR;
3907         }
3908
3909       nearest_ancestor = nearest_ancestor->parent;
3910
3911       /* Run out?  There's no mergeinfo. */
3912       if (!nearest_ancestor)
3913         {
3914           return SVN_NO_ERROR;
3915         }
3916     }
3917
3918   SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, nearest_ancestor->node,
3919                                       scratch_pool));
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);
3926
3927   /* Parse the mergeinfo; store the result in *MERGEINFO. */
3928   {
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
3931        error. */
3932     svn_error_t *err = svn_mergeinfo_parse(mergeinfo,
3933                                            mergeinfo_string->data,
3934                                            result_pool);
3935     if (err)
3936       {
3937         if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
3938           {
3939             svn_error_clear(err);
3940             err = NULL;
3941             *mergeinfo = NULL;
3942           }
3943         return svn_error_trace(err);
3944       }
3945   }
3946
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))
3952     {
3953       svn_mergeinfo_t tmp_mergeinfo;
3954
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,
3962                                                scratch_pool),
3963                                              result_pool));
3964     }
3965
3966   return SVN_NO_ERROR;
3967 }
3968
3969 /* Caching wrapper around get_mergeinfo_for_path_internal().
3970  */
3971 static svn_error_t *
3972 get_mergeinfo_for_path(svn_mergeinfo_t *mergeinfo,
3973                        svn_fs_root_t *rev_root,
3974                        const char *path,
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)
3979 {
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;
3984
3985   *mergeinfo = NULL;
3986
3987   cache_key = mergeinfo_cache_key(path, rev_root, inherit,
3988                                   adjust_inherited_mergeinfo, scratch_pool);
3989   if (ffd->mergeinfo_existence_cache)
3990     {
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));
3998     }
3999
4000   if (! found)
4001     {
4002       SVN_ERR(get_mergeinfo_for_path_internal(mergeinfo, rev_root, path,
4003                                               inherit,
4004                                               adjust_inherited_mergeinfo,
4005                                               result_pool, scratch_pool));
4006       if (ffd->mergeinfo_existence_cache)
4007         {
4008           mergeinfo_exists = svn_stringbuf_create(*mergeinfo ? "1" : "0",
4009                                                   scratch_pool);
4010           SVN_ERR(svn_cache__set(ffd->mergeinfo_existence_cache,
4011                                  cache_key, mergeinfo_exists, scratch_pool));
4012           if (*mergeinfo)
4013             SVN_ERR(svn_cache__set(ffd->mergeinfo_cache,
4014                                   cache_key, *mergeinfo, scratch_pool));
4015         }
4016     }
4017
4018   return SVN_NO_ERROR;
4019 }
4020
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,
4027                          const char *path,
4028                          apr_pool_t *result_pool,
4029                          apr_pool_t *scratch_pool)
4030 {
4031   dag_node_t *this_dag;
4032   svn_boolean_t go_down;
4033
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,
4036                                                         this_dag));
4037   if (go_down)
4038     SVN_ERR(crawl_directory_dag_for_mergeinfo(root,
4039                                               path,
4040                                               this_dag,
4041                                               result_catalog,
4042                                               result_pool,
4043                                               scratch_pool));
4044   return SVN_NO_ERROR;
4045 }
4046
4047
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)
4060 {
4061   svn_mergeinfo_catalog_t result_catalog = svn_hash__make(result_pool);
4062   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4063   int i;
4064
4065   for (i = 0; i < paths->nelts; i++)
4066     {
4067       svn_error_t *err;
4068       svn_mergeinfo_t path_mergeinfo;
4069       const char *path = APR_ARRAY_IDX(paths, i, const char *);
4070
4071       svn_pool_clear(iterpool);
4072
4073       err = get_mergeinfo_for_path(&path_mergeinfo, root, path,
4074                                    inherit, adjust_inherited_mergeinfo,
4075                                    result_pool, iterpool);
4076       if (err)
4077         {
4078           if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
4079             {
4080               svn_error_clear(err);
4081               err = NULL;
4082               path_mergeinfo = NULL;
4083             }
4084           else
4085             {
4086               return svn_error_trace(err);
4087             }
4088         }
4089
4090       if (path_mergeinfo)
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));
4095     }
4096   svn_pool_destroy(iterpool);
4097
4098   *mergeinfo_catalog = result_catalog;
4099   return SVN_NO_ERROR;
4100 }
4101
4102
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)
4113 {
4114   fs_fs_data_t *ffd = root->fs->fsap_data;
4115
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);
4119
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);
4127
4128   /* Retrieve a path -> mergeinfo hash mapping. */
4129   return get_mergeinfos_for_paths(root, catalog, paths,
4130                                   inherit,
4131                                   include_descendants,
4132                                   adjust_inherited_mergeinfo,
4133                                   result_pool, scratch_pool);
4134 }
4135
4136
4137 /* The vtable associated with root objects. */
4138 static root_vtable_t root_vtable = {
4139   fs_paths_changed,
4140   svn_fs_fs__check_path,
4141   fs_node_history,
4142   svn_fs_fs__node_id,
4143   svn_fs_fs__node_created_rev,
4144   fs_node_origin_rev,
4145   fs_node_created_path,
4146   fs_delete_node,
4147   fs_copied_from,
4148   fs_closest_copy,
4149   fs_node_prop,
4150   fs_node_proplist,
4151   fs_change_node_prop,
4152   fs_props_changed,
4153   fs_dir_entries,
4154   fs_make_dir,
4155   fs_copy,
4156   fs_revision_link,
4157   fs_file_length,
4158   fs_file_checksum,
4159   fs_file_contents,
4160   fs_try_process_file_contents,
4161   fs_make_file,
4162   fs_apply_textdelta,
4163   fs_apply_text,
4164   fs_contents_changed,
4165   fs_get_file_delta_stream,
4166   fs_merge,
4167   fs_get_mergeinfo,
4168 };
4169
4170 /* Construct a new root object in FS, allocated from POOL.  */
4171 static svn_fs_root_t *
4172 make_root(svn_fs_t *fs,
4173           apr_pool_t *pool)
4174 {
4175   svn_fs_root_t *root = apr_pcalloc(pool, sizeof(*root));
4176
4177   root->fs = fs;
4178   root->pool = pool;
4179   root->vtable = &root_vtable;
4180
4181   return root;
4182 }
4183
4184
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,
4189                    svn_revnum_t rev,
4190                    dag_node_t *root_dir,
4191                    apr_pool_t *pool)
4192 {
4193   svn_fs_root_t *root = make_root(fs, pool);
4194   fs_rev_root_data_t *frd = apr_pcalloc(root->pool, sizeof(*frd));
4195
4196   root->is_txn_root = FALSE;
4197   root->rev = rev;
4198
4199   frd->root_dir = root_dir;
4200   frd->copyfrom_cache = svn_hash__make(root->pool);
4201
4202   root->fsap_data = frd;
4203
4204   return root;
4205 }
4206
4207
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,
4213               svn_fs_t *fs,
4214               const char *txn,
4215               svn_revnum_t base_rev,
4216               apr_uint32_t flags,
4217               apr_pool_t *pool)
4218 {
4219   svn_fs_root_t *root = make_root(fs, pool);
4220   fs_txn_root_data_t *frd = apr_pcalloc(root->pool, sizeof(*frd));
4221
4222   root->is_txn_root = TRUE;
4223   root->txn = apr_pstrdup(root->pool, txn);
4224   root->txn_flags = flags;
4225   root->rev = base_rev;
4226
4227   frd->txn_id = txn;
4228
4229   /* Because this cache actually tries to invalidate elements, keep
4230      the number of elements per page down.
4231
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,
4238                                       32, 20, FALSE,
4239                                       apr_pstrcat(pool, txn, ":TXN",
4240                                                   (char *)NULL),
4241                                       root->pool));
4242
4243   /* Initialize transaction-local caches in FS.
4244
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));
4248
4249   root->fsap_data = frd;
4250
4251   *root_p = root;
4252   return SVN_NO_ERROR;
4253 }
4254
4255
4256 \f
4257 /* Verify. */
4258 static APR_INLINE const char *
4259 stringify_node(dag_node_t *node,
4260                apr_pool_t *pool)
4261 {
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;
4264 }
4265
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,
4271             svn_revnum_t rev,
4272             apr_pool_t *pool)
4273 {
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);
4278   int pred_count;
4279   svn_node_kind_t kind;
4280   apr_pool_t *iterpool = svn_pool_create(pool);
4281
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);
4288
4289   /* Sanity check. */
4290   if (mergeinfo_count < 0)
4291     return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4292                              "Negative mergeinfo-count %" APR_INT64_T_FMT
4293                              " on node '%s'",
4294                              mergeinfo_count, stringify_node(node, iterpool));
4295
4296   /* Issue #4129. (This check will explicitly catch non-root instances too.) */
4297   if (pred_id)
4298     {
4299       dag_node_t *pred;
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),
4309                                  pred_pred_count);
4310     }
4311
4312   /* Kind-dependent verifications. */
4313   if (kind == svn_node_none)
4314     {
4315       return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4316                                "Node '%s' has kind 'none'",
4317                                stringify_node(node, iterpool));
4318     }
4319   if (kind == svn_node_file)
4320     {
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);
4328     }
4329   if (kind == svn_node_dir)
4330     {
4331       apr_hash_t *entries;
4332       apr_hash_index_t *hi;
4333       apr_int64_t children_mergeinfo = 0;
4334
4335       SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool));
4336
4337       /* Compute CHILDREN_MERGEINFO. */
4338       for (hi = apr_hash_first(pool, entries);
4339            hi;
4340            hi = apr_hash_next(hi))
4341         {
4342           svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
4343           dag_node_t *child;
4344           svn_revnum_t child_rev;
4345           apr_int64_t child_mergeinfo;
4346
4347           svn_pool_clear(iterpool);
4348
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));
4352
4353           if (child_rev == rev)
4354             SVN_ERR(verify_node(child, rev, iterpool));
4355
4356           SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&child_mergeinfo, child));
4357           children_mergeinfo += child_mergeinfo;
4358         }
4359
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);
4369     }
4370
4371   svn_pool_destroy(iterpool);
4372   return SVN_NO_ERROR;
4373 }
4374
4375 svn_error_t *
4376 svn_fs_fs__verify_root(svn_fs_root_t *root,
4377                        apr_pool_t *pool)
4378 {
4379   svn_fs_t *fs = root->fs;
4380   dag_node_t *root_dir;
4381
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(). */
4385
4386   /* Callers should disable caches by setting SVN_FS_CONFIG_FSFS_CACHE_NS;
4387      see r1462436.
4388
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. */
4392
4393   if (root->is_txn_root)
4394     {
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));
4397     }
4398   else
4399     {
4400       fs_rev_root_data_t *frd = root->fsap_data;
4401       root_dir = frd->root_dir;
4402     }
4403
4404   /* Recursively verify ROOT_DIR. */
4405   SVN_ERR(verify_node(root_dir, root->rev, pool));
4406
4407   /* Verify explicitly the predecessor of the root. */
4408   {
4409     const svn_fs_id_t *pred_id;
4410
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'",
4417                                root->rev,
4418                                (pred_id
4419                                 ? svn_fs_fs__id_unparse(pred_id, pool)->data
4420                                 : "(null)"));
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",
4425                                root->txn);
4426
4427     /* Check the predecessor's revision. */
4428     if (pred_id)
4429       {
4430         svn_revnum_t pred_rev = svn_fs_fs__id_rev(pred_id);
4431         if (! root->is_txn_root && pred_rev+1 != root->rev)
4432           /* Issue #4129. */
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"
4440                                    " is r%ld"
4441                                    " but should be r%ld",
4442                                    root->txn, pred_rev, root->rev);
4443       }
4444   }
4445
4446   return SVN_NO_ERROR;
4447 }