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