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