]> CyberLeo.Net >> Repos - FreeBSD/releng/10.3.git/blob - contrib/subversion/subversion/libsvn_fs_fs/tree.c
- Copy stable/10@296371 to releng/10.3 in preparation for 10.3-RC1
[FreeBSD/releng/10.3.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                              apr_pool_t *pool);
131
132 static svn_fs_root_t *make_revision_root(svn_fs_t *fs, svn_revnum_t rev,
133                                          dag_node_t *root_dir,
134                                          apr_pool_t *pool);
135
136 static svn_error_t *make_txn_root(svn_fs_root_t **root_p,
137                                   svn_fs_t *fs, const char *txn,
138                                   svn_revnum_t base_rev, apr_uint32_t flags,
139                                   apr_pool_t *pool);
140
141 \f
142 /*** Node Caching ***/
143
144 /* 1st level cache */
145
146 /* An entry in the first-level cache.  REVISION and PATH form the key that
147    will ultimately be matched.
148  */
149 typedef struct cache_entry_t
150 {
151   /* hash value derived from PATH, REVISION.
152      Used to short-circuit failed lookups. */
153   apr_uint32_t hash_value;
154
155   /* revision to which the NODE belongs */
156   svn_revnum_t revision;
157
158   /* path of the NODE */
159   char *path;
160
161   /* cached value of strlen(PATH). */
162   apr_size_t path_len;
163
164   /* the node allocated in the cache's pool. NULL for empty entries. */
165   dag_node_t *node;
166 } cache_entry_t;
167
168 /* Number of entries in the cache.  Keep this low to keep pressure on the
169    CPU caches low as well.  A binary value is most efficient.  If we walk
170    a directory tree, we want enough entries to store nodes for all files
171    without overwriting the nodes for the parent folder.  That way, there
172    will be no unnecessary misses (except for a few random ones caused by
173    hash collision).
174
175    The actual number of instances may be higher but entries that got
176    overwritten are no longer visible.
177  */
178 enum { BUCKET_COUNT = 256 };
179
180 /* The actual cache structure.  All nodes will be allocated in POOL.
181    When the number of INSERTIONS (i.e. objects created form that pool)
182    exceeds a certain threshold, the pool will be cleared and the cache
183    with it.
184  */
185 struct fs_fs_dag_cache_t
186 {
187   /* fixed number of (possibly empty) cache entries */
188   cache_entry_t buckets[BUCKET_COUNT];
189
190   /* pool used for all node allocation */
191   apr_pool_t *pool;
192
193   /* number of entries created from POOL since the last cleanup */
194   apr_size_t insertions;
195
196   /* Property lookups etc. have a very high locality (75% re-hit).
197      Thus, remember the last hit location for optimistic lookup. */
198   apr_size_t last_hit;
199 };
200
201 fs_fs_dag_cache_t*
202 svn_fs_fs__create_dag_cache(apr_pool_t *pool)
203 {
204   fs_fs_dag_cache_t *result = apr_pcalloc(pool, sizeof(*result));
205   result->pool = svn_pool_create(pool);
206
207   return result;
208 }
209
210 /* Clears the CACHE at regular intervals (destroying all cached nodes)
211  */
212 static void
213 auto_clear_dag_cache(fs_fs_dag_cache_t* cache)
214 {
215   if (cache->insertions > BUCKET_COUNT)
216     {
217       svn_pool_clear(cache->pool);
218
219       memset(cache->buckets, 0, sizeof(cache->buckets));
220       cache->insertions = 0;
221     }
222 }
223
224 /* For the given REVISION and PATH, return the respective entry in CACHE.
225    If the entry is empty, its NODE member will be NULL and the caller
226    may then set it to the corresponding DAG node allocated in CACHE->POOL.
227  */
228 static cache_entry_t *
229 cache_lookup( fs_fs_dag_cache_t *cache
230             , svn_revnum_t revision
231             , const char *path)
232 {
233   apr_size_t i, bucket_index;
234   apr_size_t path_len = strlen(path);
235   apr_uint32_t hash_value = (apr_uint32_t)revision;
236
237 #if SVN_UNALIGNED_ACCESS_IS_OK
238   /* "randomizing" / distributing factor used in our hash function */
239   const apr_uint32_t factor = 0xd1f3da69;
240 #endif
241
242   /* optimistic lookup: hit the same bucket again? */
243   cache_entry_t *result = &cache->buckets[cache->last_hit];
244   if (   (result->revision == revision)
245       && (result->path_len == path_len)
246       && !memcmp(result->path, path, path_len))
247     {
248       return result;
249     }
250
251   /* need to do a full lookup.  Calculate the hash value
252      (HASH_VALUE has been initialized to REVISION). */
253   i = 0;
254 #if SVN_UNALIGNED_ACCESS_IS_OK
255   /* We relax the dependency chain between iterations by processing
256      two chunks from the input per hash_value self-multiplication.
257      The HASH_VALUE update latency is now 1 MUL latency + 1 ADD latency
258      per 2 chunks instead of 1 chunk.
259    */
260   for (; i + 8 <= path_len; i += 8)
261     hash_value = hash_value * factor * factor
262                + (  *(const apr_uint32_t*)(path + i) * factor
263                   + *(const apr_uint32_t*)(path + i + 4));
264 #endif
265
266   for (; i < path_len; ++i)
267     /* Help GCC to minimize the HASH_VALUE update latency by splitting the
268        MUL 33 of the naive implementation: h = h * 33 + path[i].  This
269        shortens the dependency chain from 1 shift + 2 ADDs to 1 shift + 1 ADD.
270      */
271     hash_value = hash_value * 32 + (hash_value + (unsigned char)path[i]);
272
273   bucket_index = hash_value + (hash_value >> 16);
274   bucket_index = (bucket_index + (bucket_index >> 8)) % BUCKET_COUNT;
275
276   /* access the corresponding bucket and remember its location */
277   result = &cache->buckets[bucket_index];
278   cache->last_hit = bucket_index;
279
280   /* if it is *NOT* a match,  clear the bucket, expect the caller to fill
281      in the node and count it as an insertion */
282   if (   (result->hash_value != hash_value)
283       || (result->revision != revision)
284       || (result->path_len != path_len)
285       || memcmp(result->path, path, path_len))
286     {
287       result->hash_value = hash_value;
288       result->revision = revision;
289       if (result->path_len < path_len)
290         result->path = apr_palloc(cache->pool, path_len + 1);
291       result->path_len = path_len;
292       memcpy(result->path, path, path_len + 1);
293
294       result->node = NULL;
295
296       cache->insertions++;
297     }
298
299   return result;
300 }
301
302 /* 2nd level cache */
303
304 /* Find and return the DAG node cache for ROOT and the key that
305    should be used for PATH. */
306 static void
307 locate_cache(svn_cache__t **cache,
308              const char **key,
309              svn_fs_root_t *root,
310              const char *path,
311              apr_pool_t *pool)
312 {
313   if (root->is_txn_root)
314     {
315       fs_txn_root_data_t *frd = root->fsap_data;
316       if (cache) *cache = frd->txn_node_cache;
317       if (key && path) *key = path;
318     }
319   else
320     {
321       fs_fs_data_t *ffd = root->fs->fsap_data;
322       if (cache) *cache = ffd->rev_node_cache;
323       if (key && path) *key
324         = svn_fs_fs__combine_number_and_string(root->rev, path, pool);
325     }
326 }
327
328 /* Return NODE_P for PATH from ROOT's node cache, or NULL if the node
329    isn't cached; read it from the FS. *NODE_P is allocated in POOL. */
330 static svn_error_t *
331 dag_node_cache_get(dag_node_t **node_p,
332                    svn_fs_root_t *root,
333                    const char *path,
334                    apr_pool_t *pool)
335 {
336   svn_boolean_t found;
337   dag_node_t *node = NULL;
338   svn_cache__t *cache;
339   const char *key;
340
341   SVN_ERR_ASSERT(*path == '/');
342
343   if (!root->is_txn_root)
344     {
345       /* immutable DAG node. use the global caches for it */
346
347       fs_fs_data_t *ffd = root->fs->fsap_data;
348       cache_entry_t *bucket;
349
350       auto_clear_dag_cache(ffd->dag_node_cache);
351       bucket = cache_lookup(ffd->dag_node_cache, root->rev, path);
352       if (bucket->node == NULL)
353         {
354           locate_cache(&cache, &key, root, path, pool);
355           SVN_ERR(svn_cache__get((void **)&node, &found, cache, key, pool));
356           if (found && node)
357             {
358               /* Patch up the FS, since this might have come from an old FS
359               * object. */
360               svn_fs_fs__dag_set_fs(node, root->fs);
361
362               /* Retain the DAG node in L1 cache. */
363               bucket->node = svn_fs_fs__dag_dup(node,
364                                                 ffd->dag_node_cache->pool);
365             }
366         }
367       else
368         {
369           /* Copy the node from L1 cache into the passed-in POOL. */
370           node = svn_fs_fs__dag_dup(bucket->node, pool);
371         }
372     }
373   else
374     {
375       /* DAG is mutable / may become invalid. Use the TXN-local cache */
376
377       locate_cache(&cache, &key, root, path, pool);
378
379       SVN_ERR(svn_cache__get((void **) &node, &found, cache, key, pool));
380       if (found && node)
381         {
382           /* Patch up the FS, since this might have come from an old FS
383           * object. */
384           svn_fs_fs__dag_set_fs(node, root->fs);
385         }
386     }
387
388   *node_p = node;
389
390   return SVN_NO_ERROR;
391 }
392
393
394 /* Add the NODE for PATH to ROOT's node cache. */
395 static svn_error_t *
396 dag_node_cache_set(svn_fs_root_t *root,
397                    const char *path,
398                    dag_node_t *node,
399                    apr_pool_t *pool)
400 {
401   svn_cache__t *cache;
402   const char *key;
403
404   SVN_ERR_ASSERT(*path == '/');
405
406   /* Do *not* attempt to dup and put the node into L1.
407    * dup() is twice as expensive as an L2 lookup (which will set also L1).
408    */
409   locate_cache(&cache, &key, root, path, pool);
410
411   return svn_cache__set(cache, key, node, pool);
412 }
413
414
415 /* Baton for find_descendents_in_cache. */
416 struct fdic_baton {
417   const char *path;
418   apr_array_header_t *list;
419   apr_pool_t *pool;
420 };
421
422 /* If the given item is a descendent of BATON->PATH, push
423  * it onto BATON->LIST (copying into BATON->POOL).  Implements
424  * the svn_iter_apr_hash_cb_t prototype. */
425 static svn_error_t *
426 find_descendents_in_cache(void *baton,
427                           const void *key,
428                           apr_ssize_t klen,
429                           void *val,
430                           apr_pool_t *pool)
431 {
432   struct fdic_baton *b = baton;
433   const char *item_path = key;
434
435   if (svn_fspath__skip_ancestor(b->path, item_path))
436     APR_ARRAY_PUSH(b->list, const char *) = apr_pstrdup(b->pool, item_path);
437
438   return SVN_NO_ERROR;
439 }
440
441 /* Invalidate cache entries for PATH and any of its children.  This
442    should *only* be called on a transaction root! */
443 static svn_error_t *
444 dag_node_cache_invalidate(svn_fs_root_t *root,
445                           const char *path,
446                           apr_pool_t *pool)
447 {
448   struct fdic_baton b;
449   svn_cache__t *cache;
450   apr_pool_t *iterpool;
451   int i;
452
453   b.path = path;
454   b.pool = svn_pool_create(pool);
455   b.list = apr_array_make(b.pool, 1, sizeof(const char *));
456
457   SVN_ERR_ASSERT(root->is_txn_root);
458   locate_cache(&cache, NULL, root, NULL, b.pool);
459
460
461   SVN_ERR(svn_cache__iter(NULL, cache, find_descendents_in_cache,
462                           &b, b.pool));
463
464   iterpool = svn_pool_create(b.pool);
465
466   for (i = 0; i < b.list->nelts; i++)
467     {
468       const char *descendent = APR_ARRAY_IDX(b.list, i, const char *);
469       svn_pool_clear(iterpool);
470       SVN_ERR(svn_cache__set(cache, descendent, NULL, iterpool));
471     }
472
473   svn_pool_destroy(iterpool);
474   svn_pool_destroy(b.pool);
475   return SVN_NO_ERROR;
476 }
477
478
479 \f
480 /* Creating transaction and revision root nodes.  */
481
482 svn_error_t *
483 svn_fs_fs__txn_root(svn_fs_root_t **root_p,
484                     svn_fs_txn_t *txn,
485                     apr_pool_t *pool)
486 {
487   apr_uint32_t flags = 0;
488   apr_hash_t *txnprops;
489
490   /* Look for the temporary txn props representing 'flags'. */
491   SVN_ERR(svn_fs_fs__txn_proplist(&txnprops, txn, pool));
492   if (txnprops)
493     {
494       if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD))
495         flags |= SVN_FS_TXN_CHECK_OOD;
496
497       if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS))
498         flags |= SVN_FS_TXN_CHECK_LOCKS;
499     }
500
501   return make_txn_root(root_p, txn->fs, txn->id, txn->base_rev, flags, pool);
502 }
503
504
505 svn_error_t *
506 svn_fs_fs__revision_root(svn_fs_root_t **root_p,
507                          svn_fs_t *fs,
508                          svn_revnum_t rev,
509                          apr_pool_t *pool)
510 {
511   dag_node_t *root_dir;
512
513   SVN_ERR(svn_fs__check_fs(fs, TRUE));
514
515   SVN_ERR(svn_fs_fs__dag_revision_root(&root_dir, fs, rev, pool));
516
517   *root_p = make_revision_root(fs, rev, root_dir, pool);
518
519   return SVN_NO_ERROR;
520 }
521
522
523 \f
524 /* Getting dag nodes for roots.  */
525
526
527 /* Set *NODE_P to a freshly opened dag node referring to the root
528    directory of ROOT, allocating from POOL.  */
529 static svn_error_t *
530 root_node(dag_node_t **node_p,
531           svn_fs_root_t *root,
532           apr_pool_t *pool)
533 {
534   if (root->is_txn_root)
535     {
536       /* It's a transaction root.  Open a fresh copy.  */
537       return svn_fs_fs__dag_txn_root(node_p, root->fs, root->txn, pool);
538     }
539   else
540     {
541       /* It's a revision root, so we already have its root directory
542          opened.  */
543       fs_rev_root_data_t *frd = root->fsap_data;
544       *node_p = svn_fs_fs__dag_dup(frd->root_dir, pool);
545       return SVN_NO_ERROR;
546     }
547 }
548
549
550 /* Set *NODE_P to a mutable root directory for ROOT, cloning if
551    necessary, allocating in POOL.  ROOT must be a transaction root.
552    Use ERROR_PATH in error messages.  */
553 static svn_error_t *
554 mutable_root_node(dag_node_t **node_p,
555                   svn_fs_root_t *root,
556                   const char *error_path,
557                   apr_pool_t *pool)
558 {
559   if (root->is_txn_root)
560     return svn_fs_fs__dag_clone_root(node_p, root->fs, root->txn, pool);
561   else
562     /* If it's not a transaction root, we can't change its contents.  */
563     return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path);
564 }
565
566
567 \f
568 /* Traversing directory paths.  */
569
570 typedef enum copy_id_inherit_t
571 {
572   copy_id_inherit_unknown = 0,
573   copy_id_inherit_self,
574   copy_id_inherit_parent,
575   copy_id_inherit_new
576
577 } copy_id_inherit_t;
578
579 /* A linked list representing the path from a node up to a root
580    directory.  We use this for cloning, and for operations that need
581    to deal with both a node and its parent directory.  For example, a
582    `delete' operation needs to know that the node actually exists, but
583    also needs to change the parent directory.  */
584 typedef struct parent_path_t
585 {
586
587   /* A node along the path.  This could be the final node, one of its
588      parents, or the root.  Every parent path ends with an element for
589      the root directory.  */
590   dag_node_t *node;
591
592   /* The name NODE has in its parent directory.  This is zero for the
593      root directory, which (obviously) has no name in its parent.  */
594   char *entry;
595
596   /* The parent of NODE, or zero if NODE is the root directory.  */
597   struct parent_path_t *parent;
598
599   /* The copy ID inheritance style. */
600   copy_id_inherit_t copy_inherit;
601
602   /* If copy ID inheritance style is copy_id_inherit_new, this is the
603      path which should be implicitly copied; otherwise, this is NULL. */
604   const char *copy_src_path;
605
606 } parent_path_t;
607
608 /* Return a text string describing the absolute path of parent_path
609    PARENT_PATH.  It will be allocated in POOL. */
610 static const char *
611 parent_path_path(parent_path_t *parent_path,
612                  apr_pool_t *pool)
613 {
614   const char *path_so_far = "/";
615   if (parent_path->parent)
616     path_so_far = parent_path_path(parent_path->parent, pool);
617   return parent_path->entry
618     ? svn_fspath__join(path_so_far, parent_path->entry, pool)
619     : path_so_far;
620 }
621
622
623 /* Return the FS path for the parent path chain object CHILD relative
624    to its ANCESTOR in the same chain, allocated in POOL.  */
625 static const char *
626 parent_path_relpath(parent_path_t *child,
627                     parent_path_t *ancestor,
628                     apr_pool_t *pool)
629 {
630   const char *path_so_far = "";
631   parent_path_t *this_node = child;
632   while (this_node != ancestor)
633     {
634       assert(this_node != NULL);
635       path_so_far = svn_relpath_join(this_node->entry, path_so_far, pool);
636       this_node = this_node->parent;
637     }
638   return path_so_far;
639 }
640
641
642
643 /* Choose a copy ID inheritance method *INHERIT_P to be used in the
644    event that immutable node CHILD in FS needs to be made mutable.  If
645    the inheritance method is copy_id_inherit_new, also return a
646    *COPY_SRC_PATH on which to base the new copy ID (else return NULL
647    for that path).  CHILD must have a parent (it cannot be the root
648    node).  TXN_ID is the transaction in which these items might be
649    mutable.  Allocations are taken from POOL. */
650 static svn_error_t *
651 get_copy_inheritance(copy_id_inherit_t *inherit_p,
652                      const char **copy_src_path,
653                      svn_fs_t *fs,
654                      parent_path_t *child,
655                      const char *txn_id,
656                      apr_pool_t *pool)
657 {
658   const svn_fs_id_t *child_id, *parent_id, *copyroot_id;
659   const char *child_copy_id, *parent_copy_id;
660   const char *id_path = NULL;
661   svn_fs_root_t *copyroot_root;
662   dag_node_t *copyroot_node;
663   svn_revnum_t copyroot_rev;
664   const char *copyroot_path;
665
666   SVN_ERR_ASSERT(child && child->parent && txn_id);
667
668   /* Initialize some convenience variables. */
669   child_id = svn_fs_fs__dag_get_id(child->node);
670   parent_id = svn_fs_fs__dag_get_id(child->parent->node);
671   child_copy_id = svn_fs_fs__id_copy_id(child_id);
672   parent_copy_id = svn_fs_fs__id_copy_id(parent_id);
673
674   /* If this child is already mutable, we have nothing to do. */
675   if (svn_fs_fs__id_txn_id(child_id))
676     {
677       *inherit_p = copy_id_inherit_self;
678       *copy_src_path = NULL;
679       return SVN_NO_ERROR;
680     }
681
682   /* From this point on, we'll assume that the child will just take
683      its copy ID from its parent. */
684   *inherit_p = copy_id_inherit_parent;
685   *copy_src_path = NULL;
686
687   /* Special case: if the child's copy ID is '0', use the parent's
688      copy ID. */
689   if (strcmp(child_copy_id, "0") == 0)
690     return SVN_NO_ERROR;
691
692   /* Compare the copy IDs of the child and its parent.  If they are
693      the same, then the child is already on the same branch as the
694      parent, and should use the same mutability copy ID that the
695      parent will use. */
696   if (svn_fs_fs__key_compare(child_copy_id, parent_copy_id) == 0)
697     return SVN_NO_ERROR;
698
699   /* If the child is on the same branch that the parent is on, the
700      child should just use the same copy ID that the parent would use.
701      Else, the child needs to generate a new copy ID to use should it
702      need to be made mutable.  We will claim that child is on the same
703      branch as its parent if the child itself is not a branch point,
704      or if it is a branch point that we are accessing via its original
705      copy destination path. */
706   SVN_ERR(svn_fs_fs__dag_get_copyroot(&copyroot_rev, &copyroot_path,
707                                       child->node));
708   SVN_ERR(svn_fs_fs__revision_root(&copyroot_root, fs, copyroot_rev, pool));
709   SVN_ERR(get_dag(&copyroot_node, copyroot_root, copyroot_path, pool));
710   copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
711
712   if (svn_fs_fs__id_compare(copyroot_id, child_id) == -1)
713     return SVN_NO_ERROR;
714
715   /* Determine if we are looking at the child via its original path or
716      as a subtree item of a copied tree. */
717   id_path = svn_fs_fs__dag_get_created_path(child->node);
718   if (strcmp(id_path, parent_path_path(child, pool)) == 0)
719     {
720       *inherit_p = copy_id_inherit_self;
721       return SVN_NO_ERROR;
722     }
723
724   /* We are pretty sure that the child node is an unedited nested
725      branched node.  When it needs to be made mutable, it should claim
726      a new copy ID. */
727   *inherit_p = copy_id_inherit_new;
728   *copy_src_path = id_path;
729   return SVN_NO_ERROR;
730 }
731
732 /* Allocate a new parent_path_t node from POOL, referring to NODE,
733    ENTRY, PARENT, and COPY_ID.  */
734 static parent_path_t *
735 make_parent_path(dag_node_t *node,
736                  char *entry,
737                  parent_path_t *parent,
738                  apr_pool_t *pool)
739 {
740   parent_path_t *parent_path = apr_pcalloc(pool, sizeof(*parent_path));
741   parent_path->node = node;
742   parent_path->entry = entry;
743   parent_path->parent = parent;
744   parent_path->copy_inherit = copy_id_inherit_unknown;
745   parent_path->copy_src_path = NULL;
746   return parent_path;
747 }
748
749
750 /* Flags for open_path.  */
751 typedef enum open_path_flags_t {
752
753   /* The last component of the PATH need not exist.  (All parent
754      directories must exist, as usual.)  If the last component doesn't
755      exist, simply leave the `node' member of the bottom parent_path
756      component zero.  */
757   open_path_last_optional = 1,
758
759   /* When this flag is set, don't bother to lookup the DAG node in
760      our caches because we already tried this.  Ignoring this flag
761      has no functional impact.  */
762   open_path_uncached = 2,
763
764   /* The caller does not care about the parent node chain but only
765      the final DAG node. */
766   open_path_node_only = 4
767 } open_path_flags_t;
768
769
770 /* Open the node identified by PATH in ROOT, allocating in POOL.  Set
771    *PARENT_PATH_P to a path from the node up to ROOT.  The resulting
772    **PARENT_PATH_P value is guaranteed to contain at least one
773    *element, for the root directory.  PATH must be in canonical form.
774
775    If resulting *PARENT_PATH_P will eventually be made mutable and
776    modified, or if copy ID inheritance information is otherwise
777    needed, TXN_ID should be the ID of the mutability transaction.  If
778    TXN_ID is NULL, no copy ID inheritance information will be
779    calculated for the *PARENT_PATH_P chain.
780
781    If FLAGS & open_path_last_optional is zero, return the error
782    SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist.  If
783    non-zero, require all the parent directories to exist as normal,
784    but if the final path component doesn't exist, simply return a path
785    whose bottom `node' member is zero.  This option is useful for
786    callers that create new nodes --- we find the parent directory for
787    them, and tell them whether the entry exists already.
788
789    The remaining bits in FLAGS are hints that allow this function
790    to take shortcuts based on knowledge that the caller provides,
791    such as the caller is not actually being interested in PARENT_PATH_P,
792    but only in (*PARENT_PATH_P)->NODE.
793
794    NOTE: Public interfaces which only *read* from the filesystem
795    should not call this function directly, but should instead use
796    get_dag().
797 */
798 static svn_error_t *
799 open_path(parent_path_t **parent_path_p,
800           svn_fs_root_t *root,
801           const char *path,
802           int flags,
803           const char *txn_id,
804           apr_pool_t *pool)
805 {
806   svn_fs_t *fs = root->fs;
807   dag_node_t *here = NULL; /* The directory we're currently looking at.  */
808   parent_path_t *parent_path; /* The path from HERE up to the root. */
809   const char *rest; /* The portion of PATH we haven't traversed yet.  */
810
811   /* ensure a canonical path representation */
812   const char *path_so_far = "/";
813   apr_pool_t *iterpool = svn_pool_create(pool);
814
815   /* callers often traverse the tree in some path-based order.  That means
816      a sibling of PATH has been presently accessed.  Try to start the lookup
817      directly at the parent node, if the caller did not requested the full
818      parent chain. */
819   const char *directory;
820   assert(svn_fs__is_canonical_abspath(path));
821   if (flags & open_path_node_only)
822     {
823       directory = svn_dirent_dirname(path, pool);
824       if (directory[1] != 0) /* root nodes are covered anyway */
825         SVN_ERR(dag_node_cache_get(&here, root, directory, pool));
826     }
827
828   /* did the shortcut work? */
829   if (here)
830     {
831       path_so_far = directory;
832       rest = path + strlen(directory) + 1;
833     }
834   else
835     {
836       /* Make a parent_path item for the root node, using its own current
837          copy id.  */
838       SVN_ERR(root_node(&here, root, pool));
839       rest = path + 1; /* skip the leading '/', it saves in iteration */
840     }
841
842   parent_path = make_parent_path(here, 0, 0, pool);
843   parent_path->copy_inherit = copy_id_inherit_self;
844
845   /* Whenever we are at the top of this loop:
846      - HERE is our current directory,
847      - ID is the node revision ID of HERE,
848      - REST is the path we're going to find in HERE, and
849      - PARENT_PATH includes HERE and all its parents.  */
850   for (;;)
851     {
852       const char *next;
853       char *entry;
854       dag_node_t *child;
855
856       svn_pool_clear(iterpool);
857
858       /* Parse out the next entry from the path.  */
859       entry = svn_fs__next_entry_name(&next, rest, pool);
860
861       /* Calculate the path traversed thus far. */
862       path_so_far = svn_fspath__join(path_so_far, entry, pool);
863
864       if (*entry == '\0')
865         {
866           /* Given the behavior of svn_fs__next_entry_name(), this
867              happens when the path either starts or ends with a slash.
868              In either case, we stay put: the current directory stays
869              the same, and we add nothing to the parent path. */
870           child = here;
871         }
872       else
873         {
874           copy_id_inherit_t inherit;
875           const char *copy_path = NULL;
876           svn_error_t *err = SVN_NO_ERROR;
877           dag_node_t *cached_node = NULL;
878
879           /* If we found a directory entry, follow it.  First, we
880              check our node cache, and, failing that, we hit the DAG
881              layer.  Don't bother to contact the cache for the last
882              element if we already know the lookup to fail for the
883              complete path. */
884           if (next || !(flags & open_path_uncached))
885             SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far, pool));
886
887           if (cached_node)
888             child = cached_node;
889           else
890             err = svn_fs_fs__dag_open(&child, here, entry, pool, iterpool);
891
892           /* "file not found" requires special handling.  */
893           if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
894             {
895               /* If this was the last path component, and the caller
896                  said it was optional, then don't return an error;
897                  just put a NULL node pointer in the path.  */
898
899               svn_error_clear(err);
900
901               if ((flags & open_path_last_optional)
902                   && (! next || *next == '\0'))
903                 {
904                   parent_path = make_parent_path(NULL, entry, parent_path,
905                                                  pool);
906                   break;
907                 }
908               else
909                 {
910                   /* Build a better error message than svn_fs_fs__dag_open
911                      can provide, giving the root and full path name.  */
912                   return SVN_FS__NOT_FOUND(root, path);
913                 }
914             }
915
916           /* Other errors we return normally.  */
917           SVN_ERR(err);
918
919           if (flags & open_path_node_only)
920             {
921               /* Shortcut: the caller only wan'ts the final DAG node. */
922               parent_path->node = child;
923             }
924           else
925             {
926               /* Now, make a parent_path item for CHILD. */
927               parent_path = make_parent_path(child, entry, parent_path, pool);
928               if (txn_id)
929                 {
930                   SVN_ERR(get_copy_inheritance(&inherit, &copy_path, fs,
931                                                parent_path, txn_id, iterpool));
932                   parent_path->copy_inherit = inherit;
933                   parent_path->copy_src_path = apr_pstrdup(pool, copy_path);
934                 }
935             }
936
937           /* Cache the node we found (if it wasn't already cached). */
938           if (! cached_node)
939             SVN_ERR(dag_node_cache_set(root, path_so_far, child, iterpool));
940         }
941
942       /* Are we finished traversing the path?  */
943       if (! next)
944         break;
945
946       /* The path isn't finished yet; we'd better be in a directory.  */
947       if (svn_fs_fs__dag_node_kind(child) != svn_node_dir)
948         SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far),
949                   apr_psprintf(iterpool, _("Failure opening '%s'"), path));
950
951       rest = next;
952       here = child;
953     }
954
955   svn_pool_destroy(iterpool);
956   *parent_path_p = parent_path;
957   return SVN_NO_ERROR;
958 }
959
960
961 /* Make the node referred to by PARENT_PATH mutable, if it isn't
962    already, allocating from POOL.  ROOT must be the root from which
963    PARENT_PATH descends.  Clone any parent directories as needed.
964    Adjust the dag nodes in PARENT_PATH to refer to the clones.  Use
965    ERROR_PATH in error messages.  */
966 static svn_error_t *
967 make_path_mutable(svn_fs_root_t *root,
968                   parent_path_t *parent_path,
969                   const char *error_path,
970                   apr_pool_t *pool)
971 {
972   dag_node_t *clone;
973   const char *txn_id = root->txn;
974
975   /* Is the node mutable already?  */
976   if (svn_fs_fs__dag_check_mutable(parent_path->node))
977     return SVN_NO_ERROR;
978
979   /* Are we trying to clone the root, or somebody's child node?  */
980   if (parent_path->parent)
981     {
982       const svn_fs_id_t *parent_id, *child_id, *copyroot_id;
983       const char *copy_id = NULL;
984       copy_id_inherit_t inherit = parent_path->copy_inherit;
985       const char *clone_path, *copyroot_path;
986       svn_revnum_t copyroot_rev;
987       svn_boolean_t is_parent_copyroot = FALSE;
988       svn_fs_root_t *copyroot_root;
989       dag_node_t *copyroot_node;
990
991       /* We're trying to clone somebody's child.  Make sure our parent
992          is mutable.  */
993       SVN_ERR(make_path_mutable(root, parent_path->parent,
994                                 error_path, pool));
995
996       switch (inherit)
997         {
998         case copy_id_inherit_parent:
999           parent_id = svn_fs_fs__dag_get_id(parent_path->parent->node);
1000           copy_id = svn_fs_fs__id_copy_id(parent_id);
1001           break;
1002
1003         case copy_id_inherit_new:
1004           SVN_ERR(svn_fs_fs__reserve_copy_id(&copy_id, root->fs, txn_id,
1005                                              pool));
1006           break;
1007
1008         case copy_id_inherit_self:
1009           copy_id = NULL;
1010           break;
1011
1012         case copy_id_inherit_unknown:
1013         default:
1014           SVN_ERR_MALFUNCTION(); /* uh-oh -- somebody didn't calculate copy-ID
1015                       inheritance data. */
1016         }
1017
1018       /* Determine what copyroot our new child node should use. */
1019       SVN_ERR(svn_fs_fs__dag_get_copyroot(&copyroot_rev, &copyroot_path,
1020                                           parent_path->node));
1021       SVN_ERR(svn_fs_fs__revision_root(&copyroot_root, root->fs,
1022                                        copyroot_rev, pool));
1023       SVN_ERR(get_dag(&copyroot_node, copyroot_root, copyroot_path, pool));
1024
1025       child_id = svn_fs_fs__dag_get_id(parent_path->node);
1026       copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
1027       if (strcmp(svn_fs_fs__id_node_id(child_id),
1028                  svn_fs_fs__id_node_id(copyroot_id)) != 0)
1029         is_parent_copyroot = TRUE;
1030
1031       /* Now make this node mutable.  */
1032       clone_path = parent_path_path(parent_path->parent, pool);
1033       SVN_ERR(svn_fs_fs__dag_clone_child(&clone,
1034                                          parent_path->parent->node,
1035                                          clone_path,
1036                                          parent_path->entry,
1037                                          copy_id, txn_id,
1038                                          is_parent_copyroot,
1039                                          pool));
1040
1041       /* Update the path cache. */
1042       SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool),
1043                                  clone, pool));
1044     }
1045   else
1046     {
1047       /* We're trying to clone the root directory.  */
1048       SVN_ERR(mutable_root_node(&clone, root, error_path, pool));
1049     }
1050
1051   /* Update the PARENT_PATH link to refer to the clone.  */
1052   parent_path->node = clone;
1053
1054   return SVN_NO_ERROR;
1055 }
1056
1057
1058 /* Open the node identified by PATH in ROOT.  Set DAG_NODE_P to the
1059    node we find, allocated in POOL.  Return the error
1060    SVN_ERR_FS_NOT_FOUND if this node doesn't exist. */
1061 static svn_error_t *
1062 get_dag(dag_node_t **dag_node_p,
1063         svn_fs_root_t *root,
1064         const char *path,
1065         apr_pool_t *pool)
1066 {
1067   parent_path_t *parent_path;
1068   dag_node_t *node = NULL;
1069
1070   /* First we look for the DAG in our cache
1071      (if the path may be canonical). */
1072   if (*path == '/')
1073     SVN_ERR(dag_node_cache_get(&node, root, path, pool));
1074
1075   if (! node)
1076     {
1077       /* Canonicalize the input PATH. */
1078       if (! svn_fs__is_canonical_abspath(path))
1079         {
1080           path = svn_fs__canonicalize_abspath(path, pool);
1081
1082           /* Try again with the corrected path. */
1083           SVN_ERR(dag_node_cache_get(&node, root, path, pool));
1084         }
1085
1086       if (! node)
1087         {
1088           /* Call open_path with no flags, as we want this to return an
1089            * error if the node for which we are searching doesn't exist. */
1090           SVN_ERR(open_path(&parent_path, root, path,
1091                             open_path_uncached | open_path_node_only,
1092                             NULL, pool));
1093           node = parent_path->node;
1094
1095           /* No need to cache our find -- open_path() will do that for us. */
1096         }
1097     }
1098
1099   *dag_node_p = node;
1100   return SVN_NO_ERROR;
1101 }
1102
1103
1104 \f
1105 /* Populating the `changes' table. */
1106
1107 /* Add a change to the changes table in FS, keyed on transaction id
1108    TXN_ID, and indicated that a change of kind CHANGE_KIND occurred on
1109    PATH (whose node revision id is--or was, in the case of a
1110    deletion--NODEREV_ID), and optionally that TEXT_MODs or PROP_MODs
1111    occurred.  If the change resulted from a copy, COPYFROM_REV and
1112    COPYFROM_PATH specify under which revision and path the node was
1113    copied from.  If this was not part of a copy, COPYFROM_REV should
1114    be SVN_INVALID_REVNUM.  Do all this as part of POOL.  */
1115 static svn_error_t *
1116 add_change(svn_fs_t *fs,
1117            const char *txn_id,
1118            const char *path,
1119            const svn_fs_id_t *noderev_id,
1120            svn_fs_path_change_kind_t change_kind,
1121            svn_boolean_t text_mod,
1122            svn_boolean_t prop_mod,
1123            svn_node_kind_t node_kind,
1124            svn_revnum_t copyfrom_rev,
1125            const char *copyfrom_path,
1126            apr_pool_t *pool)
1127 {
1128   return svn_fs_fs__add_change(fs, txn_id,
1129                                svn_fs__canonicalize_abspath(path, pool),
1130                                noderev_id, change_kind, text_mod, prop_mod,
1131                                node_kind, copyfrom_rev, copyfrom_path,
1132                                pool);
1133 }
1134
1135
1136 \f
1137 /* Generic node operations.  */
1138
1139 /* Get the id of a node referenced by path PATH in ROOT.  Return the
1140    id in *ID_P allocated in POOL. */
1141 svn_error_t *
1142 svn_fs_fs__node_id(const svn_fs_id_t **id_p,
1143                    svn_fs_root_t *root,
1144                    const char *path,
1145                    apr_pool_t *pool)
1146 {
1147   if ((! root->is_txn_root)
1148       && (path[0] == '\0' || ((path[0] == '/') && (path[1] == '\0'))))
1149     {
1150       /* Optimize the case where we don't need any db access at all.
1151          The root directory ("" or "/") node is stored in the
1152          svn_fs_root_t object, and never changes when it's a revision
1153          root, so we can just reach in and grab it directly. */
1154       fs_rev_root_data_t *frd = root->fsap_data;
1155       *id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(frd->root_dir), pool);
1156     }
1157   else
1158     {
1159       dag_node_t *node;
1160
1161       SVN_ERR(get_dag(&node, root, path, pool));
1162       *id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(node), pool);
1163     }
1164   return SVN_NO_ERROR;
1165 }
1166
1167
1168 svn_error_t *
1169 svn_fs_fs__node_created_rev(svn_revnum_t *revision,
1170                             svn_fs_root_t *root,
1171                             const char *path,
1172                             apr_pool_t *pool)
1173 {
1174   dag_node_t *node;
1175
1176   SVN_ERR(get_dag(&node, root, path, pool));
1177   return svn_fs_fs__dag_get_revision(revision, node, pool);
1178 }
1179
1180
1181 /* Set *CREATED_PATH to the path at which PATH under ROOT was created.
1182    Return a string allocated in POOL. */
1183 static svn_error_t *
1184 fs_node_created_path(const char **created_path,
1185                      svn_fs_root_t *root,
1186                      const char *path,
1187                      apr_pool_t *pool)
1188 {
1189   dag_node_t *node;
1190
1191   SVN_ERR(get_dag(&node, root, path, pool));
1192   *created_path = svn_fs_fs__dag_get_created_path(node);
1193
1194   return SVN_NO_ERROR;
1195 }
1196
1197
1198 /* Set *KIND_P to the type of node located at PATH under ROOT.
1199    Perform temporary allocations in POOL. */
1200 static svn_error_t *
1201 node_kind(svn_node_kind_t *kind_p,
1202           svn_fs_root_t *root,
1203           const char *path,
1204           apr_pool_t *pool)
1205 {
1206   const svn_fs_id_t *node_id;
1207   dag_node_t *node;
1208
1209   /* Get the node id. */
1210   SVN_ERR(svn_fs_fs__node_id(&node_id, root, path, pool));
1211
1212   /* Use the node id to get the real kind. */
1213   SVN_ERR(svn_fs_fs__dag_get_node(&node, root->fs, node_id, pool));
1214   *kind_p = svn_fs_fs__dag_node_kind(node);
1215
1216   return SVN_NO_ERROR;
1217 }
1218
1219
1220 /* Set *KIND_P to the type of node present at PATH under ROOT.  If
1221    PATH does not exist under ROOT, set *KIND_P to svn_node_none.  Use
1222    POOL for temporary allocation. */
1223 svn_error_t *
1224 svn_fs_fs__check_path(svn_node_kind_t *kind_p,
1225                       svn_fs_root_t *root,
1226                       const char *path,
1227                       apr_pool_t *pool)
1228 {
1229   svn_error_t *err = node_kind(kind_p, root, path, pool);
1230   if (err &&
1231       ((err->apr_err == SVN_ERR_FS_NOT_FOUND)
1232        || (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY)))
1233     {
1234       svn_error_clear(err);
1235       err = SVN_NO_ERROR;
1236       *kind_p = svn_node_none;
1237     }
1238
1239   return svn_error_trace(err);
1240 }
1241
1242 /* Set *VALUE_P to the value of the property named PROPNAME of PATH in
1243    ROOT.  If the node has no property by that name, set *VALUE_P to
1244    zero.  Allocate the result in POOL. */
1245 static svn_error_t *
1246 fs_node_prop(svn_string_t **value_p,
1247              svn_fs_root_t *root,
1248              const char *path,
1249              const char *propname,
1250              apr_pool_t *pool)
1251 {
1252   dag_node_t *node;
1253   apr_hash_t *proplist;
1254
1255   SVN_ERR(get_dag(&node, root, path, pool));
1256   SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, node, pool));
1257   *value_p = NULL;
1258   if (proplist)
1259     *value_p = svn_hash_gets(proplist, propname);
1260
1261   return SVN_NO_ERROR;
1262 }
1263
1264
1265 /* Set *TABLE_P to the entire property list of PATH under ROOT, as an
1266    APR hash table allocated in POOL.  The resulting property table
1267    maps property names to pointers to svn_string_t objects containing
1268    the property value. */
1269 static svn_error_t *
1270 fs_node_proplist(apr_hash_t **table_p,
1271                  svn_fs_root_t *root,
1272                  const char *path,
1273                  apr_pool_t *pool)
1274 {
1275   apr_hash_t *table;
1276   dag_node_t *node;
1277
1278   SVN_ERR(get_dag(&node, root, path, pool));
1279   SVN_ERR(svn_fs_fs__dag_get_proplist(&table, node, pool));
1280   *table_p = table ? table : apr_hash_make(pool);
1281
1282   return SVN_NO_ERROR;
1283 }
1284
1285
1286 static svn_error_t *
1287 increment_mergeinfo_up_tree(parent_path_t *pp,
1288                             apr_int64_t increment,
1289                             apr_pool_t *pool)
1290 {
1291   for (; pp; pp = pp->parent)
1292     SVN_ERR(svn_fs_fs__dag_increment_mergeinfo_count(pp->node,
1293                                                      increment,
1294                                                      pool));
1295
1296   return SVN_NO_ERROR;
1297 }
1298
1299 /* Change, add, or delete a node's property value.  The affected node
1300    is PATH under ROOT, the property value to modify is NAME, and VALUE
1301    points to either a string value to set the new contents to, or NULL
1302    if the property should be deleted.  Perform temporary allocations
1303    in POOL. */
1304 static svn_error_t *
1305 fs_change_node_prop(svn_fs_root_t *root,
1306                     const char *path,
1307                     const char *name,
1308                     const svn_string_t *value,
1309                     apr_pool_t *pool)
1310 {
1311   parent_path_t *parent_path;
1312   apr_hash_t *proplist;
1313   const char *txn_id;
1314
1315   if (! root->is_txn_root)
1316     return SVN_FS__NOT_TXN(root);
1317   txn_id = root->txn;
1318
1319   path = svn_fs__canonicalize_abspath(path, pool);
1320   SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, pool));
1321
1322   /* Check (non-recursively) to see if path is locked; if so, check
1323      that we can use it. */
1324   if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
1325     SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, FALSE, FALSE,
1326                                               pool));
1327
1328   SVN_ERR(make_path_mutable(root, parent_path, path, pool));
1329   SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, parent_path->node, pool));
1330
1331   /* If there's no proplist, but we're just deleting a property, exit now. */
1332   if ((! proplist) && (! value))
1333     return SVN_NO_ERROR;
1334
1335   /* Now, if there's no proplist, we know we need to make one. */
1336   if (! proplist)
1337     proplist = apr_hash_make(pool);
1338
1339   if (svn_fs_fs__fs_supports_mergeinfo(root->fs)
1340       && strcmp (name, SVN_PROP_MERGEINFO) == 0)
1341     {
1342       apr_int64_t increment = 0;
1343       svn_boolean_t had_mergeinfo;
1344       SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&had_mergeinfo, parent_path->node));
1345
1346       if (value && !had_mergeinfo)
1347         increment = 1;
1348       else if (!value && had_mergeinfo)
1349         increment = -1;
1350
1351       if (increment != 0)
1352         {
1353           SVN_ERR(increment_mergeinfo_up_tree(parent_path, increment, pool));
1354           SVN_ERR(svn_fs_fs__dag_set_has_mergeinfo(parent_path->node,
1355                                                    (value != NULL), pool));
1356         }
1357     }
1358
1359   /* Set the property. */
1360   svn_hash_sets(proplist, name, value);
1361
1362   /* Overwrite the node's proplist. */
1363   SVN_ERR(svn_fs_fs__dag_set_proplist(parent_path->node, proplist,
1364                                       pool));
1365
1366   /* Make a record of this modification in the changes table. */
1367   return add_change(root->fs, txn_id, path,
1368                     svn_fs_fs__dag_get_id(parent_path->node),
1369                     svn_fs_path_change_modify, FALSE, TRUE,
1370                     svn_fs_fs__dag_node_kind(parent_path->node),
1371                     SVN_INVALID_REVNUM, NULL, pool);
1372 }
1373
1374
1375 /* Determine if the properties of two path/root combinations are
1376    different.  Set *CHANGED_P to TRUE if the properties at PATH1 under
1377    ROOT1 differ from those at PATH2 under ROOT2, or FALSE otherwise.
1378    Both roots must be in the same filesystem. */
1379 static svn_error_t *
1380 fs_props_changed(svn_boolean_t *changed_p,
1381                  svn_fs_root_t *root1,
1382                  const char *path1,
1383                  svn_fs_root_t *root2,
1384                  const char *path2,
1385                  apr_pool_t *pool)
1386 {
1387   dag_node_t *node1, *node2;
1388
1389   /* Check that roots are in the same fs. */
1390   if (root1->fs != root2->fs)
1391     return svn_error_create
1392       (SVN_ERR_FS_GENERAL, NULL,
1393        _("Cannot compare property value between two different filesystems"));
1394
1395   SVN_ERR(get_dag(&node1, root1, path1, pool));
1396   SVN_ERR(get_dag(&node2, root2, path2, pool));
1397   return svn_fs_fs__dag_things_different(changed_p, NULL,
1398                                          node1, node2);
1399 }
1400
1401
1402 \f
1403 /* Merges and commits. */
1404
1405 /* Set *NODE to the root node of ROOT.  */
1406 static svn_error_t *
1407 get_root(dag_node_t **node, svn_fs_root_t *root, apr_pool_t *pool)
1408 {
1409   return get_dag(node, root, "/", pool);
1410 }
1411
1412
1413 /* Set the contents of CONFLICT_PATH to PATH, and return an
1414    SVN_ERR_FS_CONFLICT error that indicates that there was a conflict
1415    at PATH.  Perform all allocations in POOL (except the allocation of
1416    CONFLICT_PATH, which should be handled outside this function).  */
1417 static svn_error_t *
1418 conflict_err(svn_stringbuf_t *conflict_path,
1419              const char *path)
1420 {
1421   svn_stringbuf_set(conflict_path, path);
1422   return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL,
1423                            _("Conflict at '%s'"), path);
1424 }
1425
1426
1427 /* Merge changes between ANCESTOR and SOURCE into TARGET.  ANCESTOR
1428  * and TARGET must be distinct node revisions.  TARGET_PATH should
1429  * correspond to TARGET's full path in its filesystem, and is used for
1430  * reporting conflict location.
1431  *
1432  * SOURCE, TARGET, and ANCESTOR are generally directories; this
1433  * function recursively merges the directories' contents.  If any are
1434  * files, this function simply returns an error whenever SOURCE,
1435  * TARGET, and ANCESTOR are all distinct node revisions.
1436  *
1437  * If there are differences between ANCESTOR and SOURCE that conflict
1438  * with changes between ANCESTOR and TARGET, this function returns an
1439  * SVN_ERR_FS_CONFLICT error, and updates CONFLICT_P to the name of the
1440  * conflicting node in TARGET, with TARGET_PATH prepended as a path.
1441  *
1442  * If there are no conflicting differences, CONFLICT_P is updated to
1443  * the empty string.
1444  *
1445  * CONFLICT_P must point to a valid svn_stringbuf_t.
1446  *
1447  * Do any necessary temporary allocation in POOL.
1448  */
1449 static svn_error_t *
1450 merge(svn_stringbuf_t *conflict_p,
1451       const char *target_path,
1452       dag_node_t *target,
1453       dag_node_t *source,
1454       dag_node_t *ancestor,
1455       const char *txn_id,
1456       apr_int64_t *mergeinfo_increment_out,
1457       apr_pool_t *pool)
1458 {
1459   const svn_fs_id_t *source_id, *target_id, *ancestor_id;
1460   apr_hash_t *s_entries, *t_entries, *a_entries;
1461   apr_hash_index_t *hi;
1462   svn_fs_t *fs;
1463   apr_pool_t *iterpool;
1464   apr_int64_t mergeinfo_increment = 0;
1465   svn_boolean_t fs_supports_mergeinfo;
1466
1467   /* Make sure everyone comes from the same filesystem. */
1468   fs = svn_fs_fs__dag_get_fs(ancestor);
1469   if ((fs != svn_fs_fs__dag_get_fs(source))
1470       || (fs != svn_fs_fs__dag_get_fs(target)))
1471     {
1472       return svn_error_create
1473         (SVN_ERR_FS_CORRUPT, NULL,
1474          _("Bad merge; ancestor, source, and target not all in same fs"));
1475     }
1476
1477   /* We have the same fs, now check it. */
1478   SVN_ERR(svn_fs__check_fs(fs, TRUE));
1479
1480   source_id   = svn_fs_fs__dag_get_id(source);
1481   target_id   = svn_fs_fs__dag_get_id(target);
1482   ancestor_id = svn_fs_fs__dag_get_id(ancestor);
1483
1484   /* It's improper to call this function with ancestor == target. */
1485   if (svn_fs_fs__id_eq(ancestor_id, target_id))
1486     {
1487       svn_string_t *id_str = svn_fs_fs__id_unparse(target_id, pool);
1488       return svn_error_createf
1489         (SVN_ERR_FS_GENERAL, NULL,
1490          _("Bad merge; target '%s' has id '%s', same as ancestor"),
1491          target_path, id_str->data);
1492     }
1493
1494   svn_stringbuf_setempty(conflict_p);
1495
1496   /* Base cases:
1497    * Either no change made in source, or same change as made in target.
1498    * Both mean nothing to merge here.
1499    */
1500   if (svn_fs_fs__id_eq(ancestor_id, source_id)
1501       || (svn_fs_fs__id_eq(source_id, target_id)))
1502     return SVN_NO_ERROR;
1503
1504   /* Else proceed, knowing all three are distinct node revisions.
1505    *
1506    * How to merge from this point:
1507    *
1508    * if (not all 3 are directories)
1509    *   {
1510    *     early exit with conflict;
1511    *   }
1512    *
1513    * // Property changes may only be made to up-to-date
1514    * // directories, because once the client commits the prop
1515    * // change, it bumps the directory's revision, and therefore
1516    * // must be able to depend on there being no other changes to
1517    * // that directory in the repository.
1518    * if (target's property list differs from ancestor's)
1519    *    conflict;
1520    *
1521    * For each entry NAME in the directory ANCESTOR:
1522    *
1523    *   Let ANCESTOR-ENTRY, SOURCE-ENTRY, and TARGET-ENTRY be the IDs of
1524    *   the name within ANCESTOR, SOURCE, and TARGET respectively.
1525    *   (Possibly null if NAME does not exist in SOURCE or TARGET.)
1526    *
1527    *   If ANCESTOR-ENTRY == SOURCE-ENTRY, then:
1528    *     No changes were made to this entry while the transaction was in
1529    *     progress, so do nothing to the target.
1530    *
1531    *   Else if ANCESTOR-ENTRY == TARGET-ENTRY, then:
1532    *     A change was made to this entry while the transaction was in
1533    *     process, but the transaction did not touch this entry.  Replace
1534    *     TARGET-ENTRY with SOURCE-ENTRY.
1535    *
1536    *   Else:
1537    *     Changes were made to this entry both within the transaction and
1538    *     to the repository while the transaction was in progress.  They
1539    *     must be merged or declared to be in conflict.
1540    *
1541    *     If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
1542    *     double delete; flag a conflict.
1543    *
1544    *     If any of the three entries is of type file, declare a conflict.
1545    *
1546    *     If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
1547    *     modification of ANCESTOR-ENTRY (determine by comparing the
1548    *     node-id fields), declare a conflict.  A replacement is
1549    *     incompatible with a modification or other replacement--even
1550    *     an identical replacement.
1551    *
1552    *     Direct modifications were made to the directory ANCESTOR-ENTRY
1553    *     in both SOURCE and TARGET.  Recursively merge these
1554    *     modifications.
1555    *
1556    * For each leftover entry NAME in the directory SOURCE:
1557    *
1558    *   If NAME exists in TARGET, declare a conflict.  Even if SOURCE and
1559    *   TARGET are adding exactly the same thing, two additions are not
1560    *   auto-mergeable with each other.
1561    *
1562    *   Add NAME to TARGET with the entry from SOURCE.
1563    *
1564    * Now that we are done merging the changes from SOURCE into the
1565    * directory TARGET, update TARGET's predecessor to be SOURCE.
1566    */
1567
1568   if ((svn_fs_fs__dag_node_kind(source) != svn_node_dir)
1569       || (svn_fs_fs__dag_node_kind(target) != svn_node_dir)
1570       || (svn_fs_fs__dag_node_kind(ancestor) != svn_node_dir))
1571     {
1572       return conflict_err(conflict_p, target_path);
1573     }
1574
1575
1576   /* Possible early merge failure: if target and ancestor have
1577      different property lists, then the merge should fail.
1578      Propchanges can *only* be committed on an up-to-date directory.
1579      ### TODO: see issue #418 about the inelegance of this.
1580
1581      Another possible, similar, early merge failure: if source and
1582      ancestor have different property lists (meaning someone else
1583      changed directory properties while our commit transaction was
1584      happening), the merge should fail.  See issue #2751.
1585   */
1586   {
1587     node_revision_t *tgt_nr, *anc_nr, *src_nr;
1588
1589     /* Get node revisions for our id's. */
1590     SVN_ERR(svn_fs_fs__get_node_revision(&tgt_nr, fs, target_id, pool));
1591     SVN_ERR(svn_fs_fs__get_node_revision(&anc_nr, fs, ancestor_id, pool));
1592     SVN_ERR(svn_fs_fs__get_node_revision(&src_nr, fs, source_id, pool));
1593
1594     /* Now compare the prop-keys of the skels.  Note that just because
1595        the keys are different -doesn't- mean the proplists have
1596        different contents.  But merge() isn't concerned with contents;
1597        it doesn't do a brute-force comparison on textual contents, so
1598        it won't do that here either.  Checking to see if the propkey
1599        atoms are `equal' is enough. */
1600     if (! svn_fs_fs__noderev_same_rep_key(tgt_nr->prop_rep, anc_nr->prop_rep))
1601       return conflict_err(conflict_p, target_path);
1602     if (! svn_fs_fs__noderev_same_rep_key(src_nr->prop_rep, anc_nr->prop_rep))
1603       return conflict_err(conflict_p, target_path);
1604   }
1605
1606   /* ### todo: it would be more efficient to simply check for a NULL
1607      entries hash where necessary below than to allocate an empty hash
1608      here, but another day, another day... */
1609   SVN_ERR(svn_fs_fs__dag_dir_entries(&s_entries, source, pool));
1610   SVN_ERR(svn_fs_fs__dag_dir_entries(&t_entries, target, pool));
1611   SVN_ERR(svn_fs_fs__dag_dir_entries(&a_entries, ancestor, pool));
1612
1613   fs_supports_mergeinfo = svn_fs_fs__fs_supports_mergeinfo(fs);
1614
1615   /* for each entry E in a_entries... */
1616   iterpool = svn_pool_create(pool);
1617   for (hi = apr_hash_first(pool, a_entries);
1618        hi;
1619        hi = apr_hash_next(hi))
1620     {
1621       svn_fs_dirent_t *s_entry, *t_entry, *a_entry;
1622       const char *name;
1623       apr_ssize_t klen;
1624
1625       svn_pool_clear(iterpool);
1626
1627       name = svn__apr_hash_index_key(hi);
1628       klen = svn__apr_hash_index_klen(hi);
1629       a_entry = svn__apr_hash_index_val(hi);
1630
1631       s_entry = apr_hash_get(s_entries, name, klen);
1632       t_entry = apr_hash_get(t_entries, name, klen);
1633
1634       /* No changes were made to this entry while the transaction was
1635          in progress, so do nothing to the target. */
1636       if (s_entry && svn_fs_fs__id_eq(a_entry->id, s_entry->id))
1637         goto end;
1638
1639       /* A change was made to this entry while the transaction was in
1640          process, but the transaction did not touch this entry. */
1641       else if (t_entry && svn_fs_fs__id_eq(a_entry->id, t_entry->id))
1642         {
1643           dag_node_t *t_ent_node;
1644           SVN_ERR(svn_fs_fs__dag_get_node(&t_ent_node, fs,
1645                                           t_entry->id, iterpool));
1646           if (fs_supports_mergeinfo)
1647             {
1648               apr_int64_t mergeinfo_start;
1649               SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start,
1650                                                          t_ent_node));
1651               mergeinfo_increment -= mergeinfo_start;
1652             }
1653
1654           if (s_entry)
1655             {
1656               dag_node_t *s_ent_node;
1657               SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
1658                                               s_entry->id, iterpool));
1659
1660               if (fs_supports_mergeinfo)
1661                 {
1662                   apr_int64_t mergeinfo_end;
1663                   SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end,
1664                                                              s_ent_node));
1665                   mergeinfo_increment += mergeinfo_end;
1666                 }
1667
1668               SVN_ERR(svn_fs_fs__dag_set_entry(target, name,
1669                                                s_entry->id,
1670                                                s_entry->kind,
1671                                                txn_id,
1672                                                iterpool));
1673             }
1674           else
1675             {
1676               SVN_ERR(svn_fs_fs__dag_delete(target, name, txn_id, iterpool));
1677             }
1678         }
1679
1680       /* Changes were made to this entry both within the transaction
1681          and to the repository while the transaction was in progress.
1682          They must be merged or declared to be in conflict. */
1683       else
1684         {
1685           dag_node_t *s_ent_node, *t_ent_node, *a_ent_node;
1686           const char *new_tpath;
1687           apr_int64_t sub_mergeinfo_increment;
1688
1689           /* If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
1690              double delete; if one of them is null, that's a delete versus
1691              a modification. In any of these cases, flag a conflict. */
1692           if (s_entry == NULL || t_entry == NULL)
1693             return conflict_err(conflict_p,
1694                                 svn_fspath__join(target_path,
1695                                                  a_entry->name,
1696                                                  iterpool));
1697
1698           /* If any of the three entries is of type file, flag a conflict. */
1699           if (s_entry->kind == svn_node_file
1700               || t_entry->kind == svn_node_file
1701               || a_entry->kind == svn_node_file)
1702             return conflict_err(conflict_p,
1703                                 svn_fspath__join(target_path,
1704                                                  a_entry->name,
1705                                                  iterpool));
1706
1707           /* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
1708              modification of ANCESTOR-ENTRY, declare a conflict. */
1709           if (strcmp(svn_fs_fs__id_node_id(s_entry->id),
1710                      svn_fs_fs__id_node_id(a_entry->id)) != 0
1711               || strcmp(svn_fs_fs__id_copy_id(s_entry->id),
1712                         svn_fs_fs__id_copy_id(a_entry->id)) != 0
1713               || strcmp(svn_fs_fs__id_node_id(t_entry->id),
1714                         svn_fs_fs__id_node_id(a_entry->id)) != 0
1715               || strcmp(svn_fs_fs__id_copy_id(t_entry->id),
1716                         svn_fs_fs__id_copy_id(a_entry->id)) != 0)
1717             return conflict_err(conflict_p,
1718                                 svn_fspath__join(target_path,
1719                                                  a_entry->name,
1720                                                  iterpool));
1721
1722           /* Direct modifications were made to the directory
1723              ANCESTOR-ENTRY in both SOURCE and TARGET.  Recursively
1724              merge these modifications. */
1725           SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
1726                                           s_entry->id, iterpool));
1727           SVN_ERR(svn_fs_fs__dag_get_node(&t_ent_node, fs,
1728                                           t_entry->id, iterpool));
1729           SVN_ERR(svn_fs_fs__dag_get_node(&a_ent_node, fs,
1730                                           a_entry->id, iterpool));
1731           new_tpath = svn_fspath__join(target_path, t_entry->name, iterpool);
1732           SVN_ERR(merge(conflict_p, new_tpath,
1733                         t_ent_node, s_ent_node, a_ent_node,
1734                         txn_id,
1735                         &sub_mergeinfo_increment,
1736                         iterpool));
1737           if (fs_supports_mergeinfo)
1738             mergeinfo_increment += sub_mergeinfo_increment;
1739         }
1740
1741       /* We've taken care of any possible implications E could have.
1742          Remove it from source_entries, so it's easy later to loop
1743          over all the source entries that didn't exist in
1744          ancestor_entries. */
1745     end:
1746       apr_hash_set(s_entries, name, klen, NULL);
1747     }
1748
1749   /* For each entry E in source but not in ancestor */
1750   for (hi = apr_hash_first(pool, s_entries);
1751        hi;
1752        hi = apr_hash_next(hi))
1753     {
1754       svn_fs_dirent_t *s_entry, *t_entry;
1755       const char *name = svn__apr_hash_index_key(hi);
1756       apr_ssize_t klen = svn__apr_hash_index_klen(hi);
1757       dag_node_t *s_ent_node;
1758
1759       svn_pool_clear(iterpool);
1760
1761       s_entry = svn__apr_hash_index_val(hi);
1762       t_entry = apr_hash_get(t_entries, name, klen);
1763
1764       /* If NAME exists in TARGET, declare a conflict. */
1765       if (t_entry)
1766         return conflict_err(conflict_p,
1767                             svn_fspath__join(target_path,
1768                                              t_entry->name,
1769                                              iterpool));
1770
1771       SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
1772                                       s_entry->id, iterpool));
1773       if (fs_supports_mergeinfo)
1774         {
1775           apr_int64_t mergeinfo_s;
1776           SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_s,
1777                                                      s_ent_node));
1778           mergeinfo_increment += mergeinfo_s;
1779         }
1780
1781       SVN_ERR(svn_fs_fs__dag_set_entry
1782               (target, s_entry->name, s_entry->id, s_entry->kind,
1783                txn_id, iterpool));
1784     }
1785   svn_pool_destroy(iterpool);
1786
1787   SVN_ERR(svn_fs_fs__dag_update_ancestry(target, source, pool));
1788
1789   if (fs_supports_mergeinfo)
1790     SVN_ERR(svn_fs_fs__dag_increment_mergeinfo_count(target,
1791                                                      mergeinfo_increment,
1792                                                      pool));
1793
1794   if (mergeinfo_increment_out)
1795     *mergeinfo_increment_out = mergeinfo_increment;
1796
1797   return SVN_NO_ERROR;
1798 }
1799
1800 /* Merge changes between an ancestor and SOURCE_NODE into
1801    TXN.  The ancestor is either ANCESTOR_NODE, or if
1802    that is null, TXN's base node.
1803
1804    If the merge is successful, TXN's base will become
1805    SOURCE_NODE, and its root node will have a new ID, a
1806    successor of SOURCE_NODE.
1807
1808    If a conflict results, update *CONFLICT to the path in the txn that
1809    conflicted; see the CONFLICT_P parameter of merge() for details. */
1810 static svn_error_t *
1811 merge_changes(dag_node_t *ancestor_node,
1812               dag_node_t *source_node,
1813               svn_fs_txn_t *txn,
1814               svn_stringbuf_t *conflict,
1815               apr_pool_t *pool)
1816 {
1817   dag_node_t *txn_root_node;
1818   svn_fs_t *fs = txn->fs;
1819   const char *txn_id = txn->id;
1820
1821   SVN_ERR(svn_fs_fs__dag_txn_root(&txn_root_node, fs, txn_id, pool));
1822
1823   if (ancestor_node == NULL)
1824     {
1825       SVN_ERR(svn_fs_fs__dag_txn_base_root(&ancestor_node, fs,
1826                                            txn_id, pool));
1827     }
1828
1829   if (svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(ancestor_node),
1830                        svn_fs_fs__dag_get_id(txn_root_node)))
1831     {
1832       /* If no changes have been made in TXN since its current base,
1833          then it can't conflict with any changes since that base.
1834          The caller isn't supposed to call us in that case. */
1835       SVN_ERR_MALFUNCTION();
1836     }
1837   else
1838     SVN_ERR(merge(conflict, "/", txn_root_node,
1839                   source_node, ancestor_node, txn_id, NULL, pool));
1840
1841   return SVN_NO_ERROR;
1842 }
1843
1844
1845 svn_error_t *
1846 svn_fs_fs__commit_txn(const char **conflict_p,
1847                       svn_revnum_t *new_rev,
1848                       svn_fs_txn_t *txn,
1849                       apr_pool_t *pool)
1850 {
1851   /* How do commits work in Subversion?
1852    *
1853    * When you're ready to commit, here's what you have:
1854    *
1855    *    1. A transaction, with a mutable tree hanging off it.
1856    *    2. A base revision, against which TXN_TREE was made.
1857    *    3. A latest revision, which may be newer than the base rev.
1858    *
1859    * The problem is that if latest != base, then one can't simply
1860    * attach the txn root as the root of the new revision, because that
1861    * would lose all the changes between base and latest.  It is also
1862    * not acceptable to insist that base == latest; in a busy
1863    * repository, commits happen too fast to insist that everyone keep
1864    * their entire tree up-to-date at all times.  Non-overlapping
1865    * changes should not interfere with each other.
1866    *
1867    * The solution is to merge the changes between base and latest into
1868    * the txn tree [see the function merge()].  The txn tree is the
1869    * only one of the three trees that is mutable, so it has to be the
1870    * one to adjust.
1871    *
1872    * You might have to adjust it more than once, if a new latest
1873    * revision gets committed while you were merging in the previous
1874    * one.  For example:
1875    *
1876    *    1. Jane starts txn T, based at revision 6.
1877    *    2. Someone commits (or already committed) revision 7.
1878    *    3. Jane's starts merging the changes between 6 and 7 into T.
1879    *    4. Meanwhile, someone commits revision 8.
1880    *    5. Jane finishes the 6-->7 merge.  T could now be committed
1881    *       against a latest revision of 7, if only that were still the
1882    *       latest.  Unfortunately, 8 is now the latest, so...
1883    *    6. Jane starts merging the changes between 7 and 8 into T.
1884    *    7. Meanwhile, no one commits any new revisions.  Whew.
1885    *    8. Jane commits T, creating revision 9, whose tree is exactly
1886    *       T's tree, except immutable now.
1887    *
1888    * Lather, rinse, repeat.
1889    */
1890
1891   svn_error_t *err = SVN_NO_ERROR;
1892   svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool);
1893   svn_fs_t *fs = txn->fs;
1894
1895   /* Limit memory usage when the repository has a high commit rate and
1896      needs to run the following while loop multiple times.  The memory
1897      growth without an iteration pool is very noticeable when the
1898      transaction modifies a node that has 20,000 sibling nodes. */
1899   apr_pool_t *iterpool = svn_pool_create(pool);
1900
1901   /* Initialize output params. */
1902   *new_rev = SVN_INVALID_REVNUM;
1903   if (conflict_p)
1904     *conflict_p = NULL;
1905
1906   while (1729)
1907     {
1908       svn_revnum_t youngish_rev;
1909       svn_fs_root_t *youngish_root;
1910       dag_node_t *youngish_root_node;
1911
1912       svn_pool_clear(iterpool);
1913
1914       /* Get the *current* youngest revision.  We call it "youngish"
1915          because new revisions might get committed after we've
1916          obtained it. */
1917
1918       SVN_ERR(svn_fs_fs__youngest_rev(&youngish_rev, fs, iterpool));
1919       SVN_ERR(svn_fs_fs__revision_root(&youngish_root, fs, youngish_rev,
1920                                        iterpool));
1921
1922       /* Get the dag node for the youngest revision.  Later we'll use
1923          it as the SOURCE argument to a merge, and if the merge
1924          succeeds, this youngest root node will become the new base
1925          root for the svn txn that was the target of the merge (but
1926          note that the youngest rev may have changed by then -- that's
1927          why we're careful to get this root in its own bdb txn
1928          here). */
1929       SVN_ERR(get_root(&youngish_root_node, youngish_root, iterpool));
1930
1931       /* Try to merge.  If the merge succeeds, the base root node of
1932          TARGET's txn will become the same as youngish_root_node, so
1933          any future merges will only be between that node and whatever
1934          the root node of the youngest rev is by then. */
1935       err = merge_changes(NULL, youngish_root_node, txn, conflict, iterpool);
1936       if (err)
1937         {
1938           if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
1939             *conflict_p = conflict->data;
1940           goto cleanup;
1941         }
1942       txn->base_rev = youngish_rev;
1943
1944       /* Try to commit. */
1945       err = svn_fs_fs__commit(new_rev, fs, txn, iterpool);
1946       if (err && (err->apr_err == SVN_ERR_FS_TXN_OUT_OF_DATE))
1947         {
1948           /* Did someone else finish committing a new revision while we
1949              were in mid-merge or mid-commit?  If so, we'll need to
1950              loop again to merge the new changes in, then try to
1951              commit again.  Or if that's not what happened, then just
1952              return the error. */
1953           svn_revnum_t youngest_rev;
1954           SVN_ERR(svn_fs_fs__youngest_rev(&youngest_rev, fs, iterpool));
1955           if (youngest_rev == youngish_rev)
1956             goto cleanup;
1957           else
1958             svn_error_clear(err);
1959         }
1960       else if (err)
1961         {
1962           goto cleanup;
1963         }
1964       else
1965         {
1966           err = SVN_NO_ERROR;
1967           goto cleanup;
1968         }
1969     }
1970
1971  cleanup:
1972
1973   svn_fs_fs__reset_txn_caches(fs);
1974
1975   svn_pool_destroy(iterpool);
1976   return svn_error_trace(err);
1977 }
1978
1979
1980 /* Merge changes between two nodes into a third node.  Given nodes
1981    SOURCE_PATH under SOURCE_ROOT, TARGET_PATH under TARGET_ROOT and
1982    ANCESTOR_PATH under ANCESTOR_ROOT, modify target to contain all the
1983    changes between the ancestor and source.  If there are conflicts,
1984    return SVN_ERR_FS_CONFLICT and set *CONFLICT_P to a textual
1985    description of the offending changes.  Perform any temporary
1986    allocations in POOL. */
1987 static svn_error_t *
1988 fs_merge(const char **conflict_p,
1989          svn_fs_root_t *source_root,
1990          const char *source_path,
1991          svn_fs_root_t *target_root,
1992          const char *target_path,
1993          svn_fs_root_t *ancestor_root,
1994          const char *ancestor_path,
1995          apr_pool_t *pool)
1996 {
1997   dag_node_t *source, *ancestor;
1998   svn_fs_txn_t *txn;
1999   svn_error_t *err;
2000   svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool);
2001
2002   if (! target_root->is_txn_root)
2003     return SVN_FS__NOT_TXN(target_root);
2004
2005   /* Paranoia. */
2006   if ((source_root->fs != ancestor_root->fs)
2007       || (target_root->fs != ancestor_root->fs))
2008     {
2009       return svn_error_create
2010         (SVN_ERR_FS_CORRUPT, NULL,
2011          _("Bad merge; ancestor, source, and target not all in same fs"));
2012     }
2013
2014   /* ### kff todo: is there any compelling reason to get the nodes in
2015      one db transaction?  Right now we don't; txn_body_get_root() gets
2016      one node at a time.  This will probably need to change:
2017
2018      Jim Blandy <jimb@zwingli.cygnus.com> writes:
2019      > svn_fs_merge needs to be a single transaction, to protect it against
2020      > people deleting parents of nodes it's working on, etc.
2021   */
2022
2023   /* Get the ancestor node. */
2024   SVN_ERR(get_root(&ancestor, ancestor_root, pool));
2025
2026   /* Get the source node. */
2027   SVN_ERR(get_root(&source, source_root, pool));
2028
2029   /* Open a txn for the txn root into which we're merging. */
2030   SVN_ERR(svn_fs_fs__open_txn(&txn, ancestor_root->fs, target_root->txn,
2031                               pool));
2032
2033   /* Merge changes between ANCESTOR and SOURCE into TXN. */
2034   err = merge_changes(ancestor, source, txn, conflict, pool);
2035   if (err)
2036     {
2037       if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2038         *conflict_p = conflict->data;
2039       return svn_error_trace(err);
2040     }
2041
2042   return SVN_NO_ERROR;
2043 }
2044
2045 svn_error_t *
2046 svn_fs_fs__deltify(svn_fs_t *fs,
2047                    svn_revnum_t revision,
2048                    apr_pool_t *pool)
2049 {
2050   /* Deltify is a no-op for fs_fs. */
2051
2052   return SVN_NO_ERROR;
2053 }
2054
2055
2056 \f
2057 /* Directories.  */
2058
2059 /* Set *TABLE_P to a newly allocated APR hash table containing the
2060    entries of the directory at PATH in ROOT.  The keys of the table
2061    are entry names, as byte strings, excluding the final null
2062    character; the table's values are pointers to svn_fs_dirent_t
2063    structures.  Allocate the table and its contents in POOL. */
2064 static svn_error_t *
2065 fs_dir_entries(apr_hash_t **table_p,
2066                svn_fs_root_t *root,
2067                const char *path,
2068                apr_pool_t *pool)
2069 {
2070   dag_node_t *node;
2071
2072   /* Get the entries for this path in the caller's pool. */
2073   SVN_ERR(get_dag(&node, root, path, pool));
2074   return svn_fs_fs__dag_dir_entries(table_p, node, pool);
2075 }
2076
2077 /* Raise an error if PATH contains a newline because FSFS cannot handle
2078  * such paths. See issue #4340. */
2079 static svn_error_t *
2080 check_newline(const char *path, apr_pool_t *pool)
2081 {
2082   char *c = strchr(path, '\n');
2083
2084   if (c)
2085     return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
2086        _("Invalid control character '0x%02x' in path '%s'"),
2087        (unsigned char)*c, svn_path_illegal_path_escape(path, pool));
2088
2089   return SVN_NO_ERROR;
2090 }
2091
2092 /* Create a new directory named PATH in ROOT.  The new directory has
2093    no entries, and no properties.  ROOT must be the root of a
2094    transaction, not a revision.  Do any necessary temporary allocation
2095    in POOL.  */
2096 static svn_error_t *
2097 fs_make_dir(svn_fs_root_t *root,
2098             const char *path,
2099             apr_pool_t *pool)
2100 {
2101   parent_path_t *parent_path;
2102   dag_node_t *sub_dir;
2103   const char *txn_id = root->txn;
2104
2105   SVN_ERR(check_newline(path, pool));
2106
2107   path = svn_fs__canonicalize_abspath(path, pool);
2108   SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
2109                     txn_id, pool));
2110
2111   /* Check (recursively) to see if some lock is 'reserving' a path at
2112      that location, or even some child-path; if so, check that we can
2113      use it. */
2114   if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2115     SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, TRUE, FALSE,
2116                                               pool));
2117
2118   /* If there's already a sub-directory by that name, complain.  This
2119      also catches the case of trying to make a subdirectory named `/'.  */
2120   if (parent_path->node)
2121     return SVN_FS__ALREADY_EXISTS(root, path);
2122
2123   /* Create the subdirectory.  */
2124   SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
2125   SVN_ERR(svn_fs_fs__dag_make_dir(&sub_dir,
2126                                   parent_path->parent->node,
2127                                   parent_path_path(parent_path->parent,
2128                                                    pool),
2129                                   parent_path->entry,
2130                                   txn_id,
2131                                   pool));
2132
2133   /* Add this directory to the path cache. */
2134   SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool),
2135                              sub_dir, pool));
2136
2137   /* Make a record of this modification in the changes table. */
2138   return add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(sub_dir),
2139                     svn_fs_path_change_add, FALSE, FALSE, svn_node_dir,
2140                     SVN_INVALID_REVNUM, NULL, pool);
2141 }
2142
2143
2144 /* Delete the node at PATH under ROOT.  ROOT must be a transaction
2145    root.  Perform temporary allocations in POOL. */
2146 static svn_error_t *
2147 fs_delete_node(svn_fs_root_t *root,
2148                const char *path,
2149                apr_pool_t *pool)
2150 {
2151   parent_path_t *parent_path;
2152   const char *txn_id = root->txn;
2153   apr_int64_t mergeinfo_count = 0;
2154   svn_node_kind_t kind;
2155
2156   if (! root->is_txn_root)
2157     return SVN_FS__NOT_TXN(root);
2158
2159   path = svn_fs__canonicalize_abspath(path, pool);
2160   SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, pool));
2161   kind = svn_fs_fs__dag_node_kind(parent_path->node);
2162
2163   /* We can't remove the root of the filesystem.  */
2164   if (! parent_path->parent)
2165     return svn_error_create(SVN_ERR_FS_ROOT_DIR, NULL,
2166                             _("The root directory cannot be deleted"));
2167
2168   /* Check to see if path (or any child thereof) is locked; if so,
2169      check that we can use the existing lock(s). */
2170   if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2171     SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, TRUE, FALSE,
2172                                               pool));
2173
2174   /* Make the parent directory mutable, and do the deletion.  */
2175   SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
2176   if (svn_fs_fs__fs_supports_mergeinfo(root->fs))
2177     SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_count,
2178                                                parent_path->node));
2179   SVN_ERR(svn_fs_fs__dag_delete(parent_path->parent->node,
2180                                 parent_path->entry,
2181                                 txn_id, pool));
2182
2183   /* Remove this node and any children from the path cache. */
2184   SVN_ERR(dag_node_cache_invalidate(root, parent_path_path(parent_path, pool),
2185                                     pool));
2186
2187   /* Update mergeinfo counts for parents */
2188   if (mergeinfo_count > 0)
2189     SVN_ERR(increment_mergeinfo_up_tree(parent_path->parent,
2190                                         -mergeinfo_count,
2191                                         pool));
2192
2193   /* Make a record of this modification in the changes table. */
2194   return add_change(root->fs, txn_id, path,
2195                     svn_fs_fs__dag_get_id(parent_path->node),
2196                     svn_fs_path_change_delete, FALSE, FALSE, kind,
2197                     SVN_INVALID_REVNUM, NULL, pool);
2198 }
2199
2200
2201 /* Set *SAME_P to TRUE if FS1 and FS2 have the same UUID, else set to FALSE.
2202    Use POOL for temporary allocation only.
2203    Note: this code is duplicated between libsvn_fs_fs and libsvn_fs_base. */
2204 static svn_error_t *
2205 fs_same_p(svn_boolean_t *same_p,
2206           svn_fs_t *fs1,
2207           svn_fs_t *fs2,
2208           apr_pool_t *pool)
2209 {
2210   *same_p = ! strcmp(fs1->uuid, fs2->uuid);
2211   return SVN_NO_ERROR;
2212 }
2213
2214 /* Copy the node at FROM_PATH under FROM_ROOT to TO_PATH under
2215    TO_ROOT.  If PRESERVE_HISTORY is set, then the copy is recorded in
2216    the copies table.  Perform temporary allocations in POOL. */
2217 static svn_error_t *
2218 copy_helper(svn_fs_root_t *from_root,
2219             const char *from_path,
2220             svn_fs_root_t *to_root,
2221             const char *to_path,
2222             svn_boolean_t preserve_history,
2223             apr_pool_t *pool)
2224 {
2225   dag_node_t *from_node;
2226   parent_path_t *to_parent_path;
2227   const char *txn_id = to_root->txn;
2228   svn_boolean_t same_p;
2229
2230   /* Use an error check, not an assert, because even the caller cannot
2231      guarantee that a filesystem's UUID has not changed "on the fly". */
2232   SVN_ERR(fs_same_p(&same_p, from_root->fs, to_root->fs, pool));
2233   if (! same_p)
2234     return svn_error_createf
2235       (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2236        _("Cannot copy between two different filesystems ('%s' and '%s')"),
2237        from_root->fs->path, to_root->fs->path);
2238
2239   if (from_root->is_txn_root)
2240     return svn_error_create
2241       (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2242        _("Copy from mutable tree not currently supported"));
2243
2244   /* Get the NODE for FROM_PATH in FROM_ROOT.*/
2245   SVN_ERR(get_dag(&from_node, from_root, from_path, pool));
2246
2247   /* Build up the parent path from TO_PATH in TO_ROOT.  If the last
2248      component does not exist, it's not that big a deal.  We'll just
2249      make one there. */
2250   SVN_ERR(open_path(&to_parent_path, to_root, to_path,
2251                     open_path_last_optional, txn_id, pool));
2252
2253   /* Check to see if path (or any child thereof) is locked; if so,
2254      check that we can use the existing lock(s). */
2255   if (to_root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2256     SVN_ERR(svn_fs_fs__allow_locked_operation(to_path, to_root->fs,
2257                                               TRUE, FALSE, pool));
2258
2259   /* If the destination node already exists as the same node as the
2260      source (in other words, this operation would result in nothing
2261      happening at all), just do nothing an return successfully,
2262      proud that you saved yourself from a tiresome task. */
2263   if (to_parent_path->node &&
2264       svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(from_node),
2265                        svn_fs_fs__dag_get_id(to_parent_path->node)))
2266     return SVN_NO_ERROR;
2267
2268   if (! from_root->is_txn_root)
2269     {
2270       svn_fs_path_change_kind_t kind;
2271       dag_node_t *new_node;
2272       const char *from_canonpath;
2273       apr_int64_t mergeinfo_start;
2274       apr_int64_t mergeinfo_end;
2275
2276       /* If TO_PATH already existed prior to the copy, note that this
2277          operation is a replacement, not an addition. */
2278       if (to_parent_path->node)
2279         {
2280           kind = svn_fs_path_change_replace;
2281           if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs))
2282             SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start,
2283                                                        to_parent_path->node));
2284         }
2285       else
2286         {
2287           kind = svn_fs_path_change_add;
2288           mergeinfo_start = 0;
2289         }
2290
2291       if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs))
2292         SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end,
2293                                                    from_node));
2294
2295       /* Make sure the target node's parents are mutable.  */
2296       SVN_ERR(make_path_mutable(to_root, to_parent_path->parent,
2297                                 to_path, pool));
2298
2299       /* Canonicalize the copyfrom path. */
2300       from_canonpath = svn_fs__canonicalize_abspath(from_path, pool);
2301
2302       SVN_ERR(svn_fs_fs__dag_copy(to_parent_path->parent->node,
2303                                   to_parent_path->entry,
2304                                   from_node,
2305                                   preserve_history,
2306                                   from_root->rev,
2307                                   from_canonpath,
2308                                   txn_id, pool));
2309
2310       if (kind == svn_fs_path_change_replace)
2311         SVN_ERR(dag_node_cache_invalidate(to_root,
2312                                           parent_path_path(to_parent_path,
2313                                                            pool), pool));
2314
2315       if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs)
2316           && mergeinfo_start != mergeinfo_end)
2317         SVN_ERR(increment_mergeinfo_up_tree(to_parent_path->parent,
2318                                             mergeinfo_end - mergeinfo_start,
2319                                             pool));
2320
2321       /* Make a record of this modification in the changes table. */
2322       SVN_ERR(get_dag(&new_node, to_root, to_path, pool));
2323       SVN_ERR(add_change(to_root->fs, txn_id, to_path,
2324                          svn_fs_fs__dag_get_id(new_node), kind, FALSE, FALSE,
2325                          svn_fs_fs__dag_node_kind(from_node),
2326                          from_root->rev, from_canonpath, pool));
2327     }
2328   else
2329     {
2330       /* See IZ Issue #436 */
2331       /* Copying from transaction roots not currently available.
2332
2333          ### cmpilato todo someday: make this not so. :-) Note that
2334          when copying from mutable trees, you have to make sure that
2335          you aren't creating a cyclic graph filesystem, and a simple
2336          referencing operation won't cut it.  Currently, we should not
2337          be able to reach this clause, and the interface reports that
2338          this only works from immutable trees anyway, but JimB has
2339          stated that this requirement need not be necessary in the
2340          future. */
2341
2342       SVN_ERR_MALFUNCTION();
2343     }
2344
2345   return SVN_NO_ERROR;
2346 }
2347
2348
2349 /* Create a copy of FROM_PATH in FROM_ROOT named TO_PATH in TO_ROOT.
2350    If FROM_PATH is a directory, copy it recursively.  Temporary
2351    allocations are from POOL.*/
2352 static svn_error_t *
2353 fs_copy(svn_fs_root_t *from_root,
2354         const char *from_path,
2355         svn_fs_root_t *to_root,
2356         const char *to_path,
2357         apr_pool_t *pool)
2358 {
2359   SVN_ERR(check_newline(to_path, pool));
2360
2361   return svn_error_trace(copy_helper(from_root,
2362                                      svn_fs__canonicalize_abspath(from_path,
2363                                                                   pool),
2364                                      to_root,
2365                                      svn_fs__canonicalize_abspath(to_path,
2366                                                                   pool),
2367                                      TRUE, pool));
2368 }
2369
2370
2371 /* Create a copy of FROM_PATH in FROM_ROOT named TO_PATH in TO_ROOT.
2372    If FROM_PATH is a directory, copy it recursively.  No history is
2373    preserved.  Temporary allocations are from POOL. */
2374 static svn_error_t *
2375 fs_revision_link(svn_fs_root_t *from_root,
2376                  svn_fs_root_t *to_root,
2377                  const char *path,
2378                  apr_pool_t *pool)
2379 {
2380   if (! to_root->is_txn_root)
2381     return SVN_FS__NOT_TXN(to_root);
2382
2383   path = svn_fs__canonicalize_abspath(path, pool);
2384   return svn_error_trace(copy_helper(from_root, path, to_root, path,
2385                                      FALSE, pool));
2386 }
2387
2388
2389 /* Discover the copy ancestry of PATH under ROOT.  Return a relevant
2390    ancestor/revision combination in *PATH_P and *REV_P.  Temporary
2391    allocations are in POOL. */
2392 static svn_error_t *
2393 fs_copied_from(svn_revnum_t *rev_p,
2394                const char **path_p,
2395                svn_fs_root_t *root,
2396                const char *path,
2397                apr_pool_t *pool)
2398 {
2399   dag_node_t *node;
2400   const char *copyfrom_path, *copyfrom_str = NULL;
2401   svn_revnum_t copyfrom_rev;
2402   char *str, *buf;
2403
2404   /* Check to see if there is a cached version of this copyfrom
2405      entry. */
2406   if (! root->is_txn_root) {
2407     fs_rev_root_data_t *frd = root->fsap_data;
2408     copyfrom_str = svn_hash_gets(frd->copyfrom_cache, path);
2409   }
2410
2411   if (copyfrom_str)
2412     {
2413       if (*copyfrom_str == 0)
2414         {
2415           /* We have a cached entry that says there is no copyfrom
2416              here. */
2417           copyfrom_rev = SVN_INVALID_REVNUM;
2418           copyfrom_path = NULL;
2419         }
2420       else
2421         {
2422           /* Parse the copyfrom string for our cached entry. */
2423           buf = apr_pstrdup(pool, copyfrom_str);
2424           str = svn_cstring_tokenize(" ", &buf);
2425           copyfrom_rev = SVN_STR_TO_REV(str);
2426           copyfrom_path = buf;
2427         }
2428     }
2429   else
2430     {
2431       /* There is no cached entry, look it up the old-fashioned
2432          way. */
2433       SVN_ERR(get_dag(&node, root, path, pool));
2434       SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(&copyfrom_rev, node));
2435       SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(&copyfrom_path, node));
2436     }
2437
2438   *rev_p  = copyfrom_rev;
2439   *path_p = copyfrom_path;
2440
2441   return SVN_NO_ERROR;
2442 }
2443
2444
2445 \f
2446 /* Files.  */
2447
2448 /* Create the empty file PATH under ROOT.  Temporary allocations are
2449    in POOL. */
2450 static svn_error_t *
2451 fs_make_file(svn_fs_root_t *root,
2452              const char *path,
2453              apr_pool_t *pool)
2454 {
2455   parent_path_t *parent_path;
2456   dag_node_t *child;
2457   const char *txn_id = root->txn;
2458
2459   SVN_ERR(check_newline(path, pool));
2460
2461   path = svn_fs__canonicalize_abspath(path, pool);
2462   SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
2463                    txn_id, pool));
2464
2465   /* If there's already a file by that name, complain.
2466      This also catches the case of trying to make a file named `/'.  */
2467   if (parent_path->node)
2468     return SVN_FS__ALREADY_EXISTS(root, path);
2469
2470   /* Check (non-recursively) to see if path is locked;  if so, check
2471      that we can use it. */
2472   if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2473     SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, FALSE, FALSE,
2474                                               pool));
2475
2476   /* Create the file.  */
2477   SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
2478   SVN_ERR(svn_fs_fs__dag_make_file(&child,
2479                                    parent_path->parent->node,
2480                                    parent_path_path(parent_path->parent,
2481                                                     pool),
2482                                    parent_path->entry,
2483                                    txn_id,
2484                                    pool));
2485
2486   /* Add this file to the path cache. */
2487   SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool), child,
2488                              pool));
2489
2490   /* Make a record of this modification in the changes table. */
2491   return add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(child),
2492                     svn_fs_path_change_add, TRUE, FALSE, svn_node_file,
2493                     SVN_INVALID_REVNUM, NULL, pool);
2494 }
2495
2496
2497 /* Set *LENGTH_P to the size of the file PATH under ROOT.  Temporary
2498    allocations are in POOL. */
2499 static svn_error_t *
2500 fs_file_length(svn_filesize_t *length_p,
2501                svn_fs_root_t *root,
2502                const char *path,
2503                apr_pool_t *pool)
2504 {
2505   dag_node_t *file;
2506
2507   /* First create a dag_node_t from the root/path pair. */
2508   SVN_ERR(get_dag(&file, root, path, pool));
2509
2510   /* Now fetch its length */
2511   return svn_fs_fs__dag_file_length(length_p, file, pool);
2512 }
2513
2514
2515 /* Set *CHECKSUM to the checksum of type KIND for PATH under ROOT, or
2516    NULL if that information isn't available.  Temporary allocations
2517    are from POOL. */
2518 static svn_error_t *
2519 fs_file_checksum(svn_checksum_t **checksum,
2520                  svn_checksum_kind_t kind,
2521                  svn_fs_root_t *root,
2522                  const char *path,
2523                  apr_pool_t *pool)
2524 {
2525   dag_node_t *file;
2526
2527   SVN_ERR(get_dag(&file, root, path, pool));
2528   return svn_fs_fs__dag_file_checksum(checksum, file, kind, pool);
2529 }
2530
2531
2532 /* --- Machinery for svn_fs_file_contents() ---  */
2533
2534 /* Set *CONTENTS to a readable stream that will return the contents of
2535    PATH under ROOT.  The stream is allocated in POOL. */
2536 static svn_error_t *
2537 fs_file_contents(svn_stream_t **contents,
2538                  svn_fs_root_t *root,
2539                  const char *path,
2540                  apr_pool_t *pool)
2541 {
2542   dag_node_t *node;
2543   svn_stream_t *file_stream;
2544
2545   /* First create a dag_node_t from the root/path pair. */
2546   SVN_ERR(get_dag(&node, root, path, pool));
2547
2548   /* Then create a readable stream from the dag_node_t. */
2549   SVN_ERR(svn_fs_fs__dag_get_contents(&file_stream, node, pool));
2550
2551   *contents = file_stream;
2552   return SVN_NO_ERROR;
2553 }
2554
2555 /* --- End machinery for svn_fs_file_contents() ---  */
2556
2557
2558 /* --- Machinery for svn_fs_try_process_file_contents() ---  */
2559
2560 static svn_error_t *
2561 fs_try_process_file_contents(svn_boolean_t *success,
2562                              svn_fs_root_t *root,
2563                              const char *path,
2564                              svn_fs_process_contents_func_t processor,
2565                              void* baton,
2566                              apr_pool_t *pool)
2567 {
2568   dag_node_t *node;
2569   SVN_ERR(get_dag(&node, root, path, pool));
2570
2571   return svn_fs_fs__dag_try_process_file_contents(success, node,
2572                                                   processor, baton, pool);
2573 }
2574
2575 /* --- End machinery for svn_fs_try_process_file_contents() ---  */
2576
2577
2578 /* --- Machinery for svn_fs_apply_textdelta() ---  */
2579
2580
2581 /* Local baton type for all the helper functions below. */
2582 typedef struct txdelta_baton_t
2583 {
2584   /* This is the custom-built window consumer given to us by the delta
2585      library;  it uniquely knows how to read data from our designated
2586      "source" stream, interpret the window, and write data to our
2587      designated "target" stream (in this case, our repos file.) */
2588   svn_txdelta_window_handler_t interpreter;
2589   void *interpreter_baton;
2590
2591   /* The original file info */
2592   svn_fs_root_t *root;
2593   const char *path;
2594
2595   /* Derived from the file info */
2596   dag_node_t *node;
2597
2598   svn_stream_t *source_stream;
2599   svn_stream_t *target_stream;
2600   svn_stream_t *string_stream;
2601   svn_stringbuf_t *target_string;
2602
2603   /* MD5 digest for the base text against which a delta is to be
2604      applied, and for the resultant fulltext, respectively.  Either or
2605      both may be null, in which case ignored. */
2606   svn_checksum_t *base_checksum;
2607   svn_checksum_t *result_checksum;
2608
2609   /* Pool used by db txns */
2610   apr_pool_t *pool;
2611
2612 } txdelta_baton_t;
2613
2614
2615 /* ### see comment in window_consumer() regarding this function. */
2616
2617 /* Helper function of generic type `svn_write_fn_t'.  Implements a
2618    writable stream which appends to an svn_stringbuf_t. */
2619 static svn_error_t *
2620 write_to_string(void *baton, const char *data, apr_size_t *len)
2621 {
2622   txdelta_baton_t *tb = (txdelta_baton_t *) baton;
2623   svn_stringbuf_appendbytes(tb->target_string, data, *len);
2624   return SVN_NO_ERROR;
2625 }
2626
2627
2628
2629 /* The main window handler returned by svn_fs_apply_textdelta. */
2630 static svn_error_t *
2631 window_consumer(svn_txdelta_window_t *window, void *baton)
2632 {
2633   txdelta_baton_t *tb = (txdelta_baton_t *) baton;
2634
2635   /* Send the window right through to the custom window interpreter.
2636      In theory, the interpreter will then write more data to
2637      cb->target_string. */
2638   SVN_ERR(tb->interpreter(window, tb->interpreter_baton));
2639
2640   /* ### the write_to_string() callback for the txdelta's output stream
2641      ### should be doing all the flush determination logic, not here.
2642      ### in a drastic case, a window could generate a LOT more than the
2643      ### maximum buffer size. we want to flush to the underlying target
2644      ### stream much sooner (e.g. also in a streamy fashion). also, by
2645      ### moving this logic inside the stream, the stream becomes nice
2646      ### and encapsulated: it holds all the logic about buffering and
2647      ### flushing.
2648      ###
2649      ### further: I believe the buffering should be removed from tree.c
2650      ### the buffering should go into the target_stream itself, which
2651      ### is defined by reps-string.c. Specifically, I think the
2652      ### rep_write_contents() function will handle the buffering and
2653      ### the spill to the underlying DB. by locating it there, then
2654      ### anybody who gets a writable stream for FS content can take
2655      ### advantage of the buffering capability. this will be important
2656      ### when we export an FS API function for writing a fulltext into
2657      ### the FS, rather than forcing that fulltext thru apply_textdelta.
2658   */
2659
2660   /* Check to see if we need to purge the portion of the contents that
2661      have been written thus far. */
2662   if ((! window) || (tb->target_string->len > WRITE_BUFFER_SIZE))
2663     {
2664       apr_size_t len = tb->target_string->len;
2665       SVN_ERR(svn_stream_write(tb->target_stream,
2666                                tb->target_string->data,
2667                                &len));
2668       svn_stringbuf_setempty(tb->target_string);
2669     }
2670
2671   /* Is the window NULL?  If so, we're done. */
2672   if (! window)
2673     {
2674       /* Close the internal-use stream.  ### This used to be inside of
2675          txn_body_fulltext_finalize_edits(), but that invoked a nested
2676          Berkeley DB transaction -- scandalous! */
2677       SVN_ERR(svn_stream_close(tb->target_stream));
2678
2679       SVN_ERR(svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum,
2680                                             tb->pool));
2681     }
2682
2683   return SVN_NO_ERROR;
2684 }
2685
2686 /* Helper function for fs_apply_textdelta.  BATON is of type
2687    txdelta_baton_t. */
2688 static svn_error_t *
2689 apply_textdelta(void *baton, apr_pool_t *pool)
2690 {
2691   txdelta_baton_t *tb = (txdelta_baton_t *) baton;
2692   parent_path_t *parent_path;
2693   const char *txn_id = tb->root->txn;
2694
2695   /* Call open_path with no flags, as we want this to return an error
2696      if the node for which we are searching doesn't exist. */
2697   SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id, pool));
2698
2699   /* Check (non-recursively) to see if path is locked; if so, check
2700      that we can use it. */
2701   if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2702     SVN_ERR(svn_fs_fs__allow_locked_operation(tb->path, tb->root->fs,
2703                                               FALSE, FALSE, pool));
2704
2705   /* Now, make sure this path is mutable. */
2706   SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, pool));
2707   tb->node = parent_path->node;
2708
2709   if (tb->base_checksum)
2710     {
2711       svn_checksum_t *checksum;
2712
2713       /* Until we finalize the node, its data_key points to the old
2714          contents, in other words, the base text. */
2715       SVN_ERR(svn_fs_fs__dag_file_checksum(&checksum, tb->node,
2716                                            tb->base_checksum->kind, pool));
2717       if (!svn_checksum_match(tb->base_checksum, checksum))
2718         return svn_checksum_mismatch_err(tb->base_checksum, checksum, pool,
2719                                          _("Base checksum mismatch on '%s'"),
2720                                          tb->path);
2721     }
2722
2723   /* Make a readable "source" stream out of the current contents of
2724      ROOT/PATH; obviously, this must done in the context of a db_txn.
2725      The stream is returned in tb->source_stream. */
2726   SVN_ERR(svn_fs_fs__dag_get_contents(&(tb->source_stream),
2727                                       tb->node, tb->pool));
2728
2729   /* Make a writable "target" stream */
2730   SVN_ERR(svn_fs_fs__dag_get_edit_stream(&(tb->target_stream), tb->node,
2731                                          tb->pool));
2732
2733   /* Make a writable "string" stream which writes data to
2734      tb->target_string. */
2735   tb->target_string = svn_stringbuf_create_empty(tb->pool);
2736   tb->string_stream = svn_stream_create(tb, tb->pool);
2737   svn_stream_set_write(tb->string_stream, write_to_string);
2738
2739   /* Now, create a custom window handler that uses our two streams. */
2740   svn_txdelta_apply(tb->source_stream,
2741                     tb->string_stream,
2742                     NULL,
2743                     tb->path,
2744                     tb->pool,
2745                     &(tb->interpreter),
2746                     &(tb->interpreter_baton));
2747
2748   /* Make a record of this modification in the changes table. */
2749   return add_change(tb->root->fs, txn_id, tb->path,
2750                     svn_fs_fs__dag_get_id(tb->node),
2751                     svn_fs_path_change_modify, TRUE, FALSE, svn_node_file,
2752                     SVN_INVALID_REVNUM, NULL, pool);
2753 }
2754
2755
2756 /* Set *CONTENTS_P and *CONTENTS_BATON_P to a window handler and baton
2757    that will accept text delta windows to modify the contents of PATH
2758    under ROOT.  Allocations are in POOL. */
2759 static svn_error_t *
2760 fs_apply_textdelta(svn_txdelta_window_handler_t *contents_p,
2761                    void **contents_baton_p,
2762                    svn_fs_root_t *root,
2763                    const char *path,
2764                    svn_checksum_t *base_checksum,
2765                    svn_checksum_t *result_checksum,
2766                    apr_pool_t *pool)
2767 {
2768   txdelta_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
2769
2770   tb->root = root;
2771   tb->path = svn_fs__canonicalize_abspath(path, pool);
2772   tb->pool = pool;
2773   tb->base_checksum = svn_checksum_dup(base_checksum, pool);
2774   tb->result_checksum = svn_checksum_dup(result_checksum, pool);
2775
2776   SVN_ERR(apply_textdelta(tb, pool));
2777
2778   *contents_p = window_consumer;
2779   *contents_baton_p = tb;
2780   return SVN_NO_ERROR;
2781 }
2782
2783 /* --- End machinery for svn_fs_apply_textdelta() ---  */
2784
2785 /* --- Machinery for svn_fs_apply_text() ---  */
2786
2787 /* Baton for svn_fs_apply_text(). */
2788 struct text_baton_t
2789 {
2790   /* The original file info */
2791   svn_fs_root_t *root;
2792   const char *path;
2793
2794   /* Derived from the file info */
2795   dag_node_t *node;
2796
2797   /* The returned stream that will accept the file's new contents. */
2798   svn_stream_t *stream;
2799
2800   /* The actual fs stream that the returned stream will write to. */
2801   svn_stream_t *file_stream;
2802
2803   /* MD5 digest for the final fulltext written to the file.  May
2804      be null, in which case ignored. */
2805   svn_checksum_t *result_checksum;
2806
2807   /* Pool used by db txns */
2808   apr_pool_t *pool;
2809 };
2810
2811
2812 /* A wrapper around svn_fs_fs__dag_finalize_edits, but for
2813  * fulltext data, not text deltas.  Closes BATON->file_stream.
2814  *
2815  * Note: If you're confused about how this function relates to another
2816  * of similar name, think of it this way:
2817  *
2818  * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits()
2819  * svn_fs_apply_text()      ==> ... ==> txn_body_fulltext_finalize_edits()
2820  */
2821
2822 /* Write function for the publically returned stream. */
2823 static svn_error_t *
2824 text_stream_writer(void *baton,
2825                    const char *data,
2826                    apr_size_t *len)
2827 {
2828   struct text_baton_t *tb = baton;
2829
2830   /* Psst, here's some data.  Pass it on to the -real- file stream. */
2831   return svn_stream_write(tb->file_stream, data, len);
2832 }
2833
2834 /* Close function for the publically returned stream. */
2835 static svn_error_t *
2836 text_stream_closer(void *baton)
2837 {
2838   struct text_baton_t *tb = baton;
2839
2840   /* Close the internal-use stream.  ### This used to be inside of
2841      txn_body_fulltext_finalize_edits(), but that invoked a nested
2842      Berkeley DB transaction -- scandalous! */
2843   SVN_ERR(svn_stream_close(tb->file_stream));
2844
2845   /* Need to tell fs that we're done sending text */
2846   return svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum,
2847                                        tb->pool);
2848 }
2849
2850
2851 /* Helper function for fs_apply_text.  BATON is of type
2852    text_baton_t. */
2853 static svn_error_t *
2854 apply_text(void *baton, apr_pool_t *pool)
2855 {
2856   struct text_baton_t *tb = baton;
2857   parent_path_t *parent_path;
2858   const char *txn_id = tb->root->txn;
2859
2860   /* Call open_path with no flags, as we want this to return an error
2861      if the node for which we are searching doesn't exist. */
2862   SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id, pool));
2863
2864   /* Check (non-recursively) to see if path is locked; if so, check
2865      that we can use it. */
2866   if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2867     SVN_ERR(svn_fs_fs__allow_locked_operation(tb->path, tb->root->fs,
2868                                               FALSE, FALSE, pool));
2869
2870   /* Now, make sure this path is mutable. */
2871   SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, pool));
2872   tb->node = parent_path->node;
2873
2874   /* Make a writable stream for replacing the file's text. */
2875   SVN_ERR(svn_fs_fs__dag_get_edit_stream(&(tb->file_stream), tb->node,
2876                                          tb->pool));
2877
2878   /* Create a 'returnable' stream which writes to the file_stream. */
2879   tb->stream = svn_stream_create(tb, tb->pool);
2880   svn_stream_set_write(tb->stream, text_stream_writer);
2881   svn_stream_set_close(tb->stream, text_stream_closer);
2882
2883   /* Make a record of this modification in the changes table. */
2884   return add_change(tb->root->fs, txn_id, tb->path,
2885                     svn_fs_fs__dag_get_id(tb->node),
2886                     svn_fs_path_change_modify, TRUE, FALSE, svn_node_file,
2887                     SVN_INVALID_REVNUM, NULL, pool);
2888 }
2889
2890
2891 /* Return a writable stream that will set the contents of PATH under
2892    ROOT.  RESULT_CHECKSUM is the MD5 checksum of the final result.
2893    Temporary allocations are in POOL. */
2894 static svn_error_t *
2895 fs_apply_text(svn_stream_t **contents_p,
2896               svn_fs_root_t *root,
2897               const char *path,
2898               svn_checksum_t *result_checksum,
2899               apr_pool_t *pool)
2900 {
2901   struct text_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
2902
2903   tb->root = root;
2904   tb->path = svn_fs__canonicalize_abspath(path, pool);
2905   tb->pool = pool;
2906   tb->result_checksum = svn_checksum_dup(result_checksum, pool);
2907
2908   SVN_ERR(apply_text(tb, pool));
2909
2910   *contents_p = tb->stream;
2911   return SVN_NO_ERROR;
2912 }
2913
2914 /* --- End machinery for svn_fs_apply_text() ---  */
2915
2916
2917 /* Check if the contents of PATH1 under ROOT1 are different from the
2918    contents of PATH2 under ROOT2.  If they are different set
2919    *CHANGED_P to TRUE, otherwise set it to FALSE. */
2920 static svn_error_t *
2921 fs_contents_changed(svn_boolean_t *changed_p,
2922                     svn_fs_root_t *root1,
2923                     const char *path1,
2924                     svn_fs_root_t *root2,
2925                     const char *path2,
2926                     apr_pool_t *pool)
2927 {
2928   dag_node_t *node1, *node2;
2929
2930   /* Check that roots are in the same fs. */
2931   if (root1->fs != root2->fs)
2932     return svn_error_create
2933       (SVN_ERR_FS_GENERAL, NULL,
2934        _("Cannot compare file contents between two different filesystems"));
2935
2936   /* Check that both paths are files. */
2937   {
2938     svn_node_kind_t kind;
2939
2940     SVN_ERR(svn_fs_fs__check_path(&kind, root1, path1, pool));
2941     if (kind != svn_node_file)
2942       return svn_error_createf
2943         (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path1);
2944
2945     SVN_ERR(svn_fs_fs__check_path(&kind, root2, path2, pool));
2946     if (kind != svn_node_file)
2947       return svn_error_createf
2948         (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path2);
2949   }
2950
2951   SVN_ERR(get_dag(&node1, root1, path1, pool));
2952   SVN_ERR(get_dag(&node2, root2, path2, pool));
2953   return svn_fs_fs__dag_things_different(NULL, changed_p,
2954                                          node1, node2);
2955 }
2956
2957
2958 \f
2959 /* Public interface to computing file text deltas.  */
2960
2961 static svn_error_t *
2962 fs_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
2963                          svn_fs_root_t *source_root,
2964                          const char *source_path,
2965                          svn_fs_root_t *target_root,
2966                          const char *target_path,
2967                          apr_pool_t *pool)
2968 {
2969   dag_node_t *source_node, *target_node;
2970
2971   if (source_root && source_path)
2972     SVN_ERR(get_dag(&source_node, source_root, source_path, pool));
2973   else
2974     source_node = NULL;
2975   SVN_ERR(get_dag(&target_node, target_root, target_path, pool));
2976
2977   /* Create a delta stream that turns the source into the target.  */
2978   return svn_fs_fs__dag_get_file_delta_stream(stream_p, source_node,
2979                                               target_node, pool);
2980 }
2981
2982
2983 \f
2984 /* Finding Changes */
2985
2986 /* Set *CHANGED_PATHS_P to a newly allocated hash containing
2987    descriptions of the paths changed under ROOT.  The hash is keyed
2988    with const char * paths and has svn_fs_path_change2_t * values.  Use
2989    POOL for all allocations. */
2990 static svn_error_t *
2991 fs_paths_changed(apr_hash_t **changed_paths_p,
2992                  svn_fs_root_t *root,
2993                  apr_pool_t *pool)
2994 {
2995   if (root->is_txn_root)
2996     return svn_fs_fs__txn_changes_fetch(changed_paths_p, root->fs, root->txn,
2997                                         pool);
2998   else
2999     {
3000       fs_rev_root_data_t *frd = root->fsap_data;
3001       return svn_fs_fs__paths_changed(changed_paths_p, root->fs, root->rev,
3002                                       frd->copyfrom_cache, pool);
3003     }
3004 }
3005
3006
3007 \f
3008 /* Our coolio opaque history object. */
3009 typedef struct fs_history_data_t
3010 {
3011   /* filesystem object */
3012   svn_fs_t *fs;
3013
3014   /* path and revision of historical location */
3015   const char *path;
3016   svn_revnum_t revision;
3017
3018   /* internal-use hints about where to resume the history search. */
3019   const char *path_hint;
3020   svn_revnum_t rev_hint;
3021
3022   /* FALSE until the first call to svn_fs_history_prev(). */
3023   svn_boolean_t is_interesting;
3024 } fs_history_data_t;
3025
3026 static svn_fs_history_t *
3027 assemble_history(svn_fs_t *fs,
3028                  const char *path,
3029                  svn_revnum_t revision,
3030                  svn_boolean_t is_interesting,
3031                  const char *path_hint,
3032                  svn_revnum_t rev_hint,
3033                  apr_pool_t *pool);
3034
3035
3036 /* Set *HISTORY_P to an opaque node history object which represents
3037    PATH under ROOT.  ROOT must be a revision root.  Use POOL for all
3038    allocations. */
3039 static svn_error_t *
3040 fs_node_history(svn_fs_history_t **history_p,
3041                 svn_fs_root_t *root,
3042                 const char *path,
3043                 apr_pool_t *pool)
3044 {
3045   svn_node_kind_t kind;
3046
3047   /* We require a revision root. */
3048   if (root->is_txn_root)
3049     return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
3050
3051   /* And we require that the path exist in the root. */
3052   SVN_ERR(svn_fs_fs__check_path(&kind, root, path, pool));
3053   if (kind == svn_node_none)
3054     return SVN_FS__NOT_FOUND(root, path);
3055
3056   /* Okay, all seems well.  Build our history object and return it. */
3057   *history_p = assemble_history(root->fs,
3058                                 svn_fs__canonicalize_abspath(path, pool),
3059                                 root->rev, FALSE, NULL,
3060                                 SVN_INVALID_REVNUM, pool);
3061   return SVN_NO_ERROR;
3062 }
3063
3064 /* Find the youngest copyroot for path PARENT_PATH or its parents in
3065    filesystem FS, and store the copyroot in *REV_P and *PATH_P.
3066    Perform all allocations in POOL. */
3067 static svn_error_t *
3068 find_youngest_copyroot(svn_revnum_t *rev_p,
3069                        const char **path_p,
3070                        svn_fs_t *fs,
3071                        parent_path_t *parent_path,
3072                        apr_pool_t *pool)
3073 {
3074   svn_revnum_t rev_mine;
3075   svn_revnum_t rev_parent = SVN_INVALID_REVNUM;
3076   const char *path_mine;
3077   const char *path_parent = NULL;
3078
3079   /* First find our parent's youngest copyroot. */
3080   if (parent_path->parent)
3081     SVN_ERR(find_youngest_copyroot(&rev_parent, &path_parent, fs,
3082                                    parent_path->parent, pool));
3083
3084   /* Find our copyroot. */
3085   SVN_ERR(svn_fs_fs__dag_get_copyroot(&rev_mine, &path_mine,
3086                                       parent_path->node));
3087
3088   /* If a parent and child were copied to in the same revision, prefer
3089      the child copy target, since it is the copy relevant to the
3090      history of the child. */
3091   if (rev_mine >= rev_parent)
3092     {
3093       *rev_p = rev_mine;
3094       *path_p = path_mine;
3095     }
3096   else
3097     {
3098       *rev_p = rev_parent;
3099       *path_p = path_parent;
3100     }
3101
3102   return SVN_NO_ERROR;
3103 }
3104
3105
3106 static svn_error_t *fs_closest_copy(svn_fs_root_t **root_p,
3107                                     const char **path_p,
3108                                     svn_fs_root_t *root,
3109                                     const char *path,
3110                                     apr_pool_t *pool)
3111 {
3112   svn_fs_t *fs = root->fs;
3113   parent_path_t *parent_path, *copy_dst_parent_path;
3114   svn_revnum_t copy_dst_rev, created_rev;
3115   const char *copy_dst_path;
3116   svn_fs_root_t *copy_dst_root;
3117   dag_node_t *copy_dst_node;
3118   svn_node_kind_t kind;
3119
3120   /* Initialize return values. */
3121   *root_p = NULL;
3122   *path_p = NULL;
3123
3124   path = svn_fs__canonicalize_abspath(path, pool);
3125   SVN_ERR(open_path(&parent_path, root, path, 0, NULL, pool));
3126
3127   /* Find the youngest copyroot in the path of this node-rev, which
3128      will indicate the target of the innermost copy affecting the
3129      node-rev. */
3130   SVN_ERR(find_youngest_copyroot(&copy_dst_rev, &copy_dst_path,
3131                                  fs, parent_path, pool));
3132   if (copy_dst_rev == 0)  /* There are no copies affecting this node-rev. */
3133     return SVN_NO_ERROR;
3134
3135   /* It is possible that this node was created from scratch at some
3136      revision between COPY_DST_REV and REV.  Make sure that PATH
3137      exists as of COPY_DST_REV and is related to this node-rev. */
3138   SVN_ERR(svn_fs_fs__revision_root(&copy_dst_root, fs, copy_dst_rev, pool));
3139   SVN_ERR(svn_fs_fs__check_path(&kind, copy_dst_root, path, pool));
3140   if (kind == svn_node_none)
3141     return SVN_NO_ERROR;
3142   SVN_ERR(open_path(&copy_dst_parent_path, copy_dst_root, path,
3143                     open_path_node_only, NULL, pool));
3144   copy_dst_node = copy_dst_parent_path->node;
3145   if (! svn_fs_fs__id_check_related(svn_fs_fs__dag_get_id(copy_dst_node),
3146                                     svn_fs_fs__dag_get_id(parent_path->node)))
3147     return SVN_NO_ERROR;
3148
3149   /* One final check must be done here.  If you copy a directory and
3150      create a new entity somewhere beneath that directory in the same
3151      txn, then we can't claim that the copy affected the new entity.
3152      For example, if you do:
3153
3154         copy dir1 dir2
3155         create dir2/new-thing
3156         commit
3157
3158      then dir2/new-thing was not affected by the copy of dir1 to dir2.
3159      We detect this situation by asking if PATH@COPY_DST_REV's
3160      created-rev is COPY_DST_REV, and that node-revision has no
3161      predecessors, then there is no relevant closest copy.
3162   */
3163   SVN_ERR(svn_fs_fs__dag_get_revision(&created_rev, copy_dst_node, pool));
3164   if (created_rev == copy_dst_rev)
3165     {
3166       const svn_fs_id_t *pred;
3167       SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred, copy_dst_node));
3168       if (! pred)
3169         return SVN_NO_ERROR;
3170     }
3171
3172   /* The copy destination checks out.  Return it. */
3173   *root_p = copy_dst_root;
3174   *path_p = copy_dst_path;
3175   return SVN_NO_ERROR;
3176 }
3177
3178
3179 /* Set *PREV_PATH and *PREV_REV to the path and revision which
3180    represent the location at which PATH in FS was located immediately
3181    prior to REVISION iff there was a copy operation (to PATH or one of
3182    its parent directories) between that previous location and
3183    PATH@REVISION.
3184
3185    If there was no such copy operation in that portion of PATH's
3186    history, set *PREV_PATH to NULL and *PREV_REV to SVN_INVALID_REVNUM.  */
3187 static svn_error_t *
3188 prev_location(const char **prev_path,
3189               svn_revnum_t *prev_rev,
3190               svn_fs_t *fs,
3191               svn_fs_root_t *root,
3192               const char *path,
3193               apr_pool_t *pool)
3194 {
3195   const char *copy_path, *copy_src_path, *remainder_path;
3196   svn_fs_root_t *copy_root;
3197   svn_revnum_t copy_src_rev;
3198
3199   /* Ask about the most recent copy which affected PATH@REVISION.  If
3200      there was no such copy, we're done.  */
3201   SVN_ERR(fs_closest_copy(&copy_root, &copy_path, root, path, pool));
3202   if (! copy_root)
3203     {
3204       *prev_rev = SVN_INVALID_REVNUM;
3205       *prev_path = NULL;
3206       return SVN_NO_ERROR;
3207     }
3208
3209   /* Ultimately, it's not the path of the closest copy's source that
3210      we care about -- it's our own path's location in the copy source
3211      revision.  So we'll tack the relative path that expresses the
3212      difference between the copy destination and our path in the copy
3213      revision onto the copy source path to determine this information.
3214
3215      In other words, if our path is "/branches/my-branch/foo/bar", and
3216      we know that the closest relevant copy was a copy of "/trunk" to
3217      "/branches/my-branch", then that relative path under the copy
3218      destination is "/foo/bar".  Tacking that onto the copy source
3219      path tells us that our path was located at "/trunk/foo/bar"
3220      before the copy.
3221   */
3222   SVN_ERR(fs_copied_from(&copy_src_rev, &copy_src_path,
3223                          copy_root, copy_path, pool));
3224   remainder_path = svn_fspath__skip_ancestor(copy_path, path);
3225   *prev_path = svn_fspath__join(copy_src_path, remainder_path, pool);
3226   *prev_rev = copy_src_rev;
3227   return SVN_NO_ERROR;
3228 }
3229
3230
3231 static svn_error_t *
3232 fs_node_origin_rev(svn_revnum_t *revision,
3233                    svn_fs_root_t *root,
3234                    const char *path,
3235                    apr_pool_t *pool)
3236 {
3237   svn_fs_t *fs = root->fs;
3238   const svn_fs_id_t *given_noderev_id, *cached_origin_id;
3239   const char *node_id, *dash;
3240
3241   path = svn_fs__canonicalize_abspath(path, pool);
3242
3243   /* Check the cache first. */
3244   SVN_ERR(svn_fs_fs__node_id(&given_noderev_id, root, path, pool));
3245   node_id = svn_fs_fs__id_node_id(given_noderev_id);
3246
3247   /* Is it a brand new uncommitted node? */
3248   if (node_id[0] == '_')
3249     {
3250       *revision = SVN_INVALID_REVNUM;
3251       return SVN_NO_ERROR;
3252     }
3253
3254   /* Maybe this is a new-style node ID that just has the revision
3255      sitting right in it. */
3256   dash = strchr(node_id, '-');
3257   if (dash && *(dash+1))
3258     {
3259       *revision = SVN_STR_TO_REV(dash + 1);
3260       return SVN_NO_ERROR;
3261     }
3262
3263   /* The root node always has ID 0, created in revision 0 and will never
3264      use the new-style ID format. */
3265   if (strcmp(node_id, "0") == 0)
3266     {
3267       *revision = 0;
3268       return SVN_NO_ERROR;
3269     }
3270
3271   /* OK, it's an old-style ID?  Maybe it's cached. */
3272   SVN_ERR(svn_fs_fs__get_node_origin(&cached_origin_id,
3273                                      fs,
3274                                      node_id,
3275                                      pool));
3276   if (cached_origin_id != NULL)
3277     {
3278       *revision = svn_fs_fs__id_rev(cached_origin_id);
3279       return SVN_NO_ERROR;
3280     }
3281
3282   {
3283     /* Ah well, the answer isn't in the ID itself or in the cache.
3284        Let's actually calculate it, then. */
3285     svn_fs_root_t *curroot = root;
3286     apr_pool_t *subpool = svn_pool_create(pool);
3287     apr_pool_t *predidpool = svn_pool_create(pool);
3288     svn_stringbuf_t *lastpath = svn_stringbuf_create(path, pool);
3289     svn_revnum_t lastrev = SVN_INVALID_REVNUM;
3290     dag_node_t *node;
3291     const svn_fs_id_t *pred_id;
3292
3293     /* Walk the closest-copy chain back to the first copy in our history.
3294
3295        NOTE: We merely *assume* that this is faster than walking the
3296        predecessor chain, because we *assume* that copies of parent
3297        directories happen less often than modifications to a given item. */
3298     while (1)
3299       {
3300         svn_revnum_t currev;
3301         const char *curpath = lastpath->data;
3302
3303         svn_pool_clear(subpool);
3304
3305         /* Get a root pointing to LASTREV.  (The first time around,
3306            LASTREV is invalid, but that's cool because CURROOT is
3307            already initialized.)  */
3308         if (SVN_IS_VALID_REVNUM(lastrev))
3309           SVN_ERR(svn_fs_fs__revision_root(&curroot, fs, lastrev, subpool));
3310
3311         /* Find the previous location using the closest-copy shortcut. */
3312         SVN_ERR(prev_location(&curpath, &currev, fs, curroot, curpath,
3313                               subpool));
3314         if (! curpath)
3315           break;
3316
3317         /* Update our LASTPATH and LASTREV variables (which survive
3318            SUBPOOL). */
3319         svn_stringbuf_set(lastpath, curpath);
3320         lastrev = currev;
3321       }
3322
3323     /* Walk the predecessor links back to origin. */
3324     SVN_ERR(svn_fs_fs__node_id(&pred_id, curroot, lastpath->data, predidpool));
3325     do
3326       {
3327         svn_pool_clear(subpool);
3328         SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, subpool));
3329
3330         /* Why not just fetch the predecessor ID in PREDIDPOOL?
3331            Because svn_fs_fs__dag_get_predecessor_id() doesn't
3332            necessarily honor the passed-in pool, and might return a
3333            value cached in the node (which is allocated in
3334            SUBPOOL... maybe). */
3335         svn_pool_clear(predidpool);
3336         SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
3337         pred_id = pred_id ? svn_fs_fs__id_copy(pred_id, predidpool) : NULL;
3338       }
3339     while (pred_id);
3340
3341     /* When we get here, NODE should be the first node-revision in our
3342        chain. */
3343     SVN_ERR(svn_fs_fs__dag_get_revision(revision, node, pool));
3344
3345     /* Wow, I don't want to have to do all that again.  Let's cache
3346        the result. */
3347     if (node_id[0] != '_')
3348       SVN_ERR(svn_fs_fs__set_node_origin(fs, node_id,
3349                                          svn_fs_fs__dag_get_id(node), pool));
3350
3351     svn_pool_destroy(subpool);
3352     svn_pool_destroy(predidpool);
3353     return SVN_NO_ERROR;
3354   }
3355 }
3356
3357
3358 struct history_prev_args
3359 {
3360   svn_fs_history_t **prev_history_p;
3361   svn_fs_history_t *history;
3362   svn_boolean_t cross_copies;
3363   apr_pool_t *pool;
3364 };
3365
3366
3367 static svn_error_t *
3368 history_prev(void *baton, apr_pool_t *pool)
3369 {
3370   struct history_prev_args *args = baton;
3371   svn_fs_history_t **prev_history = args->prev_history_p;
3372   svn_fs_history_t *history = args->history;
3373   fs_history_data_t *fhd = history->fsap_data;
3374   const char *commit_path, *src_path, *path = fhd->path;
3375   svn_revnum_t commit_rev, src_rev, dst_rev;
3376   svn_revnum_t revision = fhd->revision;
3377   apr_pool_t *retpool = args->pool;
3378   svn_fs_t *fs = fhd->fs;
3379   parent_path_t *parent_path;
3380   dag_node_t *node;
3381   svn_fs_root_t *root;
3382   svn_boolean_t reported = fhd->is_interesting;
3383   svn_revnum_t copyroot_rev;
3384   const char *copyroot_path;
3385
3386   /* Initialize our return value. */
3387   *prev_history = NULL;
3388
3389   /* If our last history report left us hints about where to pickup
3390      the chase, then our last report was on the destination of a
3391      copy.  If we are crossing copies, start from those locations,
3392      otherwise, we're all done here.  */
3393   if (fhd->path_hint && SVN_IS_VALID_REVNUM(fhd->rev_hint))
3394     {
3395       reported = FALSE;
3396       if (! args->cross_copies)
3397         return SVN_NO_ERROR;
3398       path = fhd->path_hint;
3399       revision = fhd->rev_hint;
3400     }
3401
3402   /* Construct a ROOT for the current revision. */
3403   SVN_ERR(svn_fs_fs__revision_root(&root, fs, revision, pool));
3404
3405   /* Open PATH/REVISION, and get its node and a bunch of other
3406      goodies.  */
3407   SVN_ERR(open_path(&parent_path, root, path, 0, NULL, pool));
3408   node = parent_path->node;
3409   commit_path = svn_fs_fs__dag_get_created_path(node);
3410   SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, pool));
3411
3412   /* The Subversion filesystem is written in such a way that a given
3413      line of history may have at most one interesting history point
3414      per filesystem revision.  Either that node was edited (and
3415      possibly copied), or it was copied but not edited.  And a copy
3416      source cannot be from the same revision as its destination.  So,
3417      if our history revision matches its node's commit revision, we
3418      know that ... */
3419   if (revision == commit_rev)
3420     {
3421       if (! reported)
3422         {
3423           /* ... we either have not yet reported on this revision (and
3424              need now to do so) ... */
3425           *prev_history = assemble_history(fs,
3426                                            apr_pstrdup(retpool, commit_path),
3427                                            commit_rev, TRUE, NULL,
3428                                            SVN_INVALID_REVNUM, retpool);
3429           return SVN_NO_ERROR;
3430         }
3431       else
3432         {
3433           /* ... or we *have* reported on this revision, and must now
3434              progress toward this node's predecessor (unless there is
3435              no predecessor, in which case we're all done!). */
3436           const svn_fs_id_t *pred_id;
3437
3438           SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
3439           if (! pred_id)
3440             return SVN_NO_ERROR;
3441
3442           /* Replace NODE and friends with the information from its
3443              predecessor. */
3444           SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, pool));
3445           commit_path = svn_fs_fs__dag_get_created_path(node);
3446           SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, pool));
3447         }
3448     }
3449
3450   /* Find the youngest copyroot in the path of this node, including
3451      itself. */
3452   SVN_ERR(find_youngest_copyroot(&copyroot_rev, &copyroot_path, fs,
3453                                  parent_path, pool));
3454
3455   /* Initialize some state variables. */
3456   src_path = NULL;
3457   src_rev = SVN_INVALID_REVNUM;
3458   dst_rev = SVN_INVALID_REVNUM;
3459
3460   if (copyroot_rev > commit_rev)
3461     {
3462       const char *remainder_path;
3463       const char *copy_dst, *copy_src;
3464       svn_fs_root_t *copyroot_root;
3465
3466       SVN_ERR(svn_fs_fs__revision_root(&copyroot_root, fs, copyroot_rev,
3467                                        pool));
3468       SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, pool));
3469       copy_dst = svn_fs_fs__dag_get_created_path(node);
3470
3471       /* If our current path was the very destination of the copy,
3472          then our new current path will be the copy source.  If our
3473          current path was instead the *child* of the destination of
3474          the copy, then figure out its previous location by taking its
3475          path relative to the copy destination and appending that to
3476          the copy source.  Finally, if our current path doesn't meet
3477          one of these other criteria ... ### for now just fallback to
3478          the old copy hunt algorithm. */
3479       remainder_path = svn_fspath__skip_ancestor(copy_dst, path);
3480
3481       if (remainder_path)
3482         {
3483           /* If we get here, then our current path is the destination
3484              of, or the child of the destination of, a copy.  Fill
3485              in the return values and get outta here.  */
3486           SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(&src_rev, node));
3487           SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(&copy_src, node));
3488
3489           dst_rev = copyroot_rev;
3490           src_path = svn_fspath__join(copy_src, remainder_path, pool);
3491         }
3492     }
3493
3494   /* If we calculated a copy source path and revision, we'll make a
3495      'copy-style' history object. */
3496   if (src_path && SVN_IS_VALID_REVNUM(src_rev))
3497     {
3498       svn_boolean_t retry = FALSE;
3499
3500       /* It's possible for us to find a copy location that is the same
3501          as the history point we've just reported.  If that happens,
3502          we simply need to take another trip through this history
3503          search. */
3504       if ((dst_rev == revision) && reported)
3505         retry = TRUE;
3506
3507       *prev_history = assemble_history(fs, apr_pstrdup(retpool, path),
3508                                        dst_rev, ! retry,
3509                                        src_path, src_rev, retpool);
3510     }
3511   else
3512     {
3513       *prev_history = assemble_history(fs, apr_pstrdup(retpool, commit_path),
3514                                        commit_rev, TRUE, NULL,
3515                                        SVN_INVALID_REVNUM, retpool);
3516     }
3517
3518   return SVN_NO_ERROR;
3519 }
3520
3521
3522 /* Implement svn_fs_history_prev, set *PREV_HISTORY_P to a new
3523    svn_fs_history_t object that represents the predecessory of
3524    HISTORY.  If CROSS_COPIES is true, *PREV_HISTORY_P may be related
3525    only through a copy operation.  Perform all allocations in POOL. */
3526 static svn_error_t *
3527 fs_history_prev(svn_fs_history_t **prev_history_p,
3528                 svn_fs_history_t *history,
3529                 svn_boolean_t cross_copies,
3530                 apr_pool_t *pool)
3531 {
3532   svn_fs_history_t *prev_history = NULL;
3533   fs_history_data_t *fhd = history->fsap_data;
3534   svn_fs_t *fs = fhd->fs;
3535
3536   /* Special case: the root directory changes in every single
3537      revision, no exceptions.  And, the root can't be the target (or
3538      child of a target -- duh) of a copy.  So, if that's our path,
3539      then we need only decrement our revision by 1, and there you go. */
3540   if (strcmp(fhd->path, "/") == 0)
3541     {
3542       if (! fhd->is_interesting)
3543         prev_history = assemble_history(fs, "/", fhd->revision,
3544                                         1, NULL, SVN_INVALID_REVNUM, pool);
3545       else if (fhd->revision > 0)
3546         prev_history = assemble_history(fs, "/", fhd->revision - 1,
3547                                         1, NULL, SVN_INVALID_REVNUM, pool);
3548     }
3549   else
3550     {
3551       struct history_prev_args args;
3552       prev_history = history;
3553
3554       while (1)
3555         {
3556           args.prev_history_p = &prev_history;
3557           args.history = prev_history;
3558           args.cross_copies = cross_copies;
3559           args.pool = pool;
3560           SVN_ERR(history_prev(&args, pool));
3561
3562           if (! prev_history)
3563             break;
3564           fhd = prev_history->fsap_data;
3565           if (fhd->is_interesting)
3566             break;
3567         }
3568     }
3569
3570   *prev_history_p = prev_history;
3571   return SVN_NO_ERROR;
3572 }
3573
3574
3575 /* Set *PATH and *REVISION to the path and revision for the HISTORY
3576    object.  Use POOL for all allocations. */
3577 static svn_error_t *
3578 fs_history_location(const char **path,
3579                     svn_revnum_t *revision,
3580                     svn_fs_history_t *history,
3581                     apr_pool_t *pool)
3582 {
3583   fs_history_data_t *fhd = history->fsap_data;
3584
3585   *path = apr_pstrdup(pool, fhd->path);
3586   *revision = fhd->revision;
3587   return SVN_NO_ERROR;
3588 }
3589
3590 static history_vtable_t history_vtable = {
3591   fs_history_prev,
3592   fs_history_location
3593 };
3594
3595 /* Return a new history object (marked as "interesting") for PATH and
3596    REVISION, allocated in POOL, and with its members set to the values
3597    of the parameters provided.  Note that PATH and PATH_HINT are not
3598    duped into POOL -- it is the responsibility of the caller to ensure
3599    that this happens. */
3600 static svn_fs_history_t *
3601 assemble_history(svn_fs_t *fs,
3602                  const char *path,
3603                  svn_revnum_t revision,
3604                  svn_boolean_t is_interesting,
3605                  const char *path_hint,
3606                  svn_revnum_t rev_hint,
3607                  apr_pool_t *pool)
3608 {
3609   svn_fs_history_t *history = apr_pcalloc(pool, sizeof(*history));
3610   fs_history_data_t *fhd = apr_pcalloc(pool, sizeof(*fhd));
3611   fhd->path = svn_fs__canonicalize_abspath(path, pool);
3612   fhd->revision = revision;
3613   fhd->is_interesting = is_interesting;
3614   fhd->path_hint = path_hint;
3615   fhd->rev_hint = rev_hint;
3616   fhd->fs = fs;
3617
3618   history->vtable = &history_vtable;
3619   history->fsap_data = fhd;
3620   return history;
3621 }
3622
3623 \f
3624 /* mergeinfo queries */
3625
3626
3627 /* DIR_DAG is a directory DAG node which has mergeinfo in its
3628    descendants.  This function iterates over its children.  For each
3629    child with immediate mergeinfo, it adds its mergeinfo to
3630    RESULT_CATALOG.  appropriate arguments.  For each child with
3631    descendants with mergeinfo, it recurses.  Note that it does *not*
3632    call the action on the path for DIR_DAG itself.
3633
3634    POOL is used for temporary allocations, including the mergeinfo
3635    hashes passed to actions; RESULT_POOL is used for the mergeinfo added
3636    to RESULT_CATALOG.
3637  */
3638 static svn_error_t *
3639 crawl_directory_dag_for_mergeinfo(svn_fs_root_t *root,
3640                                   const char *this_path,
3641                                   dag_node_t *dir_dag,
3642                                   svn_mergeinfo_catalog_t result_catalog,
3643                                   apr_pool_t *result_pool,
3644                                   apr_pool_t *scratch_pool)
3645 {
3646   apr_hash_t *entries;
3647   apr_hash_index_t *hi;
3648   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3649
3650   SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, dir_dag,
3651                                      scratch_pool));
3652
3653   for (hi = apr_hash_first(scratch_pool, entries);
3654        hi;
3655        hi = apr_hash_next(hi))
3656     {
3657       svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
3658       const char *kid_path;
3659       dag_node_t *kid_dag;
3660       svn_boolean_t has_mergeinfo, go_down;
3661
3662       svn_pool_clear(iterpool);
3663
3664       kid_path = svn_fspath__join(this_path, dirent->name, iterpool);
3665       SVN_ERR(get_dag(&kid_dag, root, kid_path, iterpool));
3666
3667       SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, kid_dag));
3668       SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down, kid_dag));
3669
3670       if (has_mergeinfo)
3671         {
3672           /* Save this particular node's mergeinfo. */
3673           apr_hash_t *proplist;
3674           svn_mergeinfo_t kid_mergeinfo;
3675           svn_string_t *mergeinfo_string;
3676           svn_error_t *err;
3677
3678           SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, kid_dag, iterpool));
3679           mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
3680           if (!mergeinfo_string)
3681             {
3682               svn_string_t *idstr = svn_fs_fs__id_unparse(dirent->id, iterpool);
3683               return svn_error_createf
3684                 (SVN_ERR_FS_CORRUPT, NULL,
3685                  _("Node-revision #'%s' claims to have mergeinfo but doesn't"),
3686                  idstr->data);
3687             }
3688
3689           /* Issue #3896: If a node has syntactically invalid mergeinfo, then
3690              treat it as if no mergeinfo is present rather than raising a parse
3691              error. */
3692           err = svn_mergeinfo_parse(&kid_mergeinfo,
3693                                     mergeinfo_string->data,
3694                                     result_pool);
3695           if (err)
3696             {
3697               if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
3698                 svn_error_clear(err);
3699               else
3700                 return svn_error_trace(err);
3701               }
3702           else
3703             {
3704               svn_hash_sets(result_catalog, apr_pstrdup(result_pool, kid_path),
3705                             kid_mergeinfo);
3706             }
3707         }
3708
3709       if (go_down)
3710         SVN_ERR(crawl_directory_dag_for_mergeinfo(root,
3711                                                   kid_path,
3712                                                   kid_dag,
3713                                                   result_catalog,
3714                                                   result_pool,
3715                                                   iterpool));
3716     }
3717
3718   svn_pool_destroy(iterpool);
3719   return SVN_NO_ERROR;
3720 }
3721
3722 /* Return the cache key as a combination of REV_ROOT->REV, the inheritance
3723    flags INHERIT and ADJUST_INHERITED_MERGEINFO, and the PATH.  The result
3724    will be allocated in POOL..
3725  */
3726 static const char *
3727 mergeinfo_cache_key(const char *path,
3728                     svn_fs_root_t *rev_root,
3729                     svn_mergeinfo_inheritance_t inherit,
3730                     svn_boolean_t adjust_inherited_mergeinfo,
3731                     apr_pool_t *pool)
3732 {
3733   apr_int64_t number = rev_root->rev;
3734   number = number * 4
3735          + (inherit == svn_mergeinfo_nearest_ancestor ? 2 : 0)
3736          + (adjust_inherited_mergeinfo ? 1 : 0);
3737
3738   return svn_fs_fs__combine_number_and_string(number, path, pool);
3739 }
3740
3741 /* Calculates the mergeinfo for PATH under REV_ROOT using inheritance
3742    type INHERIT.  Returns it in *MERGEINFO, or NULL if there is none.
3743    The result is allocated in RESULT_POOL; SCRATCH_POOL is
3744    used for temporary allocations.
3745  */
3746 static svn_error_t *
3747 get_mergeinfo_for_path_internal(svn_mergeinfo_t *mergeinfo,
3748                                 svn_fs_root_t *rev_root,
3749                                 const char *path,
3750                                 svn_mergeinfo_inheritance_t inherit,
3751                                 svn_boolean_t adjust_inherited_mergeinfo,
3752                                 apr_pool_t *result_pool,
3753                                 apr_pool_t *scratch_pool)
3754 {
3755   parent_path_t *parent_path, *nearest_ancestor;
3756   apr_hash_t *proplist;
3757   svn_string_t *mergeinfo_string;
3758
3759   path = svn_fs__canonicalize_abspath(path, scratch_pool);
3760
3761   SVN_ERR(open_path(&parent_path, rev_root, path, 0, NULL, scratch_pool));
3762
3763   if (inherit == svn_mergeinfo_nearest_ancestor && ! parent_path->parent)
3764     return SVN_NO_ERROR;
3765
3766   if (inherit == svn_mergeinfo_nearest_ancestor)
3767     nearest_ancestor = parent_path->parent;
3768   else
3769     nearest_ancestor = parent_path;
3770
3771   while (TRUE)
3772     {
3773       svn_boolean_t has_mergeinfo;
3774
3775       SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo,
3776                                            nearest_ancestor->node));
3777       if (has_mergeinfo)
3778         break;
3779
3780       /* No need to loop if we're looking for explicit mergeinfo. */
3781       if (inherit == svn_mergeinfo_explicit)
3782         {
3783           return SVN_NO_ERROR;
3784         }
3785
3786       nearest_ancestor = nearest_ancestor->parent;
3787
3788       /* Run out?  There's no mergeinfo. */
3789       if (!nearest_ancestor)
3790         {
3791           return SVN_NO_ERROR;
3792         }
3793     }
3794
3795   SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, nearest_ancestor->node,
3796                                       scratch_pool));
3797   mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
3798   if (!mergeinfo_string)
3799     return svn_error_createf
3800       (SVN_ERR_FS_CORRUPT, NULL,
3801        _("Node-revision '%s@%ld' claims to have mergeinfo but doesn't"),
3802        parent_path_path(nearest_ancestor, scratch_pool), rev_root->rev);
3803
3804   /* Parse the mergeinfo; store the result in *MERGEINFO. */
3805   {
3806     /* Issue #3896: If a node has syntactically invalid mergeinfo, then
3807        treat it as if no mergeinfo is present rather than raising a parse
3808        error. */
3809     svn_error_t *err = svn_mergeinfo_parse(mergeinfo,
3810                                            mergeinfo_string->data,
3811                                            result_pool);
3812     if (err)
3813       {
3814         if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
3815           {
3816             svn_error_clear(err);
3817             err = NULL;
3818             *mergeinfo = NULL;
3819           }
3820         return svn_error_trace(err);
3821       }
3822   }
3823
3824   /* If our nearest ancestor is the very path we inquired about, we
3825      can return the mergeinfo results directly.  Otherwise, we're
3826      inheriting the mergeinfo, so we need to a) remove non-inheritable
3827      ranges and b) telescope the merged-from paths. */
3828   if (adjust_inherited_mergeinfo && (nearest_ancestor != parent_path))
3829     {
3830       svn_mergeinfo_t tmp_mergeinfo;
3831
3832       SVN_ERR(svn_mergeinfo_inheritable2(&tmp_mergeinfo, *mergeinfo,
3833                                          NULL, SVN_INVALID_REVNUM,
3834                                          SVN_INVALID_REVNUM, TRUE,
3835                                          scratch_pool, scratch_pool));
3836       SVN_ERR(svn_fs__append_to_merged_froms(mergeinfo, tmp_mergeinfo,
3837                                              parent_path_relpath(
3838                                                parent_path, nearest_ancestor,
3839                                                scratch_pool),
3840                                              result_pool));
3841     }
3842
3843   return SVN_NO_ERROR;
3844 }
3845
3846 /* Caching wrapper around get_mergeinfo_for_path_internal().
3847  */
3848 static svn_error_t *
3849 get_mergeinfo_for_path(svn_mergeinfo_t *mergeinfo,
3850                        svn_fs_root_t *rev_root,
3851                        const char *path,
3852                        svn_mergeinfo_inheritance_t inherit,
3853                        svn_boolean_t adjust_inherited_mergeinfo,
3854                        apr_pool_t *result_pool,
3855                        apr_pool_t *scratch_pool)
3856 {
3857   fs_fs_data_t *ffd = rev_root->fs->fsap_data;
3858   const char *cache_key;
3859   svn_boolean_t found = FALSE;
3860   svn_stringbuf_t *mergeinfo_exists;
3861
3862   *mergeinfo = NULL;
3863
3864   cache_key = mergeinfo_cache_key(path, rev_root, inherit,
3865                                   adjust_inherited_mergeinfo, scratch_pool);
3866   if (ffd->mergeinfo_existence_cache)
3867     {
3868       SVN_ERR(svn_cache__get((void **)&mergeinfo_exists, &found,
3869                              ffd->mergeinfo_existence_cache,
3870                              cache_key, result_pool));
3871       if (found && mergeinfo_exists->data[0] == '1')
3872         SVN_ERR(svn_cache__get((void **)mergeinfo, &found,
3873                               ffd->mergeinfo_cache,
3874                               cache_key, result_pool));
3875     }
3876
3877   if (! found)
3878     {
3879       SVN_ERR(get_mergeinfo_for_path_internal(mergeinfo, rev_root, path,
3880                                               inherit,
3881                                               adjust_inherited_mergeinfo,
3882                                               result_pool, scratch_pool));
3883       if (ffd->mergeinfo_existence_cache)
3884         {
3885           mergeinfo_exists = svn_stringbuf_create(*mergeinfo ? "1" : "0",
3886                                                   scratch_pool);
3887           SVN_ERR(svn_cache__set(ffd->mergeinfo_existence_cache,
3888                                  cache_key, mergeinfo_exists, scratch_pool));
3889           if (*mergeinfo)
3890             SVN_ERR(svn_cache__set(ffd->mergeinfo_cache,
3891                                   cache_key, *mergeinfo, scratch_pool));
3892         }
3893     }
3894
3895   return SVN_NO_ERROR;
3896 }
3897
3898 /* Adds mergeinfo for each descendant of PATH (but not PATH itself)
3899    under ROOT to RESULT_CATALOG.  Returned values are allocated in
3900    RESULT_POOL; temporary values in POOL. */
3901 static svn_error_t *
3902 add_descendant_mergeinfo(svn_mergeinfo_catalog_t result_catalog,
3903                          svn_fs_root_t *root,
3904                          const char *path,
3905                          apr_pool_t *result_pool,
3906                          apr_pool_t *scratch_pool)
3907 {
3908   dag_node_t *this_dag;
3909   svn_boolean_t go_down;
3910
3911   SVN_ERR(get_dag(&this_dag, root, path, scratch_pool));
3912   SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down,
3913                                                         this_dag));
3914   if (go_down)
3915     SVN_ERR(crawl_directory_dag_for_mergeinfo(root,
3916                                               path,
3917                                               this_dag,
3918                                               result_catalog,
3919                                               result_pool,
3920                                               scratch_pool));
3921   return SVN_NO_ERROR;
3922 }
3923
3924
3925 /* Get the mergeinfo for a set of paths, returned in
3926    *MERGEINFO_CATALOG.  Returned values are allocated in
3927    POOL, while temporary values are allocated in a sub-pool. */
3928 static svn_error_t *
3929 get_mergeinfos_for_paths(svn_fs_root_t *root,
3930                          svn_mergeinfo_catalog_t *mergeinfo_catalog,
3931                          const apr_array_header_t *paths,
3932                          svn_mergeinfo_inheritance_t inherit,
3933                          svn_boolean_t include_descendants,
3934                          svn_boolean_t adjust_inherited_mergeinfo,
3935                          apr_pool_t *result_pool,
3936                          apr_pool_t *scratch_pool)
3937 {
3938   svn_mergeinfo_catalog_t result_catalog = svn_hash__make(result_pool);
3939   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3940   int i;
3941
3942   for (i = 0; i < paths->nelts; i++)
3943     {
3944       svn_error_t *err;
3945       svn_mergeinfo_t path_mergeinfo;
3946       const char *path = APR_ARRAY_IDX(paths, i, const char *);
3947
3948       svn_pool_clear(iterpool);
3949
3950       err = get_mergeinfo_for_path(&path_mergeinfo, root, path,
3951                                    inherit, adjust_inherited_mergeinfo,
3952                                    result_pool, iterpool);
3953       if (err)
3954         {
3955           if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
3956             {
3957               svn_error_clear(err);
3958               err = NULL;
3959               path_mergeinfo = NULL;
3960             }
3961           else
3962             {
3963               return svn_error_trace(err);
3964             }
3965         }
3966
3967       if (path_mergeinfo)
3968         svn_hash_sets(result_catalog, path, path_mergeinfo);
3969       if (include_descendants)
3970         SVN_ERR(add_descendant_mergeinfo(result_catalog, root, path,
3971                                          result_pool, scratch_pool));
3972     }
3973   svn_pool_destroy(iterpool);
3974
3975   *mergeinfo_catalog = result_catalog;
3976   return SVN_NO_ERROR;
3977 }
3978
3979
3980 /* Implements svn_fs_get_mergeinfo. */
3981 static svn_error_t *
3982 fs_get_mergeinfo(svn_mergeinfo_catalog_t *catalog,
3983                  svn_fs_root_t *root,
3984                  const apr_array_header_t *paths,
3985                  svn_mergeinfo_inheritance_t inherit,
3986                  svn_boolean_t include_descendants,
3987                  svn_boolean_t adjust_inherited_mergeinfo,
3988                  apr_pool_t *result_pool,
3989                  apr_pool_t *scratch_pool)
3990 {
3991   fs_fs_data_t *ffd = root->fs->fsap_data;
3992
3993   /* We require a revision root. */
3994   if (root->is_txn_root)
3995     return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
3996
3997   /* We have to actually be able to find the mergeinfo metadata! */
3998   if (! svn_fs_fs__fs_supports_mergeinfo(root->fs))
3999     return svn_error_createf
4000       (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
4001        _("Querying mergeinfo requires version %d of the FSFS filesystem "
4002          "schema; filesystem '%s' uses only version %d"),
4003        SVN_FS_FS__MIN_MERGEINFO_FORMAT, root->fs->path, ffd->format);
4004
4005   /* Retrieve a path -> mergeinfo hash mapping. */
4006   return get_mergeinfos_for_paths(root, catalog, paths,
4007                                   inherit,
4008                                   include_descendants,
4009                                   adjust_inherited_mergeinfo,
4010                                   result_pool, scratch_pool);
4011 }
4012
4013
4014 /* The vtable associated with root objects. */
4015 static root_vtable_t root_vtable = {
4016   fs_paths_changed,
4017   svn_fs_fs__check_path,
4018   fs_node_history,
4019   svn_fs_fs__node_id,
4020   svn_fs_fs__node_created_rev,
4021   fs_node_origin_rev,
4022   fs_node_created_path,
4023   fs_delete_node,
4024   fs_copied_from,
4025   fs_closest_copy,
4026   fs_node_prop,
4027   fs_node_proplist,
4028   fs_change_node_prop,
4029   fs_props_changed,
4030   fs_dir_entries,
4031   fs_make_dir,
4032   fs_copy,
4033   fs_revision_link,
4034   fs_file_length,
4035   fs_file_checksum,
4036   fs_file_contents,
4037   fs_try_process_file_contents,
4038   fs_make_file,
4039   fs_apply_textdelta,
4040   fs_apply_text,
4041   fs_contents_changed,
4042   fs_get_file_delta_stream,
4043   fs_merge,
4044   fs_get_mergeinfo,
4045 };
4046
4047 /* Construct a new root object in FS, allocated from POOL.  */
4048 static svn_fs_root_t *
4049 make_root(svn_fs_t *fs,
4050           apr_pool_t *pool)
4051 {
4052   svn_fs_root_t *root = apr_pcalloc(pool, sizeof(*root));
4053
4054   root->fs = fs;
4055   root->pool = pool;
4056   root->vtable = &root_vtable;
4057
4058   return root;
4059 }
4060
4061
4062 /* Construct a root object referring to the root of REVISION in FS,
4063    whose root directory is ROOT_DIR.  Create the new root in POOL.  */
4064 static svn_fs_root_t *
4065 make_revision_root(svn_fs_t *fs,
4066                    svn_revnum_t rev,
4067                    dag_node_t *root_dir,
4068                    apr_pool_t *pool)
4069 {
4070   svn_fs_root_t *root = make_root(fs, pool);
4071   fs_rev_root_data_t *frd = apr_pcalloc(root->pool, sizeof(*frd));
4072
4073   root->is_txn_root = FALSE;
4074   root->rev = rev;
4075
4076   frd->root_dir = root_dir;
4077   frd->copyfrom_cache = svn_hash__make(root->pool);
4078
4079   root->fsap_data = frd;
4080
4081   return root;
4082 }
4083
4084
4085 /* Construct a root object referring to the root of the transaction
4086    named TXN and based on revision BASE_REV in FS, with FLAGS to
4087    describe transaction's behavior.  Create the new root in POOL.  */
4088 static svn_error_t *
4089 make_txn_root(svn_fs_root_t **root_p,
4090               svn_fs_t *fs,
4091               const char *txn,
4092               svn_revnum_t base_rev,
4093               apr_uint32_t flags,
4094               apr_pool_t *pool)
4095 {
4096   svn_fs_root_t *root = make_root(fs, pool);
4097   fs_txn_root_data_t *frd = apr_pcalloc(root->pool, sizeof(*frd));
4098
4099   root->is_txn_root = TRUE;
4100   root->txn = apr_pstrdup(root->pool, txn);
4101   root->txn_flags = flags;
4102   root->rev = base_rev;
4103
4104   frd->txn_id = txn;
4105
4106   /* Because this cache actually tries to invalidate elements, keep
4107      the number of elements per page down.
4108
4109      Note that since dag_node_cache_invalidate uses svn_cache__iter,
4110      this *cannot* be a memcache-based cache.  */
4111   SVN_ERR(svn_cache__create_inprocess(&(frd->txn_node_cache),
4112                                       svn_fs_fs__dag_serialize,
4113                                       svn_fs_fs__dag_deserialize,
4114                                       APR_HASH_KEY_STRING,
4115                                       32, 20, FALSE,
4116                                       apr_pstrcat(pool, txn, ":TXN",
4117                                                   (char *)NULL),
4118                                       root->pool));
4119
4120   /* Initialize transaction-local caches in FS.
4121
4122      Note that we cannot put those caches in frd because that content
4123      fs root object is not available where we would need it. */
4124   SVN_ERR(svn_fs_fs__initialize_txn_caches(fs, txn, pool));
4125
4126   root->fsap_data = frd;
4127
4128   *root_p = root;
4129   return SVN_NO_ERROR;
4130 }
4131
4132
4133 \f
4134 /* Verify. */
4135 static APR_INLINE const char *
4136 stringify_node(dag_node_t *node,
4137                apr_pool_t *pool)
4138 {
4139   /* ### TODO: print some PATH@REV to it, too. */
4140   return svn_fs_fs__id_unparse(svn_fs_fs__dag_get_id(node), pool)->data;
4141 }
4142
4143 /* Check metadata sanity on NODE, and on its children.  Manually verify
4144    information for DAG nodes in revision REV, and trust the metadata
4145    accuracy for nodes belonging to older revisions. */
4146 static svn_error_t *
4147 verify_node(dag_node_t *node,
4148             svn_revnum_t rev,
4149             apr_pool_t *pool)
4150 {
4151   svn_boolean_t has_mergeinfo;
4152   apr_int64_t mergeinfo_count;
4153   const svn_fs_id_t *pred_id;
4154   svn_fs_t *fs = svn_fs_fs__dag_get_fs(node);
4155   int pred_count;
4156   svn_node_kind_t kind;
4157   apr_pool_t *iterpool = svn_pool_create(pool);
4158
4159   /* Fetch some data. */
4160   SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, node));
4161   SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_count, node));
4162   SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
4163   SVN_ERR(svn_fs_fs__dag_get_predecessor_count(&pred_count, node));
4164   kind = svn_fs_fs__dag_node_kind(node);
4165
4166   /* Sanity check. */
4167   if (mergeinfo_count < 0)
4168     return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4169                              "Negative mergeinfo-count %" APR_INT64_T_FMT
4170                              " on node '%s'",
4171                              mergeinfo_count, stringify_node(node, iterpool));
4172
4173   /* Issue #4129. (This check will explicitly catch non-root instances too.) */
4174   if (pred_id)
4175     {
4176       dag_node_t *pred;
4177       int pred_pred_count;
4178       SVN_ERR(svn_fs_fs__dag_get_node(&pred, fs, pred_id, iterpool));
4179       SVN_ERR(svn_fs_fs__dag_get_predecessor_count(&pred_pred_count, pred));
4180       if (pred_pred_count+1 != pred_count)
4181         return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4182                                  "Predecessor count mismatch: "
4183                                  "%s has %d, but %s has %d",
4184                                  stringify_node(node, iterpool), pred_count,
4185                                  stringify_node(pred, iterpool),
4186                                  pred_pred_count);
4187     }
4188
4189   /* Kind-dependent verifications. */
4190   if (kind == svn_node_none)
4191     {
4192       return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4193                                "Node '%s' has kind 'none'",
4194                                stringify_node(node, iterpool));
4195     }
4196   if (kind == svn_node_file)
4197     {
4198       if (has_mergeinfo != mergeinfo_count) /* comparing int to bool */
4199         return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4200                                  "File node '%s' has inconsistent mergeinfo: "
4201                                  "has_mergeinfo=%d, "
4202                                  "mergeinfo_count=%" APR_INT64_T_FMT,
4203                                  stringify_node(node, iterpool),
4204                                  has_mergeinfo, mergeinfo_count);
4205     }
4206   if (kind == svn_node_dir)
4207     {
4208       apr_hash_t *entries;
4209       apr_hash_index_t *hi;
4210       apr_int64_t children_mergeinfo = 0;
4211
4212       SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool));
4213
4214       /* Compute CHILDREN_MERGEINFO. */
4215       for (hi = apr_hash_first(pool, entries);
4216            hi;
4217            hi = apr_hash_next(hi))
4218         {
4219           svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
4220           dag_node_t *child;
4221           svn_revnum_t child_rev;
4222           apr_int64_t child_mergeinfo;
4223
4224           svn_pool_clear(iterpool);
4225
4226           /* Compute CHILD_REV. */
4227           SVN_ERR(svn_fs_fs__dag_get_node(&child, fs, dirent->id, iterpool));
4228           SVN_ERR(svn_fs_fs__dag_get_revision(&child_rev, child, iterpool));
4229
4230           if (child_rev == rev)
4231             SVN_ERR(verify_node(child, rev, iterpool));
4232
4233           SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&child_mergeinfo, child));
4234           children_mergeinfo += child_mergeinfo;
4235         }
4236
4237       /* Side-effect of issue #4129. */
4238       if (children_mergeinfo+has_mergeinfo != mergeinfo_count)
4239         return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4240                                  "Mergeinfo-count discrepancy on '%s': "
4241                                  "expected %" APR_INT64_T_FMT "+%d, "
4242                                  "counted %" APR_INT64_T_FMT,
4243                                  stringify_node(node, iterpool),
4244                                  mergeinfo_count, has_mergeinfo,
4245                                  children_mergeinfo);
4246     }
4247
4248   svn_pool_destroy(iterpool);
4249   return SVN_NO_ERROR;
4250 }
4251
4252 svn_error_t *
4253 svn_fs_fs__verify_root(svn_fs_root_t *root,
4254                        apr_pool_t *pool)
4255 {
4256   svn_fs_t *fs = root->fs;
4257   dag_node_t *root_dir;
4258
4259   /* Issue #4129: bogus pred-counts and minfo-cnt's on the root node-rev
4260      (and elsewhere).  This code makes more thorough checks than the
4261      commit-time checks in validate_root_noderev(). */
4262
4263   /* Callers should disable caches by setting SVN_FS_CONFIG_FSFS_CACHE_NS;
4264      see r1462436.
4265
4266      When this code is called in the library, we want to ensure we
4267      use the on-disk data --- rather than some data that was read
4268      in the possibly-distance past and cached since. */
4269
4270   if (root->is_txn_root)
4271     {
4272       fs_txn_root_data_t *frd = root->fsap_data;
4273       SVN_ERR(svn_fs_fs__dag_txn_root(&root_dir, fs, frd->txn_id, pool));
4274     }
4275   else
4276     {
4277       fs_rev_root_data_t *frd = root->fsap_data;
4278       root_dir = frd->root_dir;
4279     }
4280
4281   /* Recursively verify ROOT_DIR. */
4282   SVN_ERR(verify_node(root_dir, root->rev, pool));
4283
4284   /* Verify explicitly the predecessor of the root. */
4285   {
4286     const svn_fs_id_t *pred_id;
4287
4288     /* Only r0 should have no predecessor. */
4289     SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, root_dir));
4290     if (! root->is_txn_root && !!pred_id != !!root->rev)
4291       return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4292                                "r%ld's root node's predecessor is "
4293                                "unexpectedly '%s'",
4294                                root->rev,
4295                                (pred_id
4296                                 ? svn_fs_fs__id_unparse(pred_id, pool)->data
4297                                 : "(null)"));
4298     if (root->is_txn_root && !pred_id)
4299       return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4300                                "Transaction '%s''s root node's predecessor is "
4301                                "unexpectedly NULL",
4302                                root->txn);
4303
4304     /* Check the predecessor's revision. */
4305     if (pred_id)
4306       {
4307         svn_revnum_t pred_rev = svn_fs_fs__id_rev(pred_id);
4308         if (! root->is_txn_root && pred_rev+1 != root->rev)
4309           /* Issue #4129. */
4310           return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4311                                    "r%ld's root node's predecessor is r%ld"
4312                                    " but should be r%ld",
4313                                    root->rev, pred_rev, root->rev - 1);
4314         if (root->is_txn_root && pred_rev != root->rev)
4315           return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4316                                    "Transaction '%s''s root node's predecessor"
4317                                    " is r%ld"
4318                                    " but should be r%ld",
4319                                    root->txn, pred_rev, root->rev);
4320       }
4321   }
4322
4323   return SVN_NO_ERROR;
4324 }