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