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