]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/subversion/subversion/libsvn_fs_base/tree.c
MFV r348573: 9993 zil writes can get delayed in zio pipeline
[FreeBSD/FreeBSD.git] / contrib / subversion / subversion / libsvn_fs_base / 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 "svn_private_config.h"
42 #include "svn_hash.h"
43 #include "svn_pools.h"
44 #include "svn_error.h"
45 #include "svn_path.h"
46 #include "svn_mergeinfo.h"
47 #include "svn_fs.h"
48 #include "svn_sorts.h"
49 #include "svn_checksum.h"
50 #include "fs.h"
51 #include "err.h"
52 #include "trail.h"
53 #include "node-rev.h"
54 #include "key-gen.h"
55 #include "dag.h"
56 #include "tree.h"
57 #include "lock.h"
58 #include "revs-txns.h"
59 #include "id.h"
60 #include "bdb/txn-table.h"
61 #include "bdb/rev-table.h"
62 #include "bdb/nodes-table.h"
63 #include "bdb/changes-table.h"
64 #include "bdb/copies-table.h"
65 #include "bdb/node-origins-table.h"
66 #include "bdb/miscellaneous-table.h"
67 #include "../libsvn_fs/fs-loader.h"
68 #include "private/svn_fspath.h"
69 #include "private/svn_fs_util.h"
70 #include "private/svn_mergeinfo_private.h"
71 #include "private/svn_sorts_private.h"
72
73
74 /* ### I believe this constant will become internal to reps-strings.c.
75    ### see the comment in window_consumer() for more information. */
76
77 /* ### the comment also seems to need tweaking: the log file stuff
78    ### is no longer an issue... */
79 /* Data written to the filesystem through the svn_fs_apply_textdelta()
80    interface is cached in memory until the end of the data stream, or
81    until a size trigger is hit.  Define that trigger here (in bytes).
82    Setting the value to 0 will result in no filesystem buffering at
83    all.  The value only really matters when dealing with file contents
84    bigger than the value itself.  Above that point, large values here
85    allow the filesystem to buffer more data in memory before flushing
86    to the database, which increases memory usage but greatly decreases
87    the amount of disk access (and log-file generation) in database.
88    Smaller values will limit your overall memory consumption, but can
89    drastically hurt throughput by necessitating more write operations
90    to the database (which also generates more log-files).  */
91 #define WRITE_BUFFER_SIZE          512000
92
93 /* The maximum number of cache items to maintain in the node cache. */
94 #define NODE_CACHE_MAX_KEYS        32
95
96
97 \f
98 /* The root structure.  */
99
100 /* Structure for svn_fs_root_t's node_cache hash values. */
101 struct dag_node_cache_t
102 {
103   dag_node_t *node; /* NODE to be cached. */
104   int idx;          /* Index into the keys array for this cache item's key. */
105   apr_pool_t *pool; /* Pool in which NODE is allocated. */
106 };
107
108
109 typedef struct base_root_data_t
110 {
111
112   /* For revision roots, this is a dag node for the revision's root
113      directory.  For transaction roots, we open the root directory
114      afresh every time, since the root may have been cloned, or
115      the transaction may have disappeared altogether.  */
116   dag_node_t *root_dir;
117
118   /* Cache structures, for mapping const char * PATH to const
119      struct dag_node_cache_t * structures.
120
121      ### Currently this is only used for revision roots.  To be safe
122      for transaction roots, you must have the guarantee that there is
123      never more than a single transaction root per Subversion
124      transaction ever open at a given time -- having two roots open to
125      the same Subversion transaction would be a request for pain.
126      Also, you have to ensure that if a 'make_path_mutable()' fails for
127      any reason, you don't leave cached nodes for the portion of that
128      function that succeeded.  In other words, this cache must never,
129      ever, lie. */
130   apr_hash_t *node_cache;
131   const char *node_cache_keys[NODE_CACHE_MAX_KEYS];
132   int node_cache_idx;
133 } base_root_data_t;
134
135
136 static svn_fs_root_t *make_revision_root(svn_fs_t *fs, svn_revnum_t rev,
137                                          dag_node_t *root_dir,
138                                          apr_pool_t *pool);
139
140 static svn_fs_root_t *make_txn_root(svn_fs_t *fs, const char *txn,
141                                     svn_revnum_t base_rev, apr_uint32_t flags,
142                                     apr_pool_t *pool);
143
144 \f
145 /*** Node Caching in the Roots. ***/
146
147 /* Return NODE for PATH from ROOT's node cache, or NULL if the node
148    isn't cached. */
149 static dag_node_t *
150 dag_node_cache_get(svn_fs_root_t *root,
151                    const char *path,
152                    apr_pool_t *pool)
153 {
154   base_root_data_t *brd = root->fsap_data;
155   struct dag_node_cache_t *cache_item;
156
157   /* Assert valid input. */
158   assert(*path == '/');
159
160   /* Only allow revision roots. */
161   if (root->is_txn_root)
162     return NULL;
163
164   /* Look in the cache for our desired item. */
165   cache_item = svn_hash_gets(brd->node_cache, path);
166   if (cache_item)
167     return svn_fs_base__dag_dup(cache_item->node, pool);
168
169   return NULL;
170 }
171
172
173 /* Add the NODE for PATH to ROOT's node cache.  Callers should *NOT*
174    call this unless they are adding a currently un-cached item to the
175    cache, or are replacing the NODE for PATH with a new (different)
176    one. */
177 static void
178 dag_node_cache_set(svn_fs_root_t *root,
179                    const char *path,
180                    dag_node_t *node)
181 {
182   base_root_data_t *brd = root->fsap_data;
183   const char *cache_path;
184   apr_pool_t *cache_pool;
185   struct dag_node_cache_t *cache_item;
186   int num_keys = apr_hash_count(brd->node_cache);
187
188   /* What?  No POOL passed to this function?
189
190      To ensure that our cache values live as long as the svn_fs_root_t
191      in which they are ultimately stored, and to allow us to free()
192      them individually without harming the rest, they are each
193      allocated from a subpool of ROOT's pool.  We'll keep one subpool
194      around for each cache slot -- as we start expiring stuff
195      to make room for more entries, we'll re-use the expired thing's
196      pool. */
197
198   /* Assert valid input and state. */
199   assert(*path == '/');
200   assert((brd->node_cache_idx <= num_keys)
201          && (num_keys <= NODE_CACHE_MAX_KEYS));
202
203   /* Only allow revision roots. */
204   if (root->is_txn_root)
205     return;
206
207   /* Special case: the caller wants us to replace an existing cached
208      node with a new one.  If the callers aren't mindless, this should
209      only happen when a node is made mutable under a transaction
210      root, and that only happens once under that root.  So, we'll be a
211      little bit sloppy here, and count on callers doing the right
212      thing. */
213   cache_item = svn_hash_gets(brd->node_cache, path);
214   if (cache_item)
215     {
216       /* ### This section is somehow broken.  I don't know how, but it
217          ### is.  And I don't want to spend any more time on it.  So,
218          ### callers, use only revision root and don't try to update
219          ### an already-cached thing.  -- cmpilato */
220       SVN_ERR_MALFUNCTION_NO_RETURN();
221
222 #if 0
223       int cache_index = cache_item->idx;
224       cache_path = brd->node_cache_keys[cache_index];
225       cache_pool = cache_item->pool;
226       cache_item->node = svn_fs_base__dag_dup(node, cache_pool);
227
228       /* Now, move the cache key reference to the end of the keys in
229          the keys array (unless it's already at the end).  ### Yes,
230          it's a memmove(), but we're not talking about pages of memory
231          here. */
232       if (cache_index != (num_keys - 1))
233         {
234           int move_num = NODE_CACHE_MAX_KEYS - cache_index - 1;
235           memmove(brd->node_cache_keys + cache_index,
236                   brd->node_cache_keys + cache_index + 1,
237                   move_num * sizeof(const char *));
238           cache_index = num_keys - 1;
239           brd->node_cache_keys[cache_index] = cache_path;
240         }
241
242       /* Advance the cache pointers. */
243       cache_item->idx = cache_index;
244       brd->node_cache_idx = (cache_index + 1) % NODE_CACHE_MAX_KEYS;
245       return;
246 #endif
247     }
248
249   /* We're adding a new cache item.  First, see if we have room for it
250      (otherwise, make some room). */
251   if (apr_hash_count(brd->node_cache) == NODE_CACHE_MAX_KEYS)
252     {
253       /* No room.  Expire the oldest thing. */
254       cache_path = brd->node_cache_keys[brd->node_cache_idx];
255       cache_item = svn_hash_gets(brd->node_cache, cache_path);
256       svn_hash_sets(brd->node_cache, cache_path, NULL);
257       cache_pool = cache_item->pool;
258       svn_pool_clear(cache_pool);
259     }
260   else
261     {
262       cache_pool = svn_pool_create(root->pool);
263     }
264
265   /* Make the cache item, allocated in its own pool. */
266   cache_item = apr_palloc(cache_pool, sizeof(*cache_item));
267   cache_item->node = svn_fs_base__dag_dup(node, cache_pool);
268   cache_item->idx = brd->node_cache_idx;
269   cache_item->pool = cache_pool;
270
271   /* Now add it to the cache. */
272   cache_path = apr_pstrdup(cache_pool, path);
273   svn_hash_sets(brd->node_cache, cache_path, cache_item);
274   brd->node_cache_keys[brd->node_cache_idx] = cache_path;
275
276   /* Advance the cache pointer. */
277   brd->node_cache_idx = (brd->node_cache_idx + 1) % NODE_CACHE_MAX_KEYS;
278 }
279
280
281
282 \f
283 /* Creating transaction and revision root nodes.  */
284
285 struct txn_root_args
286 {
287   svn_fs_root_t **root_p;
288   svn_fs_txn_t *txn;
289 };
290
291
292 static svn_error_t *
293 txn_body_txn_root(void *baton,
294                   trail_t *trail)
295 {
296   struct txn_root_args *args = baton;
297   svn_fs_root_t **root_p = args->root_p;
298   svn_fs_txn_t *txn = args->txn;
299   svn_fs_t *fs = txn->fs;
300   const char *svn_txn_id = txn->id;
301   const svn_fs_id_t *root_id, *base_root_id;
302   svn_fs_root_t *root;
303   apr_hash_t *txnprops;
304   apr_uint32_t flags = 0;
305
306   /* Verify that the transaction actually exists.  */
307   SVN_ERR(svn_fs_base__get_txn_ids(&root_id, &base_root_id, fs,
308                                    svn_txn_id, trail, trail->pool));
309
310   /* Look for special txn props that represent the 'flags' behavior of
311      the transaction. */
312   SVN_ERR(svn_fs_base__txn_proplist_in_trail(&txnprops, svn_txn_id, trail));
313   if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD))
314     flags |= SVN_FS_TXN_CHECK_OOD;
315
316   if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS))
317     flags |= SVN_FS_TXN_CHECK_LOCKS;
318
319   root = make_txn_root(fs, svn_txn_id, txn->base_rev, flags, trail->pool);
320
321   *root_p = root;
322   return SVN_NO_ERROR;
323 }
324
325
326 svn_error_t *
327 svn_fs_base__txn_root(svn_fs_root_t **root_p,
328                       svn_fs_txn_t *txn,
329                       apr_pool_t *pool)
330 {
331   svn_fs_root_t *root;
332   struct txn_root_args args;
333
334   args.root_p = &root;
335   args.txn = txn;
336   SVN_ERR(svn_fs_base__retry_txn(txn->fs, txn_body_txn_root, &args,
337                                  FALSE, pool));
338
339   *root_p = root;
340   return SVN_NO_ERROR;
341 }
342
343
344 struct revision_root_args
345 {
346   svn_fs_root_t **root_p;
347   svn_revnum_t rev;
348 };
349
350
351 static svn_error_t *
352 txn_body_revision_root(void *baton,
353                        trail_t *trail)
354 {
355   struct revision_root_args *args = baton;
356   dag_node_t *root_dir;
357   svn_fs_root_t *root;
358
359   SVN_ERR(svn_fs_base__dag_revision_root(&root_dir, trail->fs, args->rev,
360                                          trail, trail->pool));
361   root = make_revision_root(trail->fs, args->rev, root_dir, trail->pool);
362
363   *args->root_p = root;
364   return SVN_NO_ERROR;
365 }
366
367
368 svn_error_t *
369 svn_fs_base__revision_root(svn_fs_root_t **root_p,
370                            svn_fs_t *fs,
371                            svn_revnum_t rev,
372                            apr_pool_t *pool)
373 {
374   struct revision_root_args args;
375   svn_fs_root_t *root;
376
377   SVN_ERR(svn_fs__check_fs(fs, TRUE));
378
379   args.root_p = &root;
380   args.rev = rev;
381   SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_revision_root, &args,
382                                  FALSE, pool));
383
384   *root_p = root;
385   return SVN_NO_ERROR;
386 }
387
388
389 \f
390 /* Getting dag nodes for roots.  */
391
392
393 /* Set *NODE_P to a freshly opened dag node referring to the root
394    directory of ROOT, as part of TRAIL.  */
395 static svn_error_t *
396 root_node(dag_node_t **node_p,
397           svn_fs_root_t *root,
398           trail_t *trail,
399           apr_pool_t *pool)
400 {
401   base_root_data_t *brd = root->fsap_data;
402
403   if (! root->is_txn_root)
404     {
405       /* It's a revision root, so we already have its root directory
406          opened.  */
407       *node_p = svn_fs_base__dag_dup(brd->root_dir, pool);
408       return SVN_NO_ERROR;
409     }
410   else
411     {
412       /* It's a transaction root.  Open a fresh copy.  */
413       return svn_fs_base__dag_txn_root(node_p, root->fs, root->txn,
414                                        trail, pool);
415     }
416 }
417
418
419 /* Set *NODE_P to a mutable root directory for ROOT, cloning if
420    necessary, as part of TRAIL.  ROOT must be a transaction root.  Use
421    ERROR_PATH in error messages.  */
422 static svn_error_t *
423 mutable_root_node(dag_node_t **node_p,
424                   svn_fs_root_t *root,
425                   const char *error_path,
426                   trail_t *trail,
427                   apr_pool_t *pool)
428 {
429   if (root->is_txn_root)
430     return svn_fs_base__dag_clone_root(node_p, root->fs, root->txn,
431                                        trail, pool);
432   else
433     /* If it's not a transaction root, we can't change its contents.  */
434     return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path);
435 }
436
437
438 \f
439 /* Traversing directory paths.  */
440
441 typedef enum copy_id_inherit_t
442 {
443   copy_id_inherit_unknown = 0,
444   copy_id_inherit_self,
445   copy_id_inherit_parent,
446   copy_id_inherit_new
447
448 } copy_id_inherit_t;
449
450 /* A linked list representing the path from a node up to a root
451    directory.  We use this for cloning, and for operations that need
452    to deal with both a node and its parent directory.  For example, a
453    `delete' operation needs to know that the node actually exists, but
454    also needs to change the parent directory.  */
455 typedef struct parent_path_t
456 {
457
458   /* A node along the path.  This could be the final node, one of its
459      parents, or the root.  Every parent path ends with an element for
460      the root directory.  */
461   dag_node_t *node;
462
463   /* The name NODE has in its parent directory.  This is zero for the
464      root directory, which (obviously) has no name in its parent.  */
465   char *entry;
466
467   /* The parent of NODE, or zero if NODE is the root directory.  */
468   struct parent_path_t *parent;
469
470   /* The copy ID inheritance style. */
471   copy_id_inherit_t copy_inherit;
472
473   /* If copy ID inheritance style is copy_id_inherit_new, this is the
474      path which should be implicitly copied; otherwise, this is NULL. */
475   const char *copy_src_path;
476
477 } parent_path_t;
478
479
480 /* Return the FS path for the parent path chain object PARENT_PATH,
481    allocated in POOL. */
482 static const char *
483 parent_path_path(parent_path_t *parent_path,
484                  apr_pool_t *pool)
485 {
486   const char *path_so_far = "/";
487   if (parent_path->parent)
488     path_so_far = parent_path_path(parent_path->parent, pool);
489   return parent_path->entry
490     ? svn_fspath__join(path_so_far, parent_path->entry, pool)
491          : path_so_far;
492 }
493
494
495 /* Return the FS path for the parent path chain object CHILD relative
496    to its ANCESTOR in the same chain, allocated in POOL.  */
497 static const char *
498 parent_path_relpath(parent_path_t *child,
499                     parent_path_t *ancestor,
500                     apr_pool_t *pool)
501 {
502   const char *path_so_far = "";
503   parent_path_t *this_node = child;
504   while (this_node != ancestor)
505     {
506       assert(this_node != NULL);
507       path_so_far = svn_relpath_join(this_node->entry, path_so_far, pool);
508       this_node = this_node->parent;
509     }
510   return path_so_far;
511 }
512
513
514 /* Choose a copy ID inheritance method *INHERIT_P to be used in the
515    event that immutable node CHILD in FS needs to be made mutable.  If
516    the inheritance method is copy_id_inherit_new, also return a
517    *COPY_SRC_PATH on which to base the new copy ID (else return NULL
518    for that path).  CHILD must have a parent (it cannot be the root
519    node).  TXN_ID is the transaction in which these items might be
520    mutable.  */
521 static svn_error_t *
522 get_copy_inheritance(copy_id_inherit_t *inherit_p,
523                      const char **copy_src_path,
524                      svn_fs_t *fs,
525                      parent_path_t *child,
526                      const char *txn_id,
527                      trail_t *trail,
528                      apr_pool_t *pool)
529 {
530   const svn_fs_id_t *child_id, *parent_id;
531   const char *child_copy_id, *parent_copy_id;
532   const char *id_path = NULL;
533
534   SVN_ERR_ASSERT(child && child->parent && txn_id);
535
536   /* Initialize our return variables (default: self-inheritance). */
537   *inherit_p = copy_id_inherit_self;
538   *copy_src_path = NULL;
539
540   /* Initialize some convenience variables. */
541   child_id = svn_fs_base__dag_get_id(child->node);
542   parent_id = svn_fs_base__dag_get_id(child->parent->node);
543   child_copy_id = svn_fs_base__id_copy_id(child_id);
544   parent_copy_id = svn_fs_base__id_copy_id(parent_id);
545
546   /* Easy out: if this child is already mutable, we have nothing to do. */
547   if (strcmp(svn_fs_base__id_txn_id(child_id), txn_id) == 0)
548     return SVN_NO_ERROR;
549
550   /* If the child and its parent are on the same branch, then the
551      child will inherit the copy ID of its parent when made mutable.
552      This is trivially detectable when the child and its parent have
553      the same copy ID.  But that's not the sole indicator of
554      same-branchness.  It might be the case that the parent was the
555      result of a copy, but the child has not yet been cloned for
556      mutability since that copy.  Detection of this latter case
557      basically means making sure the copy IDs don't differ for some
558      other reason, such as that the child was the direct target of the
559      copy whose ID it has.  There is a special case here, too -- if
560      the child's copy ID is the special ID "0", it can't have been the
561      target of any copy, and therefore must be on the same branch as
562      its parent.  */
563   if ((strcmp(child_copy_id, "0") == 0)
564       || (strcmp(child_copy_id, parent_copy_id) == 0))
565     {
566       *inherit_p = copy_id_inherit_parent;
567       return SVN_NO_ERROR;
568     }
569   else
570     {
571       copy_t *copy;
572       SVN_ERR(svn_fs_bdb__get_copy(&copy, fs, child_copy_id, trail, pool));
573       if (   svn_fs_base__id_compare(copy->dst_noderev_id, child_id)
574           == svn_fs_node_unrelated)
575         {
576           *inherit_p = copy_id_inherit_parent;
577           return SVN_NO_ERROR;
578         }
579     }
580
581   /* If we get here, the child and its parent are not on speaking
582      terms -- there will be no parental inheritance handed down in
583      *this* generation. */
584
585   /* If the child was created at a different path than the one we are
586      expecting its clone to live, one of its parents must have been
587      created via a copy since the child was created.  The child isn't
588      on the same branch as its parent (we caught those cases early);
589      it can't keep its current copy ID because there's been an
590      affecting copy (its clone won't be on the same branch as the
591      child is).  That leaves only one course of action -- to assign
592      the child a brand new "soft" copy ID. */
593   id_path = svn_fs_base__dag_get_created_path(child->node);
594   if (strcmp(id_path, parent_path_path(child, pool)) != 0)
595     {
596       *inherit_p = copy_id_inherit_new;
597       *copy_src_path = id_path;
598       return SVN_NO_ERROR;
599     }
600
601   /* The node gets to keep its own ID. */
602   return SVN_NO_ERROR;
603 }
604
605
606 /* Allocate a new parent_path_t node from POOL, referring to NODE,
607    ENTRY, PARENT, and COPY_ID.  */
608 static parent_path_t *
609 make_parent_path(dag_node_t *node,
610                  char *entry,
611                  parent_path_t *parent,
612                  apr_pool_t *pool)
613 {
614   parent_path_t *parent_path = apr_pcalloc(pool, sizeof(*parent_path));
615   parent_path->node = node;
616   parent_path->entry = entry;
617   parent_path->parent = parent;
618   parent_path->copy_inherit = copy_id_inherit_unknown;
619   parent_path->copy_src_path = NULL;
620   return parent_path;
621 }
622
623
624 /* Flags for open_path.  */
625 typedef enum open_path_flags_t {
626
627   /* The last component of the PATH need not exist.  (All parent
628      directories must exist, as usual.)  If the last component doesn't
629      exist, simply leave the `node' member of the bottom parent_path
630      component zero.  */
631   open_path_last_optional = 1
632
633 } open_path_flags_t;
634
635
636 /* Open the node identified by PATH in ROOT, as part of TRAIL.  Set
637    *PARENT_PATH_P to a path from the node up to ROOT, allocated in
638    TRAIL->pool.  The resulting *PARENT_PATH_P value is guaranteed to
639    contain at least one element, for the root directory.
640
641    If resulting *PARENT_PATH_P will eventually be made mutable and
642    modified, or if copy ID inheritance information is otherwise
643    needed, TXN_ID should be the ID of the mutability transaction.  If
644    TXN_ID is NULL, no copy ID in heritance information will be
645    calculated for the *PARENT_PATH_P chain.
646
647    If FLAGS & open_path_last_optional is zero, return the error
648    SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist.  If
649    non-zero, require all the parent directories to exist as normal,
650    but if the final path component doesn't exist, simply return a path
651    whose bottom `node' member is zero.  This option is useful for
652    callers that create new nodes --- we find the parent directory for
653    them, and tell them whether the entry exists already.
654
655    NOTE: Public interfaces which only *read* from the filesystem
656    should not call this function directly, but should instead use
657    get_dag().
658 */
659 static svn_error_t *
660 open_path(parent_path_t **parent_path_p,
661           svn_fs_root_t *root,
662           const char *path,
663           int flags,
664           const char *txn_id,
665           trail_t *trail,
666           apr_pool_t *pool)
667 {
668   svn_fs_t *fs = root->fs;
669   dag_node_t *here; /* The directory we're currently looking at.  */
670   parent_path_t *parent_path; /* The path from HERE up to the root.  */
671   const char *rest; /* The portion of PATH we haven't traversed yet.  */
672   const char *canon_path = svn_fs__canonicalize_abspath(path, pool);
673   const char *path_so_far = "/";
674
675   /* Make a parent_path item for the root node, using its own current
676      copy id.  */
677   SVN_ERR(root_node(&here, root, trail, pool));
678   parent_path = make_parent_path(here, 0, 0, pool);
679   parent_path->copy_inherit = copy_id_inherit_self;
680
681   rest = canon_path + 1; /* skip the leading '/', it saves in iteration */
682
683   /* Whenever we are at the top of this loop:
684      - HERE is our current directory,
685      - ID is the node revision ID of HERE,
686      - REST is the path we're going to find in HERE, and
687      - PARENT_PATH includes HERE and all its parents.  */
688   for (;;)
689     {
690       const char *next;
691       char *entry;
692       dag_node_t *child;
693
694       /* Parse out the next entry from the path.  */
695       entry = svn_fs__next_entry_name(&next, rest, pool);
696
697       /* Calculate the path traversed thus far. */
698       path_so_far = svn_fspath__join(path_so_far, entry, pool);
699
700       if (*entry == '\0')
701         {
702           /* Given the behavior of svn_fs__next_entry_name(), this
703              happens when the path either starts or ends with a slash.
704              In either case, we stay put: the current directory stays
705              the same, and we add nothing to the parent path. */
706           child = here;
707         }
708       else
709         {
710           copy_id_inherit_t inherit;
711           const char *copy_path = NULL;
712           svn_error_t *err = SVN_NO_ERROR;
713           dag_node_t *cached_node;
714
715           /* If we found a directory entry, follow it.  First, we
716              check our node cache, and, failing that, we hit the DAG
717              layer. */
718           cached_node = dag_node_cache_get(root, path_so_far, pool);
719           if (cached_node)
720             child = cached_node;
721           else
722             err = svn_fs_base__dag_open(&child, here, entry, trail, pool);
723
724           /* "file not found" requires special handling.  */
725           if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
726             {
727               /* If this was the last path component, and the caller
728                  said it was optional, then don't return an error;
729                  just put a NULL node pointer in the path.  */
730
731               svn_error_clear(err);
732
733               if ((flags & open_path_last_optional)
734                   && (! next || *next == '\0'))
735                 {
736                   parent_path = make_parent_path(NULL, entry, parent_path,
737                                                  pool);
738                   break;
739                 }
740               else
741                 {
742                   /* Build a better error message than svn_fs_base__dag_open
743                      can provide, giving the root and full path name.  */
744                   return SVN_FS__NOT_FOUND(root, path);
745                 }
746             }
747
748           /* Other errors we return normally.  */
749           SVN_ERR(err);
750
751           /* Now, make a parent_path item for CHILD. */
752           parent_path = make_parent_path(child, entry, parent_path, pool);
753           if (txn_id)
754             {
755               SVN_ERR(get_copy_inheritance(&inherit, &copy_path,
756                                            fs, parent_path, txn_id,
757                                            trail, pool));
758               parent_path->copy_inherit = inherit;
759               parent_path->copy_src_path = apr_pstrdup(pool, copy_path);
760             }
761
762           /* Cache the node we found (if it wasn't already cached). */
763           if (! cached_node)
764             dag_node_cache_set(root, path_so_far, child);
765         }
766
767       /* Are we finished traversing the path?  */
768       if (! next)
769         break;
770
771       /* The path isn't finished yet; we'd better be in a directory.  */
772       if (svn_fs_base__dag_node_kind(child) != svn_node_dir)
773         SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far),
774                   apr_psprintf(pool, _("Failure opening '%s'"), path));
775
776       rest = next;
777       here = child;
778     }
779
780   *parent_path_p = parent_path;
781   return SVN_NO_ERROR;
782 }
783
784
785 /* Make the node referred to by PARENT_PATH mutable, if it isn't
786    already, as part of TRAIL.  ROOT must be the root from which
787    PARENT_PATH descends.  Clone any parent directories as needed.
788    Adjust the dag nodes in PARENT_PATH to refer to the clones.  Use
789    ERROR_PATH in error messages.  */
790 static svn_error_t *
791 make_path_mutable(svn_fs_root_t *root,
792                   parent_path_t *parent_path,
793                   const char *error_path,
794                   trail_t *trail,
795                   apr_pool_t *pool)
796 {
797   dag_node_t *cloned_node;
798   const char *txn_id = root->txn;
799   svn_fs_t *fs = root->fs;
800
801   /* Is the node mutable already?  */
802   if (svn_fs_base__dag_check_mutable(parent_path->node, txn_id))
803     return SVN_NO_ERROR;
804
805   /* Are we trying to clone the root, or somebody's child node?  */
806   if (parent_path->parent)
807     {
808       const svn_fs_id_t *parent_id;
809       const svn_fs_id_t *node_id = svn_fs_base__dag_get_id(parent_path->node);
810       const char *copy_id = NULL;
811       const char *copy_src_path = parent_path->copy_src_path;
812       copy_id_inherit_t inherit = parent_path->copy_inherit;
813       const char *clone_path;
814
815       /* We're trying to clone somebody's child.  Make sure our parent
816          is mutable.  */
817       SVN_ERR(make_path_mutable(root, parent_path->parent,
818                                 error_path, trail, pool));
819
820       switch (inherit)
821         {
822         case copy_id_inherit_parent:
823           parent_id = svn_fs_base__dag_get_id(parent_path->parent->node);
824           copy_id = svn_fs_base__id_copy_id(parent_id);
825           break;
826
827         case copy_id_inherit_new:
828           SVN_ERR(svn_fs_bdb__reserve_copy_id(&copy_id, fs, trail, pool));
829           break;
830
831         case copy_id_inherit_self:
832           copy_id = NULL;
833           break;
834
835         case copy_id_inherit_unknown:
836         default:
837           SVN_ERR_MALFUNCTION(); /* uh-oh -- somebody didn't calculate copy-ID
838                       inheritance data. */
839         }
840
841       /* Now make this node mutable.  */
842       clone_path = parent_path_path(parent_path->parent, pool);
843       SVN_ERR(svn_fs_base__dag_clone_child(&cloned_node,
844                                            parent_path->parent->node,
845                                            clone_path,
846                                            parent_path->entry,
847                                            copy_id, txn_id,
848                                            trail, pool));
849
850       /* If we just created a brand new copy ID, we need to store a
851          `copies' table entry for it, as well as a notation in the
852          transaction that should this transaction be terminated, our
853          new copy needs to be removed. */
854       if (inherit == copy_id_inherit_new)
855         {
856           const svn_fs_id_t *new_node_id =
857             svn_fs_base__dag_get_id(cloned_node);
858           SVN_ERR(svn_fs_bdb__create_copy(fs, copy_id, copy_src_path,
859                                           svn_fs_base__id_txn_id(node_id),
860                                           new_node_id,
861                                           copy_kind_soft, trail, pool));
862           SVN_ERR(svn_fs_base__add_txn_copy(fs, txn_id, copy_id,
863                                             trail, pool));
864         }
865     }
866   else
867     {
868       /* We're trying to clone the root directory.  */
869       SVN_ERR(mutable_root_node(&cloned_node, root, error_path, trail, pool));
870     }
871
872   /* Update the PARENT_PATH link to refer to the clone.  */
873   parent_path->node = cloned_node;
874
875   return SVN_NO_ERROR;
876 }
877
878
879 /* Walk up PARENT_PATH to the root of the tree, adjusting each node's
880    mergeinfo count by COUNT_DELTA as part of Subversion transaction
881    TXN_ID and TRAIL.  Use POOL for allocations. */
882 static svn_error_t *
883 adjust_parent_mergeinfo_counts(parent_path_t *parent_path,
884                                apr_int64_t count_delta,
885                                const char *txn_id,
886                                trail_t *trail,
887                                apr_pool_t *pool)
888 {
889   apr_pool_t *iterpool;
890   parent_path_t *pp = parent_path;
891
892   if (count_delta == 0)
893     return SVN_NO_ERROR;
894
895   iterpool = svn_pool_create(pool);
896
897   while (pp)
898     {
899       svn_pool_clear(iterpool);
900       SVN_ERR(svn_fs_base__dag_adjust_mergeinfo_count(pp->node, count_delta,
901                                                       txn_id, trail,
902                                                       iterpool));
903       pp = pp->parent;
904     }
905   svn_pool_destroy(iterpool);
906
907   return SVN_NO_ERROR;
908 }
909
910
911 /* Open the node identified by PATH in ROOT, as part of TRAIL.  Set
912    *DAG_NODE_P to the node we find, allocated in TRAIL->pool.  Return
913    the error SVN_ERR_FS_NOT_FOUND if this node doesn't exist. */
914 static svn_error_t *
915 get_dag(dag_node_t **dag_node_p,
916         svn_fs_root_t *root,
917         const char *path,
918         trail_t *trail,
919         apr_pool_t *pool)
920 {
921   parent_path_t *parent_path;
922   dag_node_t *node = NULL;
923
924   /* Canonicalize the input PATH. */
925   path = svn_fs__canonicalize_abspath(path, pool);
926
927   /* If ROOT is a revision root, we'll look for the DAG in our cache. */
928   node = dag_node_cache_get(root, path, pool);
929   if (! node)
930     {
931       /* Call open_path with no flags, as we want this to return an error
932          if the node for which we are searching doesn't exist. */
933       SVN_ERR(open_path(&parent_path, root, path, 0, NULL, trail, pool));
934       node = parent_path->node;
935
936       /* No need to cache our find -- open_path() will do that for us. */
937     }
938
939   *dag_node_p = node;
940   return SVN_NO_ERROR;
941 }
942
943
944 \f
945 /* Populating the `changes' table. */
946
947 /* Add a change to the changes table in FS, keyed on transaction id
948    TXN_ID, and indicated that a change of kind CHANGE_KIND occurred on
949    PATH (whose node revision id is--or was, in the case of a
950    deletion--NODEREV_ID), and optionally that TEXT_MODs or PROP_MODs
951    occurred.  Do all this as part of TRAIL.  */
952 static svn_error_t *
953 add_change(svn_fs_t *fs,
954            const char *txn_id,
955            const char *path,
956            const svn_fs_id_t *noderev_id,
957            svn_fs_path_change_kind_t change_kind,
958            svn_boolean_t text_mod,
959            svn_boolean_t prop_mod,
960            trail_t *trail,
961            apr_pool_t *pool)
962 {
963   change_t change;
964   change.path = svn_fs__canonicalize_abspath(path, pool);
965   change.noderev_id = noderev_id;
966   change.kind = change_kind;
967   change.text_mod = text_mod;
968   change.prop_mod = prop_mod;
969   return svn_fs_bdb__changes_add(fs, txn_id, &change, trail, pool);
970 }
971
972
973 \f
974 /* Generic node operations.  */
975
976
977 struct node_id_args {
978   const svn_fs_id_t **id_p;
979   svn_fs_root_t *root;
980   const char *path;
981 };
982
983
984 static svn_error_t *
985 txn_body_node_id(void *baton, trail_t *trail)
986 {
987   struct node_id_args *args = baton;
988   dag_node_t *node;
989
990   SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
991   *args->id_p = svn_fs_base__id_copy(svn_fs_base__dag_get_id(node),
992                                      trail->pool);
993
994   return SVN_NO_ERROR;
995 }
996
997
998 static svn_error_t *
999 base_node_id(const svn_fs_id_t **id_p,
1000              svn_fs_root_t *root,
1001              const char *path,
1002              apr_pool_t *pool)
1003 {
1004   base_root_data_t *brd = root->fsap_data;
1005
1006   if (! root->is_txn_root
1007       && (path[0] == '\0' || ((path[0] == '/') && (path[1] == '\0'))))
1008     {
1009       /* Optimize the case where we don't need any db access at all.
1010          The root directory ("" or "/") node is stored in the
1011          svn_fs_root_t object, and never changes when it's a revision
1012          root, so we can just reach in and grab it directly. */
1013       *id_p = svn_fs_base__id_copy(svn_fs_base__dag_get_id(brd->root_dir),
1014                                    pool);
1015     }
1016   else
1017     {
1018       const svn_fs_id_t *id;
1019       struct node_id_args args;
1020
1021       args.id_p = &id;
1022       args.root = root;
1023       args.path = path;
1024
1025       SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_id, &args,
1026                                      FALSE, pool));
1027       *id_p = id;
1028     }
1029   return SVN_NO_ERROR;
1030 }
1031
1032 static svn_error_t *
1033 base_node_relation(svn_fs_node_relation_t *relation,
1034                    svn_fs_root_t *root_a, const char *path_a,
1035                    svn_fs_root_t *root_b, const char *path_b,
1036                    apr_pool_t *pool)
1037 {
1038   const svn_fs_id_t *id_a, *id_b;
1039
1040   /* Paths from different repository are never related. */
1041   if (root_a->fs != root_b->fs)
1042     {
1043       *relation = svn_fs_node_unrelated;
1044       return SVN_NO_ERROR;
1045     }
1046
1047   /* Naive implementation. */
1048   SVN_ERR(base_node_id(&id_a, root_a, path_a, pool));
1049   SVN_ERR(base_node_id(&id_b, root_b, path_b, pool));
1050
1051   *relation = svn_fs_base__id_compare(id_a, id_b);
1052
1053   return SVN_NO_ERROR;
1054 }
1055
1056
1057 struct node_created_rev_args {
1058   svn_revnum_t revision;
1059   svn_fs_root_t *root;
1060   const char *path;
1061 };
1062
1063
1064 static svn_error_t *
1065 txn_body_node_created_rev(void *baton, trail_t *trail)
1066 {
1067   struct node_created_rev_args *args = baton;
1068   dag_node_t *node;
1069
1070   SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1071   return svn_fs_base__dag_get_revision(&(args->revision), node,
1072                                        trail, trail->pool);
1073 }
1074
1075
1076 static svn_error_t *
1077 base_node_created_rev(svn_revnum_t *revision,
1078                       svn_fs_root_t *root,
1079                       const char *path,
1080                       apr_pool_t *pool)
1081 {
1082   struct node_created_rev_args args;
1083
1084   args.revision = SVN_INVALID_REVNUM;
1085   args.root = root;
1086   args.path = path;
1087   SVN_ERR(svn_fs_base__retry_txn
1088           (root->fs, txn_body_node_created_rev, &args, TRUE, pool));
1089   *revision = args.revision;
1090   return SVN_NO_ERROR;
1091 }
1092
1093
1094 struct node_created_path_args {
1095   const char **created_path;
1096   svn_fs_root_t *root;
1097   const char *path;
1098 };
1099
1100
1101 static svn_error_t *
1102 txn_body_node_created_path(void *baton, trail_t *trail)
1103 {
1104   struct node_created_path_args *args = baton;
1105   dag_node_t *node;
1106
1107   SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1108   *args->created_path = svn_fs_base__dag_get_created_path(node);
1109   return SVN_NO_ERROR;
1110 }
1111
1112
1113 static svn_error_t *
1114 base_node_created_path(const char **created_path,
1115                        svn_fs_root_t *root,
1116                        const char *path,
1117                        apr_pool_t *pool)
1118 {
1119   struct node_created_path_args args;
1120   apr_pool_t *scratch_pool = svn_pool_create(pool);
1121
1122   args.created_path = created_path;
1123   args.root = root;
1124   args.path = path;
1125
1126   SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_created_path, &args,
1127                                  FALSE, scratch_pool));
1128   if (*created_path)
1129     *created_path = apr_pstrdup(pool, *created_path);
1130   svn_pool_destroy(scratch_pool);
1131   return SVN_NO_ERROR;
1132 }
1133
1134
1135 struct node_kind_args {
1136   const svn_fs_id_t *id;
1137   svn_node_kind_t kind; /* OUT parameter */
1138 };
1139
1140
1141 static svn_error_t *
1142 txn_body_node_kind(void *baton, trail_t *trail)
1143 {
1144   struct node_kind_args *args = baton;
1145   dag_node_t *node;
1146
1147   SVN_ERR(svn_fs_base__dag_get_node(&node, trail->fs, args->id,
1148                                     trail, trail->pool));
1149   args->kind = svn_fs_base__dag_node_kind(node);
1150
1151   return SVN_NO_ERROR;
1152 }
1153
1154
1155 static svn_error_t *
1156 node_kind(svn_node_kind_t *kind_p,
1157           svn_fs_root_t *root,
1158           const char *path,
1159           apr_pool_t *pool)
1160 {
1161   struct node_kind_args args;
1162   const svn_fs_id_t *node_id;
1163
1164   /* Get the node id. */
1165   SVN_ERR(base_node_id(&node_id, root, path, pool));
1166
1167   /* Use the node id to get the real kind. */
1168   args.id = node_id;
1169   SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_kind, &args,
1170                                  TRUE, pool));
1171
1172   *kind_p = args.kind;
1173   return SVN_NO_ERROR;
1174 }
1175
1176
1177 static svn_error_t *
1178 base_check_path(svn_node_kind_t *kind_p,
1179                 svn_fs_root_t *root,
1180                 const char *path,
1181                 apr_pool_t *pool)
1182 {
1183   svn_error_t *err = node_kind(kind_p, root, path, pool);
1184   if (err &&
1185       ((err->apr_err == SVN_ERR_FS_NOT_FOUND)
1186        || (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY)))
1187     {
1188       svn_error_clear(err);
1189       err = SVN_NO_ERROR;
1190       *kind_p = svn_node_none;
1191     }
1192
1193   return svn_error_trace(err);
1194 }
1195
1196
1197 struct node_prop_args
1198 {
1199   svn_string_t **value_p;
1200   svn_fs_root_t *root;
1201   const char *path;
1202   const char *propname;
1203 };
1204
1205
1206 static svn_error_t *
1207 txn_body_node_prop(void *baton,
1208                    trail_t *trail)
1209 {
1210   struct node_prop_args *args = baton;
1211   dag_node_t *node;
1212   apr_hash_t *proplist;
1213
1214   SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1215   SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, node,
1216                                         trail, trail->pool));
1217   *(args->value_p) = NULL;
1218   if (proplist)
1219     *(args->value_p) = svn_hash_gets(proplist, args->propname);
1220   return SVN_NO_ERROR;
1221 }
1222
1223
1224 static svn_error_t *
1225 base_node_prop(svn_string_t **value_p,
1226                svn_fs_root_t *root,
1227                const char *path,
1228                const char *propname,
1229                apr_pool_t *pool)
1230 {
1231   struct node_prop_args args;
1232   svn_string_t *value;
1233   apr_pool_t *scratch_pool = svn_pool_create(pool);
1234
1235   args.value_p  = &value;
1236   args.root     = root;
1237   args.path     = path;
1238   args.propname = propname;
1239   SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_prop, &args,
1240                                  FALSE, scratch_pool));
1241   *value_p = svn_string_dup(value, pool);
1242   svn_pool_destroy(scratch_pool);
1243   return SVN_NO_ERROR;
1244 }
1245
1246
1247 struct node_proplist_args {
1248   apr_hash_t **table_p;
1249   svn_fs_root_t *root;
1250   const char *path;
1251 };
1252
1253
1254 static svn_error_t *
1255 txn_body_node_proplist(void *baton, trail_t *trail)
1256 {
1257   struct node_proplist_args *args = baton;
1258   dag_node_t *node;
1259   apr_hash_t *proplist;
1260
1261   SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1262   SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, node,
1263                                         trail, trail->pool));
1264   *args->table_p = proplist ? proplist : apr_hash_make(trail->pool);
1265   return SVN_NO_ERROR;
1266 }
1267
1268
1269 static svn_error_t *
1270 base_node_proplist(apr_hash_t **table_p,
1271                    svn_fs_root_t *root,
1272                    const char *path,
1273                    apr_pool_t *pool)
1274 {
1275   apr_hash_t *table;
1276   struct node_proplist_args args;
1277
1278   args.table_p = &table;
1279   args.root = root;
1280   args.path = path;
1281
1282   SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_proplist, &args,
1283                                  FALSE, pool));
1284
1285   *table_p = table;
1286   return SVN_NO_ERROR;
1287 }
1288
1289 static svn_error_t *
1290 base_node_has_props(svn_boolean_t *has_props,
1291                     svn_fs_root_t *root,
1292                     const char *path,
1293                     apr_pool_t *scratch_pool)
1294 {
1295   apr_hash_t *props;
1296
1297   SVN_ERR(base_node_proplist(&props, root, path, scratch_pool));
1298
1299   *has_props = (0 < apr_hash_count(props));
1300
1301   return SVN_NO_ERROR;
1302 }
1303
1304
1305 struct change_node_prop_args {
1306   svn_fs_root_t *root;
1307   const char *path;
1308   const char *name;
1309   const svn_string_t *value;
1310 };
1311
1312
1313 static svn_error_t *
1314 txn_body_change_node_prop(void *baton,
1315                           trail_t *trail)
1316 {
1317   struct change_node_prop_args *args = baton;
1318   parent_path_t *parent_path;
1319   apr_hash_t *proplist;
1320   const char *txn_id = args->root->txn;
1321   base_fs_data_t *bfd = trail->fs->fsap_data;
1322
1323   SVN_ERR(open_path(&parent_path, args->root, args->path, 0, txn_id,
1324                     trail, trail->pool));
1325
1326   /* Check to see if path is locked; if so, check that we can use it.
1327      Notice that we're doing this non-recursively, regardless of node kind. */
1328   if (args->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
1329     SVN_ERR(svn_fs_base__allow_locked_operation
1330             (args->path, FALSE, trail, trail->pool));
1331
1332   SVN_ERR(make_path_mutable(args->root, parent_path, args->path,
1333                             trail, trail->pool));
1334   SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, parent_path->node,
1335                                         trail, trail->pool));
1336
1337   /* If there's no proplist, but we're just deleting a property, exit now. */
1338   if ((! proplist) && (! args->value))
1339     return SVN_NO_ERROR;
1340
1341   /* Now, if there's no proplist, we know we need to make one. */
1342   if (! proplist)
1343     proplist = apr_hash_make(trail->pool);
1344
1345   /* Set the property. */
1346   svn_hash_sets(proplist, args->name, args->value);
1347
1348   /* Overwrite the node's proplist. */
1349   SVN_ERR(svn_fs_base__dag_set_proplist(parent_path->node, proplist,
1350                                         txn_id, trail, trail->pool));
1351
1352   /* If this was a change to the mergeinfo property, and our version
1353      of the filesystem cares, we have some extra recording to do.
1354
1355      ### If the format *doesn't* support mergeinfo recording, should
1356      ### we fuss about attempts to change the svn:mergeinfo property
1357      ### in any way save to delete it?  */
1358   if ((bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
1359       && (strcmp(args->name, SVN_PROP_MERGEINFO) == 0))
1360     {
1361       svn_boolean_t had_mergeinfo, has_mergeinfo = args->value != NULL;
1362
1363       /* First, note on our node that it has mergeinfo. */
1364       SVN_ERR(svn_fs_base__dag_set_has_mergeinfo(parent_path->node,
1365                                                  has_mergeinfo,
1366                                                  &had_mergeinfo, txn_id,
1367                                                  trail, trail->pool));
1368
1369       /* If this is a change from the old state, we need to update our
1370          node's parents' mergeinfo counts by a factor of 1. */
1371       if (parent_path->parent && ((! had_mergeinfo) != (! has_mergeinfo)))
1372         SVN_ERR(adjust_parent_mergeinfo_counts(parent_path->parent,
1373                                                has_mergeinfo ? 1 : -1,
1374                                                txn_id, trail, trail->pool));
1375     }
1376
1377   /* Make a record of this modification in the changes table. */
1378   return add_change(args->root->fs, txn_id,
1379                     args->path, svn_fs_base__dag_get_id(parent_path->node),
1380                     svn_fs_path_change_modify, FALSE, TRUE, trail,
1381                     trail->pool);
1382 }
1383
1384
1385 static svn_error_t *
1386 base_change_node_prop(svn_fs_root_t *root,
1387                       const char *path,
1388                       const char *name,
1389                       const svn_string_t *value,
1390                       apr_pool_t *pool)
1391 {
1392   struct change_node_prop_args args;
1393
1394   if (! root->is_txn_root)
1395     return SVN_FS__NOT_TXN(root);
1396
1397   args.root  = root;
1398   args.path  = path;
1399   args.name  = name;
1400   args.value = value;
1401   return svn_fs_base__retry_txn(root->fs, txn_body_change_node_prop, &args,
1402                                 TRUE, pool);
1403 }
1404
1405
1406 struct things_changed_args
1407 {
1408   svn_boolean_t *changed_p;
1409   svn_fs_root_t *root1;
1410   svn_fs_root_t *root2;
1411   const char *path1;
1412   const char *path2;
1413   svn_boolean_t strict;
1414   apr_pool_t *pool;
1415 };
1416
1417
1418 static svn_error_t *
1419 txn_body_props_changed(void *baton, trail_t *trail)
1420 {
1421   struct things_changed_args *args = baton;
1422   dag_node_t *node1, *node2;
1423   apr_hash_t *proplist1, *proplist2;
1424
1425   SVN_ERR(get_dag(&node1, args->root1, args->path1, trail, trail->pool));
1426   SVN_ERR(get_dag(&node2, args->root2, args->path2, trail, trail->pool));
1427   SVN_ERR(svn_fs_base__things_different(args->changed_p, NULL,
1428                                         node1, node2, trail, trail->pool));
1429
1430   /* Is there a potential false positive and do we want to correct it? */
1431   if (!args->strict || !*args->changed_p)
1432     return SVN_NO_ERROR;
1433
1434   /* Different representations. They might still have equal contents. */
1435   SVN_ERR(svn_fs_base__dag_get_proplist(&proplist1, node1,
1436                                         trail, trail->pool));
1437   SVN_ERR(svn_fs_base__dag_get_proplist(&proplist2, node2,
1438                                         trail, trail->pool));
1439
1440   *args->changed_p = !svn_fs__prop_lists_equal(proplist1, proplist2,
1441                                                trail->pool);
1442   return SVN_NO_ERROR;
1443 }
1444
1445
1446 static svn_error_t *
1447 base_props_changed(svn_boolean_t *changed_p,
1448                    svn_fs_root_t *root1,
1449                    const char *path1,
1450                    svn_fs_root_t *root2,
1451                    const char *path2,
1452                    svn_boolean_t strict,
1453                    apr_pool_t *pool)
1454 {
1455   struct things_changed_args args;
1456
1457   /* Check that roots are in the same fs. */
1458   if (root1->fs != root2->fs)
1459     return svn_error_create
1460       (SVN_ERR_FS_GENERAL, NULL,
1461        _("Cannot compare property value between two different filesystems"));
1462
1463   args.root1      = root1;
1464   args.root2      = root2;
1465   args.path1      = path1;
1466   args.path2      = path2;
1467   args.changed_p  = changed_p;
1468   args.pool       = pool;
1469   args.strict     = strict;
1470
1471   return svn_fs_base__retry_txn(root1->fs, txn_body_props_changed, &args,
1472                                 TRUE, pool);
1473 }
1474
1475
1476 \f
1477 /* Miscellaneous table handling */
1478
1479 struct miscellaneous_set_args
1480 {
1481   const char *key;
1482   const char *val;
1483 };
1484
1485 static svn_error_t *
1486 txn_body_miscellaneous_set(void *baton, trail_t *trail)
1487 {
1488   struct miscellaneous_set_args *msa = baton;
1489
1490   return svn_fs_bdb__miscellaneous_set(trail->fs, msa->key, msa->val, trail,
1491                                        trail->pool);
1492 }
1493
1494 svn_error_t *
1495 svn_fs_base__miscellaneous_set(svn_fs_t *fs,
1496                                const char *key,
1497                                const char *val,
1498                                apr_pool_t *pool)
1499 {
1500   struct miscellaneous_set_args msa;
1501   msa.key = key;
1502   msa.val = val;
1503
1504   return svn_fs_base__retry_txn(fs, txn_body_miscellaneous_set, &msa,
1505                                 TRUE, pool);
1506 }
1507
1508 struct miscellaneous_get_args
1509 {
1510   const char *key;
1511   const char **val;
1512 };
1513
1514 static svn_error_t *
1515 txn_body_miscellaneous_get(void *baton, trail_t *trail)
1516 {
1517   struct miscellaneous_get_args *mga = baton;
1518   return svn_fs_bdb__miscellaneous_get(mga->val, trail->fs, mga->key, trail,
1519                                        trail->pool);
1520 }
1521
1522 svn_error_t *
1523 svn_fs_base__miscellaneous_get(const char **val,
1524                                svn_fs_t *fs,
1525                                const char *key,
1526                                apr_pool_t *pool)
1527 {
1528   struct miscellaneous_get_args mga;
1529   apr_pool_t *scratch_pool = svn_pool_create(pool);
1530
1531   mga.key = key;
1532   mga.val = val;
1533   SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_miscellaneous_get, &mga,
1534                                  FALSE, scratch_pool));
1535   if (*val)
1536     *val = apr_pstrdup(pool, *val);
1537   svn_pool_destroy(scratch_pool);
1538   return SVN_NO_ERROR;
1539 }
1540
1541
1542 \f
1543 /* Getting a directory's entries */
1544
1545
1546 struct dir_entries_args
1547 {
1548   apr_hash_t **table_p;
1549   svn_fs_root_t *root;
1550   const char *path;
1551 };
1552
1553
1554 /* *(BATON->table_p) will never be NULL on successful return */
1555 static svn_error_t *
1556 txn_body_dir_entries(void *baton,
1557                      trail_t *trail)
1558 {
1559   struct dir_entries_args *args = baton;
1560   dag_node_t *node;
1561   apr_hash_t *entries;
1562
1563   SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1564
1565   /* Get the entries for PARENT_PATH. */
1566   SVN_ERR(svn_fs_base__dag_dir_entries(&entries, node, trail, trail->pool));
1567
1568   /* Potentially initialize the return value to an empty hash. */
1569   *args->table_p = entries ? entries : apr_hash_make(trail->pool);
1570   return SVN_NO_ERROR;
1571 }
1572
1573
1574 static svn_error_t *
1575 base_dir_entries(apr_hash_t **table_p,
1576                  svn_fs_root_t *root,
1577                  const char *path,
1578                  apr_pool_t *pool)
1579 {
1580   struct dir_entries_args args;
1581   apr_pool_t *iterpool;
1582   apr_hash_t *table;
1583   svn_fs_t *fs = root->fs;
1584   apr_hash_index_t *hi;
1585
1586   args.table_p = &table;
1587   args.root    = root;
1588   args.path    = path;
1589   SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_dir_entries, &args,
1590                                  FALSE, pool));
1591
1592   iterpool = svn_pool_create(pool);
1593
1594   /* Add in the kind data. */
1595   for (hi = apr_hash_first(pool, table); hi; hi = apr_hash_next(hi))
1596     {
1597       svn_fs_dirent_t *entry;
1598       struct node_kind_args nk_args;
1599       void *val;
1600
1601       svn_pool_clear(iterpool);
1602
1603       /* KEY will be the entry name in ancestor (about which we
1604          simply don't care), VAL the dirent. */
1605       apr_hash_this(hi, NULL, NULL, &val);
1606       entry = val;
1607       nk_args.id = entry->id;
1608
1609       /* We don't need to have the retry function destroy the trail
1610          pool because we're already doing that via the use of an
1611          iteration pool. */
1612       SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_node_kind, &nk_args,
1613                                      FALSE, iterpool));
1614       entry->kind = nk_args.kind;
1615     }
1616
1617   svn_pool_destroy(iterpool);
1618
1619   *table_p = table;
1620   return SVN_NO_ERROR;
1621 }
1622
1623 static svn_error_t *
1624 base_dir_optimal_order(apr_array_header_t **ordered_p,
1625                        svn_fs_root_t *root,
1626                        apr_hash_t *entries,
1627                        apr_pool_t *result_pool,
1628                        apr_pool_t *scratch_pool)
1629 {
1630   /* 1:1 copy of entries with no differnce in ordering */
1631   apr_hash_index_t *hi;
1632   apr_array_header_t *result
1633     = apr_array_make(result_pool, apr_hash_count(entries),
1634                      sizeof(svn_fs_dirent_t *));
1635   for (hi = apr_hash_first(scratch_pool, entries); hi; hi = apr_hash_next(hi))
1636     APR_ARRAY_PUSH(result, svn_fs_dirent_t *) = apr_hash_this_val(hi);
1637
1638   *ordered_p = result;
1639   return SVN_NO_ERROR;
1640 }
1641
1642
1643 \f
1644 /* Merges and commits. */
1645
1646
1647 struct deltify_committed_args
1648 {
1649   svn_fs_t *fs; /* the filesystem */
1650   svn_revnum_t rev; /* revision just committed */
1651   const char *txn_id; /* transaction just committed */
1652 };
1653
1654
1655 struct txn_deltify_args
1656 {
1657   /* The transaction ID whose nodes are being deltified. */
1658   const char *txn_id;
1659
1660   /* The target is what we're deltifying. */
1661   const svn_fs_id_t *tgt_id;
1662
1663   /* The base is what we're deltifying against.  It's not necessarily
1664      the "next" revision of the node; skip deltas mean we sometimes
1665      deltify against a successor many generations away.  This may be
1666      NULL, in which case we'll avoid deltification and simply index
1667      TGT_ID's data checksum. */
1668   const svn_fs_id_t *base_id;
1669
1670   /* We only deltify props for directories.
1671      ### Didn't we try removing this horrid little optimization once?
1672      ### What was the result?  I would have thought that skip deltas
1673      ### mean directory undeltification is cheap enough now. */
1674   svn_boolean_t is_dir;
1675 };
1676
1677
1678 static svn_error_t *
1679 txn_body_txn_deltify(void *baton, trail_t *trail)
1680 {
1681   struct txn_deltify_args *args = baton;
1682   dag_node_t *tgt_node, *base_node;
1683   base_fs_data_t *bfd = trail->fs->fsap_data;
1684
1685   SVN_ERR(svn_fs_base__dag_get_node(&tgt_node, trail->fs, args->tgt_id,
1686                                     trail, trail->pool));
1687   /* If we have something to deltify against, do so. */
1688   if (args->base_id)
1689     {
1690       SVN_ERR(svn_fs_base__dag_get_node(&base_node, trail->fs, args->base_id,
1691                                         trail, trail->pool));
1692       SVN_ERR(svn_fs_base__dag_deltify(tgt_node, base_node, args->is_dir,
1693                                        args->txn_id, trail, trail->pool));
1694     }
1695
1696   /* If we support rep sharing, and this isn't a directory, record a
1697      mapping of TGT_NODE's data checksum to its representation key. */
1698   if (bfd->format >= SVN_FS_BASE__MIN_REP_SHARING_FORMAT)
1699     SVN_ERR(svn_fs_base__dag_index_checksums(tgt_node, trail, trail->pool));
1700
1701   return SVN_NO_ERROR;
1702 }
1703
1704
1705 struct txn_pred_count_args
1706 {
1707   const svn_fs_id_t *id;
1708   int pred_count;
1709 };
1710
1711
1712 static svn_error_t *
1713 txn_body_pred_count(void *baton, trail_t *trail)
1714 {
1715   node_revision_t *noderev;
1716   struct txn_pred_count_args *args = baton;
1717
1718   SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, trail->fs,
1719                                         args->id, trail, trail->pool));
1720   args->pred_count = noderev->predecessor_count;
1721   return SVN_NO_ERROR;
1722 }
1723
1724
1725 struct txn_pred_id_args
1726 {
1727   const svn_fs_id_t *id;      /* The node id whose predecessor we want. */
1728   const svn_fs_id_t *pred_id; /* The returned predecessor id. */
1729   apr_pool_t *pool;           /* The pool in which to allocate pred_id. */
1730 };
1731
1732
1733 static svn_error_t *
1734 txn_body_pred_id(void *baton, trail_t *trail)
1735 {
1736   node_revision_t *nr;
1737   struct txn_pred_id_args *args = baton;
1738
1739   SVN_ERR(svn_fs_bdb__get_node_revision(&nr, trail->fs, args->id,
1740                                         trail, trail->pool));
1741   if (nr->predecessor_id)
1742     args->pred_id = svn_fs_base__id_copy(nr->predecessor_id, args->pool);
1743   else
1744     args->pred_id = NULL;
1745
1746   return SVN_NO_ERROR;
1747 }
1748
1749
1750 /* Deltify PATH in ROOT's predecessor iff PATH is mutable under TXN_ID
1751    in FS.  If PATH is a mutable directory, recurse.
1752
1753    NODE_ID is the node revision ID for PATH in ROOT, or NULL if that
1754    value isn't known.  KIND is the node kind for PATH in ROOT, or
1755    svn_node_unknown is the kind isn't known.
1756
1757    Use POOL for necessary allocations.  */
1758 static svn_error_t *
1759 deltify_mutable(svn_fs_t *fs,
1760                 svn_fs_root_t *root,
1761                 const char *path,
1762                 const svn_fs_id_t *node_id,
1763                 svn_node_kind_t kind,
1764                 const char *txn_id,
1765                 apr_pool_t *pool)
1766 {
1767   const svn_fs_id_t *id = node_id;
1768   apr_hash_t *entries = NULL;
1769   struct txn_deltify_args td_args;
1770   base_fs_data_t *bfd = fs->fsap_data;
1771
1772   /* Get the ID for PATH under ROOT if it wasn't provided. */
1773   if (! node_id)
1774     SVN_ERR(base_node_id(&id, root, path, pool));
1775
1776   /* Check for mutability.  Not mutable?  Go no further.  This is safe
1777      to do because for items in the tree to be mutable, their parent
1778      dirs must also be mutable.  Therefore, if a directory is not
1779      mutable under TXN_ID, its children cannot be.  */
1780   if (strcmp(svn_fs_base__id_txn_id(id), txn_id))
1781     return SVN_NO_ERROR;
1782
1783   /* Is this a directory?  */
1784   if (kind == svn_node_unknown)
1785     SVN_ERR(base_check_path(&kind, root, path, pool));
1786
1787   /* If this is a directory, read its entries.  */
1788   if (kind == svn_node_dir)
1789     SVN_ERR(base_dir_entries(&entries, root, path, pool));
1790
1791   /* If there are entries, recurse on 'em.  */
1792   if (entries)
1793     {
1794       apr_pool_t *subpool = svn_pool_create(pool);
1795       apr_hash_index_t *hi;
1796
1797       for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
1798         {
1799           /* KEY will be the entry name, VAL the dirent */
1800           const void *key;
1801           void *val;
1802           svn_fs_dirent_t *entry;
1803           svn_pool_clear(subpool);
1804           apr_hash_this(hi, &key, NULL, &val);
1805           entry = val;
1806           SVN_ERR(deltify_mutable(fs, root,
1807                                   svn_fspath__join(path, key, subpool),
1808                                   entry->id, entry->kind, txn_id, subpool));
1809         }
1810
1811       svn_pool_destroy(subpool);
1812     }
1813
1814   /* Index ID's data checksum. */
1815   td_args.txn_id = txn_id;
1816   td_args.tgt_id = id;
1817   td_args.base_id = NULL;
1818   td_args.is_dir = (kind == svn_node_dir);
1819   SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_deltify, &td_args,
1820                                  TRUE, pool));
1821
1822   /* Finally, deltify old data against this node. */
1823   {
1824     /* Prior to 1.6, we use the following algorithm to deltify nodes:
1825
1826        Redeltify predecessor node-revisions of the one we added.  The
1827        idea is to require at most 2*lg(N) deltas to be applied to get
1828        to any node-revision in a chain of N predecessors.  We do this
1829        using a technique derived from skip lists:
1830
1831           - Always redeltify the immediate parent
1832
1833           - If the number of predecessors is divisible by 2,
1834               redeltify the revision two predecessors back
1835
1836           - If the number of predecessors is divisible by 4,
1837               redeltify the revision four predecessors back
1838
1839        ... and so on.
1840
1841        That's the theory, anyway.  Unfortunately, if we strictly
1842        follow that theory we get a bunch of overhead up front and no
1843        great benefit until the number of predecessors gets large.  So,
1844        stop at redeltifying the parent if the number of predecessors
1845        is less than 32, and also skip the second level (redeltifying
1846        two predecessors back), since that doesn't help much.  Also,
1847        don't redeltify the oldest node-revision; it's potentially
1848        expensive and doesn't help retrieve any other revision.
1849        (Retrieving the oldest node-revision will still be fast, just
1850        not as blindingly so.)
1851
1852        For 1.6 and beyond, we just deltify the current node against its
1853        predecessors, using skip deltas similar to the way FSFS does.  */
1854
1855     int pred_count;
1856     const svn_fs_id_t *pred_id;
1857     struct txn_pred_count_args tpc_args;
1858     apr_pool_t *subpools[2];
1859     int active_subpool = 0;
1860     svn_revnum_t forward_delta_rev = 0;
1861
1862     tpc_args.id = id;
1863     SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_count, &tpc_args,
1864                                    TRUE, pool));
1865     pred_count = tpc_args.pred_count;
1866
1867     /* If nothing to deltify, then we're done. */
1868     if (pred_count == 0)
1869       return SVN_NO_ERROR;
1870
1871     subpools[0] = svn_pool_create(pool);
1872     subpools[1] = svn_pool_create(pool);
1873
1874     /* If we support the 'miscellaneous' table, check it to see if
1875        there is a point in time before which we don't want to do
1876        deltification. */
1877     /* ### FIXME:  I think this is an unnecessary restriction.  We
1878        ### should be able to do something meaningful for most
1879        ### deltification requests -- what that is depends on the
1880        ### directory of the deltas for that revision, though. */
1881     if (bfd->format >= SVN_FS_BASE__MIN_MISCELLANY_FORMAT)
1882       {
1883         const char *val;
1884         SVN_ERR(svn_fs_base__miscellaneous_get
1885                 (&val, fs, SVN_FS_BASE__MISC_FORWARD_DELTA_UPGRADE, pool));
1886         if (val)
1887           SVN_ERR(svn_revnum_parse(&forward_delta_rev, val, NULL));
1888       }
1889
1890     if (bfd->format >= SVN_FS_BASE__MIN_FORWARD_DELTAS_FORMAT
1891           && forward_delta_rev <= root->rev)
1892       {
1893         /**** FORWARD DELTA STORAGE ****/
1894
1895         /* Decide which predecessor to deltify against.  Flip the rightmost '1'
1896            bit of the predecessor count to determine which file rev (counting
1897            from 0) we want to use.  (To see why count & (count - 1) unsets the
1898            rightmost set bit, think about how you decrement a binary number. */
1899         pred_count = pred_count & (pred_count - 1);
1900
1901         /* Walk back a number of predecessors equal to the difference between
1902            pred_count and the original predecessor count.  (For example, if
1903            the node has ten predecessors and we want the eighth node, walk back
1904            two predecessors. */
1905         pred_id = id;
1906
1907         /* We need to use two alternating pools because the id used in the
1908            call to txn_body_pred_id is allocated by the previous inner
1909            loop iteration.  If we would clear the pool each iteration we
1910            would free the previous result.  */
1911         while ((pred_count++) < tpc_args.pred_count)
1912           {
1913             struct txn_pred_id_args tpi_args;
1914
1915             active_subpool = !active_subpool;
1916             svn_pool_clear(subpools[active_subpool]);
1917
1918             tpi_args.id = pred_id;
1919             tpi_args.pool = subpools[active_subpool];
1920             SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_id, &tpi_args,
1921                                            FALSE, subpools[active_subpool]));
1922             pred_id = tpi_args.pred_id;
1923
1924             if (pred_id == NULL)
1925               return svn_error_create
1926                 (SVN_ERR_FS_CORRUPT, 0,
1927                  _("Corrupt DB: faulty predecessor count"));
1928
1929           }
1930
1931         /* Finally, do the deltification. */
1932         td_args.txn_id = txn_id;
1933         td_args.tgt_id = id;
1934         td_args.base_id = pred_id;
1935         td_args.is_dir = (kind == svn_node_dir);
1936         SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_deltify, &td_args,
1937                                        TRUE, subpools[active_subpool]));
1938       }
1939     else
1940       {
1941         int nlevels, lev, count;
1942
1943         /**** REVERSE DELTA STORAGE ****/
1944
1945         /* Decide how many predecessors to redeltify.  To save overhead,
1946            don't redeltify anything but the immediate predecessor if there
1947            are less than 32 predecessors. */
1948         nlevels = 1;
1949         if (pred_count >= 32)
1950           {
1951             while (pred_count % 2 == 0)
1952               {
1953                 pred_count /= 2;
1954                 nlevels++;
1955               }
1956
1957             /* Don't redeltify the oldest revision. */
1958             if (1 << (nlevels - 1) == pred_count)
1959               nlevels--;
1960           }
1961
1962         /* Redeltify the desired number of predecessors. */
1963         count = 0;
1964         pred_id = id;
1965
1966         /* We need to use two alternating pools because the id used in the
1967            call to txn_body_pred_id is allocated by the previous inner
1968            loop iteration.  If we would clear the pool each iteration we
1969            would free the previous result.  */
1970         for (lev = 0; lev < nlevels; lev++)
1971           {
1972             /* To save overhead, skip the second level (that is, never
1973                redeltify the node-revision two predecessors back). */
1974             if (lev == 1)
1975               continue;
1976
1977             /* Note that COUNT is not reset between levels, and neither is
1978                PREDNODE; we just keep counting from where we were up to
1979                where we're supposed to get. */
1980             while (count < (1 << lev))
1981               {
1982                 struct txn_pred_id_args tpi_args;
1983
1984                 active_subpool = !active_subpool;
1985                 svn_pool_clear(subpools[active_subpool]);
1986
1987                 tpi_args.id = pred_id;
1988                 tpi_args.pool = subpools[active_subpool];
1989                 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_id,
1990                                                &tpi_args, FALSE,
1991                                                subpools[active_subpool]));
1992                 pred_id = tpi_args.pred_id;
1993
1994                 if (pred_id == NULL)
1995                   return svn_error_create
1996                     (SVN_ERR_FS_CORRUPT, 0,
1997                      _("Corrupt DB: faulty predecessor count"));
1998
1999                 count++;
2000               }
2001
2002             /* Finally, do the deltification. */
2003             td_args.txn_id = NULL;  /* Don't require mutable reps */
2004             td_args.tgt_id = pred_id;
2005             td_args.base_id = id;
2006             td_args.is_dir = (kind == svn_node_dir);
2007             SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_deltify, &td_args,
2008                                            TRUE, subpools[active_subpool]));
2009
2010           }
2011       }
2012
2013     svn_pool_destroy(subpools[0]);
2014     svn_pool_destroy(subpools[1]);
2015   }
2016
2017   return SVN_NO_ERROR;
2018 }
2019
2020
2021 struct get_root_args
2022 {
2023   svn_fs_root_t *root;
2024   dag_node_t *node;
2025 };
2026
2027
2028 /* Set ARGS->node to the root node of ARGS->root.  */
2029 static svn_error_t *
2030 txn_body_get_root(void *baton, trail_t *trail)
2031 {
2032   struct get_root_args *args = baton;
2033   return get_dag(&(args->node), args->root, "", trail, trail->pool);
2034 }
2035
2036
2037
2038 static svn_error_t *
2039 update_ancestry(svn_fs_t *fs,
2040                 const svn_fs_id_t *source_id,
2041                 const svn_fs_id_t *target_id,
2042                 const char *txn_id,
2043                 const char *target_path,
2044                 int source_pred_count,
2045                 trail_t *trail,
2046                 apr_pool_t *pool)
2047 {
2048   node_revision_t *noderev;
2049
2050   /* Set target's predecessor-id to source_id.  */
2051   if (strcmp(svn_fs_base__id_txn_id(target_id), txn_id))
2052     return svn_error_createf
2053       (SVN_ERR_FS_NOT_MUTABLE, NULL,
2054        _("Unexpected immutable node at '%s'"), target_path);
2055   SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, fs, target_id,
2056                                         trail, pool));
2057   noderev->predecessor_id = source_id;
2058   noderev->predecessor_count = source_pred_count;
2059   if (noderev->predecessor_count != -1)
2060     noderev->predecessor_count++;
2061   return svn_fs_bdb__put_node_revision(fs, target_id, noderev, trail, pool);
2062 }
2063
2064
2065 /* Set the contents of CONFLICT_PATH to PATH, and return an
2066    SVN_ERR_FS_CONFLICT error that indicates that there was a conflict
2067    at PATH.  Perform all allocations in POOL (except the allocation of
2068    CONFLICT_PATH, which should be handled outside this function).  */
2069 static svn_error_t *
2070 conflict_err(svn_stringbuf_t *conflict_path,
2071              const char *path)
2072 {
2073   svn_stringbuf_set(conflict_path, path);
2074   return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL,
2075                            _("Conflict at '%s'"), path);
2076 }
2077
2078
2079 /* Merge changes between ANCESTOR and SOURCE into TARGET as part of
2080  * TRAIL.  ANCESTOR and TARGET must be distinct node revisions.
2081  * TARGET_PATH should correspond to TARGET's full path in its
2082  * filesystem, and is used for reporting conflict location.
2083  *
2084  * SOURCE, TARGET, and ANCESTOR are generally directories; this
2085  * function recursively merges the directories' contents.  If any are
2086  * files, this function simply returns an error whenever SOURCE,
2087  * TARGET, and ANCESTOR are all distinct node revisions.
2088  *
2089  * If there are differences between ANCESTOR and SOURCE that conflict
2090  * with changes between ANCESTOR and TARGET, this function returns an
2091  * SVN_ERR_FS_CONFLICT error, and updates CONFLICT_P to the name of the
2092  * conflicting node in TARGET, with TARGET_PATH prepended as a path.
2093  *
2094  * If there are no conflicting differences, CONFLICT_P is updated to
2095  * the empty string.
2096  *
2097  * CONFLICT_P must point to a valid svn_stringbuf_t.
2098  *
2099  * Do any necessary temporary allocation in POOL.
2100  */
2101 static svn_error_t *
2102 merge(svn_stringbuf_t *conflict_p,
2103       const char *target_path,
2104       dag_node_t *target,
2105       dag_node_t *source,
2106       dag_node_t *ancestor,
2107       const char *txn_id,
2108       apr_int64_t *mergeinfo_increment_out,
2109       trail_t *trail,
2110       apr_pool_t *pool)
2111 {
2112   const svn_fs_id_t *source_id, *target_id, *ancestor_id;
2113   apr_hash_t *s_entries, *t_entries, *a_entries;
2114   apr_hash_index_t *hi;
2115   apr_pool_t *iterpool;
2116   svn_fs_t *fs;
2117   int pred_count;
2118   apr_int64_t mergeinfo_increment = 0;
2119   base_fs_data_t *bfd = trail->fs->fsap_data;
2120
2121   /* Make sure everyone comes from the same filesystem. */
2122   fs = svn_fs_base__dag_get_fs(ancestor);
2123   if ((fs != svn_fs_base__dag_get_fs(source))
2124       || (fs != svn_fs_base__dag_get_fs(target)))
2125     {
2126       return svn_error_create
2127         (SVN_ERR_FS_CORRUPT, NULL,
2128          _("Bad merge; ancestor, source, and target not all in same fs"));
2129     }
2130
2131   /* We have the same fs, now check it. */
2132   SVN_ERR(svn_fs__check_fs(fs, TRUE));
2133
2134   source_id   = svn_fs_base__dag_get_id(source);
2135   target_id   = svn_fs_base__dag_get_id(target);
2136   ancestor_id = svn_fs_base__dag_get_id(ancestor);
2137
2138   /* It's improper to call this function with ancestor == target. */
2139   if (svn_fs_base__id_eq(ancestor_id, target_id))
2140     {
2141       svn_string_t *id_str = svn_fs_base__id_unparse(target_id, pool);
2142       return svn_error_createf
2143         (SVN_ERR_FS_GENERAL, NULL,
2144          _("Bad merge; target '%s' has id '%s', same as ancestor"),
2145          target_path, id_str->data);
2146     }
2147
2148   svn_stringbuf_setempty(conflict_p);
2149
2150   /* Base cases:
2151    * Either no change made in source, or same change as made in target.
2152    * Both mean nothing to merge here.
2153    */
2154   if (svn_fs_base__id_eq(ancestor_id, source_id)
2155       || (svn_fs_base__id_eq(source_id, target_id)))
2156     return SVN_NO_ERROR;
2157
2158   /* Else proceed, knowing all three are distinct node revisions.
2159    *
2160    * How to merge from this point:
2161    *
2162    * if (not all 3 are directories)
2163    *   {
2164    *     early exit with conflict;
2165    *   }
2166    *
2167    * // Property changes may only be made to up-to-date
2168    * // directories, because once the client commits the prop
2169    * // change, it bumps the directory's revision, and therefore
2170    * // must be able to depend on there being no other changes to
2171    * // that directory in the repository.
2172    * if (target's property list differs from ancestor's)
2173    *    conflict;
2174    *
2175    * For each entry NAME in the directory ANCESTOR:
2176    *
2177    *   Let ANCESTOR-ENTRY, SOURCE-ENTRY, and TARGET-ENTRY be the IDs of
2178    *   the name within ANCESTOR, SOURCE, and TARGET respectively.
2179    *   (Possibly null if NAME does not exist in SOURCE or TARGET.)
2180    *
2181    *   If ANCESTOR-ENTRY == SOURCE-ENTRY, then:
2182    *     No changes were made to this entry while the transaction was in
2183    *     progress, so do nothing to the target.
2184    *
2185    *   Else if ANCESTOR-ENTRY == TARGET-ENTRY, then:
2186    *     A change was made to this entry while the transaction was in
2187    *     process, but the transaction did not touch this entry.  Replace
2188    *     TARGET-ENTRY with SOURCE-ENTRY.
2189    *
2190    *   Else:
2191    *     Changes were made to this entry both within the transaction and
2192    *     to the repository while the transaction was in progress.  They
2193    *     must be merged or declared to be in conflict.
2194    *
2195    *     If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
2196    *     double delete; flag a conflict.
2197    *
2198    *     If any of the three entries is of type file, declare a conflict.
2199    *
2200    *     If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
2201    *     modification of ANCESTOR-ENTRY (determine by comparing the
2202    *     node-id fields), declare a conflict.  A replacement is
2203    *     incompatible with a modification or other replacement--even
2204    *     an identical replacement.
2205    *
2206    *     Direct modifications were made to the directory ANCESTOR-ENTRY
2207    *     in both SOURCE and TARGET.  Recursively merge these
2208    *     modifications.
2209    *
2210    * For each leftover entry NAME in the directory SOURCE:
2211    *
2212    *   If NAME exists in TARGET, declare a conflict.  Even if SOURCE and
2213    *   TARGET are adding exactly the same thing, two additions are not
2214    *   auto-mergeable with each other.
2215    *
2216    *   Add NAME to TARGET with the entry from SOURCE.
2217    *
2218    * Now that we are done merging the changes from SOURCE into the
2219    * directory TARGET, update TARGET's predecessor to be SOURCE.
2220    */
2221
2222   if ((svn_fs_base__dag_node_kind(source) != svn_node_dir)
2223       || (svn_fs_base__dag_node_kind(target) != svn_node_dir)
2224       || (svn_fs_base__dag_node_kind(ancestor) != svn_node_dir))
2225     {
2226       return conflict_err(conflict_p, target_path);
2227     }
2228
2229
2230   /* Possible early merge failure: if target and ancestor have
2231      different property lists, then the merge should fail.
2232      Propchanges can *only* be committed on an up-to-date directory.
2233      ### TODO: see issue #418 about the inelegance of this.
2234
2235      Another possible, similar, early merge failure: if source and
2236      ancestor have different property lists (meaning someone else
2237      changed directory properties while our commit transaction was
2238      happening), the merge should fail.  See issue #2751.
2239   */
2240   {
2241     node_revision_t *tgt_nr, *anc_nr, *src_nr;
2242
2243     /* Get node revisions for our id's. */
2244     SVN_ERR(svn_fs_bdb__get_node_revision(&tgt_nr, fs, target_id,
2245                                           trail, pool));
2246     SVN_ERR(svn_fs_bdb__get_node_revision(&anc_nr, fs, ancestor_id,
2247                                           trail, pool));
2248     SVN_ERR(svn_fs_bdb__get_node_revision(&src_nr, fs, source_id,
2249                                           trail, pool));
2250
2251     /* Now compare the prop-keys of the skels.  Note that just because
2252        the keys are different -doesn't- mean the proplists have
2253        different contents.  But merge() isn't concerned with contents;
2254        it doesn't do a brute-force comparison on textual contents, so
2255        it won't do that here either.  Checking to see if the propkey
2256        atoms are `equal' is enough. */
2257     if (! svn_fs_base__same_keys(tgt_nr->prop_key, anc_nr->prop_key))
2258       return conflict_err(conflict_p, target_path);
2259     if (! svn_fs_base__same_keys(src_nr->prop_key, anc_nr->prop_key))
2260       return conflict_err(conflict_p, target_path);
2261   }
2262
2263   /* ### todo: it would be more efficient to simply check for a NULL
2264      entries hash where necessary below than to allocate an empty hash
2265      here, but another day, another day... */
2266   SVN_ERR(svn_fs_base__dag_dir_entries(&s_entries, source, trail, pool));
2267   if (! s_entries)
2268     s_entries = apr_hash_make(pool);
2269   SVN_ERR(svn_fs_base__dag_dir_entries(&t_entries, target, trail, pool));
2270   if (! t_entries)
2271     t_entries = apr_hash_make(pool);
2272   SVN_ERR(svn_fs_base__dag_dir_entries(&a_entries, ancestor, trail, pool));
2273   if (! a_entries)
2274     a_entries = apr_hash_make(pool);
2275
2276   /* for each entry E in a_entries... */
2277   iterpool = svn_pool_create(pool);
2278   for (hi = apr_hash_first(pool, a_entries);
2279        hi;
2280        hi = apr_hash_next(hi))
2281     {
2282       svn_fs_dirent_t *s_entry, *t_entry, *a_entry;
2283
2284       const void *key;
2285       void *val;
2286       apr_ssize_t klen;
2287
2288       svn_pool_clear(iterpool);
2289
2290       /* KEY will be the entry name in ancestor, VAL the dirent */
2291       apr_hash_this(hi, &key, &klen, &val);
2292       a_entry = val;
2293
2294       s_entry = apr_hash_get(s_entries, key, klen);
2295       t_entry = apr_hash_get(t_entries, key, klen);
2296
2297       /* No changes were made to this entry while the transaction was
2298          in progress, so do nothing to the target. */
2299       if (s_entry && svn_fs_base__id_eq(a_entry->id, s_entry->id))
2300         goto end;
2301
2302       /* A change was made to this entry while the transaction was in
2303          process, but the transaction did not touch this entry. */
2304       else if (t_entry && svn_fs_base__id_eq(a_entry->id, t_entry->id))
2305         {
2306           dag_node_t *t_ent_node;
2307           apr_int64_t mergeinfo_start;
2308           SVN_ERR(svn_fs_base__dag_get_node(&t_ent_node, fs,
2309                                             t_entry->id, trail, iterpool));
2310           SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, &mergeinfo_start,
2311                                                        t_ent_node, trail,
2312                                                        iterpool));
2313           mergeinfo_increment -= mergeinfo_start;
2314
2315            if (s_entry)
2316              {
2317               dag_node_t *s_ent_node;
2318               apr_int64_t mergeinfo_end;
2319               SVN_ERR(svn_fs_base__dag_get_node(&s_ent_node, fs,
2320                                                 s_entry->id, trail,
2321                                                 iterpool));
2322               SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL,
2323                                                            &mergeinfo_end,
2324                                                            s_ent_node, trail,
2325                                                            iterpool));
2326               mergeinfo_increment += mergeinfo_end;
2327               SVN_ERR(svn_fs_base__dag_set_entry(target, key, s_entry->id,
2328                                                  txn_id, trail, iterpool));
2329             }
2330           else
2331             {
2332               SVN_ERR(svn_fs_base__dag_delete(target, key, txn_id,
2333                                               trail, iterpool));
2334             }
2335         }
2336
2337       /* Changes were made to this entry both within the transaction
2338          and to the repository while the transaction was in progress.
2339          They must be merged or declared to be in conflict. */
2340       else
2341         {
2342           dag_node_t *s_ent_node, *t_ent_node, *a_ent_node;
2343           const char *new_tpath;
2344           apr_int64_t sub_mergeinfo_increment;
2345
2346           /* If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
2347              double delete; if one of them is null, that's a delete versus
2348              a modification. In any of these cases, flag a conflict. */
2349           if (s_entry == NULL || t_entry == NULL)
2350             return conflict_err(conflict_p,
2351                                 svn_fspath__join(target_path,
2352                                                 a_entry->name,
2353                                                 iterpool));
2354
2355           /* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
2356              modification of ANCESTOR-ENTRY, declare a conflict. */
2357           if (strcmp(svn_fs_base__id_node_id(s_entry->id),
2358                      svn_fs_base__id_node_id(a_entry->id)) != 0
2359               || strcmp(svn_fs_base__id_copy_id(s_entry->id),
2360                         svn_fs_base__id_copy_id(a_entry->id)) != 0
2361               || strcmp(svn_fs_base__id_node_id(t_entry->id),
2362                         svn_fs_base__id_node_id(a_entry->id)) != 0
2363               || strcmp(svn_fs_base__id_copy_id(t_entry->id),
2364                         svn_fs_base__id_copy_id(a_entry->id)) != 0)
2365             return conflict_err(conflict_p,
2366                                 svn_fspath__join(target_path,
2367                                                 a_entry->name,
2368                                                 iterpool));
2369
2370           /* Fetch the nodes for our entries. */
2371           SVN_ERR(svn_fs_base__dag_get_node(&s_ent_node, fs,
2372                                             s_entry->id, trail, iterpool));
2373           SVN_ERR(svn_fs_base__dag_get_node(&t_ent_node, fs,
2374                                             t_entry->id, trail, iterpool));
2375           SVN_ERR(svn_fs_base__dag_get_node(&a_ent_node, fs,
2376                                             a_entry->id, trail, iterpool));
2377
2378           /* If any of the three entries is of type file, flag a conflict. */
2379           if ((svn_fs_base__dag_node_kind(s_ent_node) == svn_node_file)
2380               || (svn_fs_base__dag_node_kind(t_ent_node) == svn_node_file)
2381               || (svn_fs_base__dag_node_kind(a_ent_node) == svn_node_file))
2382             return conflict_err(conflict_p,
2383                                 svn_fspath__join(target_path,
2384                                                 a_entry->name,
2385                                                 iterpool));
2386
2387           /* Direct modifications were made to the directory
2388              ANCESTOR-ENTRY in both SOURCE and TARGET.  Recursively
2389              merge these modifications. */
2390           new_tpath = svn_fspath__join(target_path, t_entry->name, iterpool);
2391           SVN_ERR(merge(conflict_p, new_tpath,
2392                         t_ent_node, s_ent_node, a_ent_node,
2393                         txn_id, &sub_mergeinfo_increment, trail, iterpool));
2394           mergeinfo_increment += sub_mergeinfo_increment;
2395         }
2396
2397       /* We've taken care of any possible implications E could have.
2398          Remove it from source_entries, so it's easy later to loop
2399          over all the source entries that didn't exist in
2400          ancestor_entries. */
2401     end:
2402       apr_hash_set(s_entries, key, klen, NULL);
2403     }
2404
2405   /* For each entry E in source but not in ancestor */
2406   for (hi = apr_hash_first(pool, s_entries);
2407        hi;
2408        hi = apr_hash_next(hi))
2409     {
2410       svn_fs_dirent_t *s_entry, *t_entry;
2411       const void *key;
2412       void *val;
2413       apr_ssize_t klen;
2414       dag_node_t *s_ent_node;
2415       apr_int64_t mergeinfo_s;
2416
2417       svn_pool_clear(iterpool);
2418
2419       apr_hash_this(hi, &key, &klen, &val);
2420       s_entry = val;
2421       t_entry = apr_hash_get(t_entries, key, klen);
2422
2423       /* If NAME exists in TARGET, declare a conflict. */
2424       if (t_entry)
2425         return conflict_err(conflict_p,
2426                             svn_fspath__join(target_path,
2427                                             t_entry->name,
2428                                             iterpool));
2429
2430       SVN_ERR(svn_fs_base__dag_get_node(&s_ent_node, fs,
2431                                         s_entry->id, trail, iterpool));
2432       SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, &mergeinfo_s,
2433                                                    s_ent_node, trail,
2434                                                    iterpool));
2435       mergeinfo_increment += mergeinfo_s;
2436       SVN_ERR(svn_fs_base__dag_set_entry
2437               (target, s_entry->name, s_entry->id, txn_id, trail, iterpool));
2438     }
2439   svn_pool_destroy(iterpool);
2440
2441   /* Now that TARGET has absorbed all of the history between ANCESTOR
2442      and SOURCE, we can update its predecessor to point to SOURCE.  */
2443   SVN_ERR(svn_fs_base__dag_get_predecessor_count(&pred_count, source,
2444                                                  trail, pool));
2445   SVN_ERR(update_ancestry(fs, source_id, target_id, txn_id, target_path,
2446                           pred_count, trail, pool));
2447
2448   /* Tweak mergeinfo data if our format supports it. */
2449   if (bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
2450     {
2451       SVN_ERR(svn_fs_base__dag_adjust_mergeinfo_count(target,
2452                                                       mergeinfo_increment,
2453                                                       txn_id, trail, pool));
2454     }
2455
2456   if (mergeinfo_increment_out)
2457     *mergeinfo_increment_out = mergeinfo_increment;
2458
2459   return SVN_NO_ERROR;
2460 }
2461
2462
2463 struct merge_args
2464 {
2465   /* The ancestor for the merge.  If this is null, then TXN's base is
2466      used as the ancestor for the merge. */
2467   dag_node_t *ancestor_node;
2468
2469   /* This is the SOURCE node for the merge.  It may not be null. */
2470   dag_node_t *source_node;
2471
2472   /* This is the TARGET of the merge.  It may not be null.  If
2473      ancestor_node above is null, then this txn's base is used as the
2474      ancestor for the merge. */
2475   svn_fs_txn_t *txn;
2476
2477   /* If a conflict results, this is updated to the path in the txn that
2478      conflicted.  It must point to a valid svn_stringbuf_t before calling
2479      svn_fs_base__retry_txn, as this determines the pool used to allocate any
2480      required memory. */
2481   svn_stringbuf_t *conflict;
2482 };
2483
2484
2485 /* Merge changes between an ancestor and BATON->source_node into
2486    BATON->txn.  The ancestor is either BATON->ancestor_node, or if
2487    that is null, BATON->txn's base node.
2488
2489    If the merge is successful, BATON->txn's base will become
2490    BATON->source_node, and its root node will have a new ID, a
2491    successor of BATON->source_node. */
2492 static svn_error_t *
2493 txn_body_merge(void *baton, trail_t *trail)
2494 {
2495   struct merge_args *args = baton;
2496   dag_node_t *source_node, *txn_root_node, *ancestor_node;
2497   const svn_fs_id_t *source_id;
2498   svn_fs_t *fs = args->txn->fs;
2499   const char *txn_id = args->txn->id;
2500
2501   source_node = args->source_node;
2502   ancestor_node = args->ancestor_node;
2503   source_id = svn_fs_base__dag_get_id(source_node);
2504
2505   SVN_ERR(svn_fs_base__dag_txn_root(&txn_root_node, fs, txn_id,
2506                                     trail, trail->pool));
2507
2508   if (ancestor_node == NULL)
2509     {
2510       SVN_ERR(svn_fs_base__dag_txn_base_root(&ancestor_node, fs,
2511                                              txn_id, trail, trail->pool));
2512     }
2513
2514   if (svn_fs_base__id_eq(svn_fs_base__dag_get_id(ancestor_node),
2515                          svn_fs_base__dag_get_id(txn_root_node)))
2516     {
2517       /* If no changes have been made in TXN since its current base,
2518          then it can't conflict with any changes since that base.  So
2519          we just set *both* its base and root to source, making TXN
2520          in effect a repeat of source. */
2521
2522       /* ### kff todo: this would, of course, be a mighty silly thing
2523          for the caller to do, and we might want to consider whether
2524          this response is really appropriate. */
2525
2526       SVN_ERR(svn_fs_base__set_txn_base(fs, txn_id, source_id,
2527                                         trail, trail->pool));
2528       SVN_ERR(svn_fs_base__set_txn_root(fs, txn_id, source_id,
2529                                         trail, trail->pool));
2530     }
2531   else
2532     {
2533       int pred_count;
2534
2535       SVN_ERR(merge(args->conflict, "/", txn_root_node, source_node,
2536                     ancestor_node, txn_id, NULL, trail, trail->pool));
2537
2538       SVN_ERR(svn_fs_base__dag_get_predecessor_count(&pred_count,
2539                                                      source_node, trail,
2540                                                      trail->pool));
2541
2542       /* After the merge, txn's new "ancestor" is now really the node
2543          at source_id, so record that fact.  Think of this as
2544          ratcheting the txn forward in time, so it can't backslide and
2545          forget the merging work that's already been done. */
2546       SVN_ERR(update_ancestry(fs, source_id,
2547                               svn_fs_base__dag_get_id(txn_root_node),
2548                               txn_id, "/", pred_count, trail, trail->pool));
2549       SVN_ERR(svn_fs_base__set_txn_base(fs, txn_id, source_id,
2550                                         trail, trail->pool));
2551     }
2552
2553   return SVN_NO_ERROR;
2554 }
2555
2556
2557 /* Verify that there are registered with TRAIL->fs all the locks
2558    necessary to permit all the changes associated with TXN_NAME. */
2559 static svn_error_t *
2560 verify_locks(const char *txn_name,
2561              trail_t *trail,
2562              apr_pool_t *pool)
2563 {
2564   apr_pool_t *subpool = svn_pool_create(pool);
2565   apr_hash_t *changes;
2566   apr_hash_index_t *hi;
2567   apr_array_header_t *changed_paths;
2568   svn_stringbuf_t *last_recursed = NULL;
2569   int i;
2570
2571   /* Fetch the changes for this transaction. */
2572   SVN_ERR(svn_fs_bdb__changes_fetch(&changes, trail->fs, txn_name,
2573                                     trail, pool));
2574
2575   /* Make an array of the changed paths, and sort them depth-first-ily.  */
2576   changed_paths = apr_array_make(pool, apr_hash_count(changes) + 1,
2577                                  sizeof(const char *));
2578   for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
2579     {
2580       const void *key;
2581       apr_hash_this(hi, &key, NULL, NULL);
2582       APR_ARRAY_PUSH(changed_paths, const char *) = key;
2583     }
2584   svn_sort__array(changed_paths, svn_sort_compare_paths);
2585
2586   /* Now, traverse the array of changed paths, verify locks.  Note
2587      that if we need to do a recursive verification a path, we'll skip
2588      over children of that path when we get to them. */
2589   for (i = 0; i < changed_paths->nelts; i++)
2590     {
2591       const char *path;
2592       svn_fs_path_change2_t *change;
2593       svn_boolean_t recurse = TRUE;
2594
2595       svn_pool_clear(subpool);
2596       path = APR_ARRAY_IDX(changed_paths, i, const char *);
2597
2598       /* If this path has already been verified as part of a recursive
2599          check of one of its parents, no need to do it again.  */
2600       if (last_recursed
2601           && svn_fspath__skip_ancestor(last_recursed->data, path))
2602         continue;
2603
2604       /* Fetch the change associated with our path.  */
2605       change = svn_hash_gets(changes, path);
2606
2607       /* What does it mean to succeed at lock verification for a given
2608          path?  For an existing file or directory getting modified
2609          (text, props), it means we hold the lock on the file or
2610          directory.  For paths being added or removed, we need to hold
2611          the locks for that path and any children of that path.
2612
2613          WHEW!  We have no reliable way to determine the node kind of
2614          deleted items, but fortunately we are going to do a recursive
2615          check on deleted paths regardless of their kind.  */
2616       if (change->change_kind == svn_fs_path_change_modify)
2617         recurse = FALSE;
2618       SVN_ERR(svn_fs_base__allow_locked_operation(path, recurse,
2619                                                   trail, subpool));
2620
2621       /* If we just did a recursive check, remember the path we
2622          checked (so children can be skipped).  */
2623       if (recurse)
2624         {
2625           if (! last_recursed)
2626             last_recursed = svn_stringbuf_create(path, pool);
2627           else
2628             svn_stringbuf_set(last_recursed, path);
2629         }
2630     }
2631   svn_pool_destroy(subpool);
2632   return SVN_NO_ERROR;
2633 }
2634
2635
2636 struct commit_args
2637 {
2638   svn_fs_txn_t *txn;
2639   svn_revnum_t new_rev;
2640 };
2641
2642
2643 /* Commit ARGS->txn, setting ARGS->new_rev to the resulting new
2644  * revision, if ARGS->txn is up-to-date with respect to the repository.
2645  *
2646  * Up-to-date means that ARGS->txn's base root is the same as the root
2647  * of the youngest revision.  If ARGS->txn is not up-to-date, the
2648  * error SVN_ERR_FS_TXN_OUT_OF_DATE is returned, and the commit fails: no
2649  * new revision is created, and ARGS->new_rev is not touched.
2650  *
2651  * If the commit succeeds, ARGS->txn is destroyed.
2652  */
2653 static svn_error_t *
2654 txn_body_commit(void *baton, trail_t *trail)
2655 {
2656   struct commit_args *args = baton;
2657
2658   svn_fs_txn_t *txn = args->txn;
2659   svn_fs_t *fs = txn->fs;
2660   const char *txn_name = txn->id;
2661
2662   svn_revnum_t youngest_rev;
2663   const svn_fs_id_t *y_rev_root_id;
2664   dag_node_t *txn_base_root_node, *txn_root_node;
2665
2666   /* Getting the youngest revision locks the revisions table until
2667      this trail is done. */
2668   SVN_ERR(svn_fs_bdb__youngest_rev(&youngest_rev, fs, trail, trail->pool));
2669
2670   /* If the root of the youngest revision is the same as txn's base,
2671      then no further merging is necessary and we can commit. */
2672   SVN_ERR(svn_fs_base__rev_get_root(&y_rev_root_id, fs, youngest_rev,
2673                                     trail, trail->pool));
2674   SVN_ERR(svn_fs_base__dag_txn_base_root(&txn_base_root_node, fs, txn_name,
2675                                          trail, trail->pool));
2676   /* ### kff todo: it seems weird to grab the ID for one, and the node
2677      for the other.  We can certainly do the comparison we need, but
2678      it would be nice to grab the same type of information from the
2679      start, instead of having to transform one of them. */
2680   if (! svn_fs_base__id_eq(y_rev_root_id,
2681                            svn_fs_base__dag_get_id(txn_base_root_node)))
2682     {
2683       svn_string_t *id_str = svn_fs_base__id_unparse(y_rev_root_id,
2684                                                      trail->pool);
2685       return svn_error_createf
2686         (SVN_ERR_FS_TXN_OUT_OF_DATE, NULL,
2687          _("Transaction '%s' out-of-date with respect to revision '%s'"),
2688          txn_name, id_str->data);
2689     }
2690
2691   /* Locks may have been added (or stolen) between the calling of
2692      previous svn_fs.h functions and svn_fs_commit_txn(), so we need
2693      to re-examine every changed-path in the txn and re-verify all
2694      discovered locks. */
2695   SVN_ERR(verify_locks(txn_name, trail, trail->pool));
2696
2697   /* Ensure every txn has a mutable root as then the new revision will
2698      have a distinct root node-revision-id.  This is necessary as
2699      future transactions use the root node-revision-id as a proxy for
2700      the transaction base revision. */
2701   SVN_ERR(svn_fs_base__dag_txn_root(&txn_root_node, fs, txn_name,
2702                                     trail, trail->pool));
2703   if (!svn_fs_base__dag_check_mutable(txn_root_node, txn->id))
2704     {
2705       dag_node_t *clone;
2706       SVN_ERR(svn_fs_base__dag_clone_root(&clone, fs, txn->id,
2707                                           trail, trail->pool));
2708     }
2709
2710   /* Else, commit the txn. */
2711   return svn_fs_base__dag_commit_txn(&(args->new_rev), txn, trail,
2712                                      trail->pool);
2713 }
2714
2715
2716 /* Note:  it is acceptable for this function to call back into
2717    top-level FS interfaces because it does not itself use trails.  */
2718 svn_error_t *
2719 svn_fs_base__commit_txn(const char **conflict_p,
2720                         svn_revnum_t *new_rev,
2721                         svn_fs_txn_t *txn,
2722                         apr_pool_t *pool)
2723 {
2724   /* How do commits work in Subversion?
2725    *
2726    * When you're ready to commit, here's what you have:
2727    *
2728    *    1. A transaction, with a mutable tree hanging off it.
2729    *    2. A base revision, against which TXN_TREE was made.
2730    *    3. A latest revision, which may be newer than the base rev.
2731    *
2732    * The problem is that if latest != base, then one can't simply
2733    * attach the txn root as the root of the new revision, because that
2734    * would lose all the changes between base and latest.  It is also
2735    * not acceptable to insist that base == latest; in a busy
2736    * repository, commits happen too fast to insist that everyone keep
2737    * their entire tree up-to-date at all times.  Non-overlapping
2738    * changes should not interfere with each other.
2739    *
2740    * The solution is to merge the changes between base and latest into
2741    * the txn tree [see the function merge()].  The txn tree is the
2742    * only one of the three trees that is mutable, so it has to be the
2743    * one to adjust.
2744    *
2745    * You might have to adjust it more than once, if a new latest
2746    * revision gets committed while you were merging in the previous
2747    * one.  For example:
2748    *
2749    *    1. Jane starts txn T, based at revision 6.
2750    *    2. Someone commits (or already committed) revision 7.
2751    *    3. Jane's starts merging the changes between 6 and 7 into T.
2752    *    4. Meanwhile, someone commits revision 8.
2753    *    5. Jane finishes the 6-->7 merge.  T could now be committed
2754    *       against a latest revision of 7, if only that were still the
2755    *       latest.  Unfortunately, 8 is now the latest, so...
2756    *    6. Jane starts merging the changes between 7 and 8 into T.
2757    *    7. Meanwhile, no one commits any new revisions.  Whew.
2758    *    8. Jane commits T, creating revision 9, whose tree is exactly
2759    *       T's tree, except immutable now.
2760    *
2761    * Lather, rinse, repeat.
2762    */
2763
2764   svn_error_t *err;
2765   svn_fs_t *fs = txn->fs;
2766   apr_pool_t *subpool = svn_pool_create(pool);
2767
2768   /* Initialize output params. */
2769   *new_rev = SVN_INVALID_REVNUM;
2770   if (conflict_p)
2771     *conflict_p = NULL;
2772
2773   while (1729)
2774     {
2775       struct get_root_args get_root_args;
2776       struct merge_args merge_args;
2777       struct commit_args commit_args;
2778       svn_revnum_t youngish_rev;
2779       svn_fs_root_t *youngish_root;
2780       dag_node_t *youngish_root_node;
2781
2782       svn_pool_clear(subpool);
2783
2784       /* Get the *current* youngest revision, in one short-lived
2785          Berkeley transaction.  (We don't want the revisions table
2786          locked while we do the main merge.)  We call it "youngish"
2787          because new revisions might get committed after we've
2788          obtained it. */
2789
2790       SVN_ERR(svn_fs_base__youngest_rev(&youngish_rev, fs, subpool));
2791       SVN_ERR(svn_fs_base__revision_root(&youngish_root, fs, youngish_rev,
2792                                          subpool));
2793
2794       /* Get the dag node for the youngest revision, also in one
2795          Berkeley transaction.  Later we'll use it as the SOURCE
2796          argument to a merge, and if the merge succeeds, this youngest
2797          root node will become the new base root for the svn txn that
2798          was the target of the merge (but note that the youngest rev
2799          may have changed by then -- that's why we're careful to get
2800          this root in its own bdb txn here). */
2801       get_root_args.root = youngish_root;
2802       SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_root, &get_root_args,
2803                                      FALSE, subpool));
2804       youngish_root_node = get_root_args.node;
2805
2806       /* Try to merge.  If the merge succeeds, the base root node of
2807          TARGET's txn will become the same as youngish_root_node, so
2808          any future merges will only be between that node and whatever
2809          the root node of the youngest rev is by then. */
2810       merge_args.ancestor_node = NULL;
2811       merge_args.source_node = youngish_root_node;
2812       merge_args.txn = txn;
2813       merge_args.conflict = svn_stringbuf_create_empty(pool); /* use pool */
2814       err = svn_fs_base__retry_txn(fs, txn_body_merge, &merge_args,
2815                                    FALSE, subpool);
2816       if (err)
2817         {
2818           if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2819             *conflict_p = merge_args.conflict->data;
2820           return svn_error_trace(err);
2821         }
2822
2823       /* Try to commit. */
2824       commit_args.txn = txn;
2825       err = svn_fs_base__retry_txn(fs, txn_body_commit, &commit_args,
2826                                    FALSE, subpool);
2827       if (err && (err->apr_err == SVN_ERR_FS_TXN_OUT_OF_DATE))
2828         {
2829           /* Did someone else finish committing a new revision while we
2830              were in mid-merge or mid-commit?  If so, we'll need to
2831              loop again to merge the new changes in, then try to
2832              commit again.  Or if that's not what happened, then just
2833              return the error. */
2834           svn_revnum_t youngest_rev;
2835           svn_error_t *err2 = svn_fs_base__youngest_rev(&youngest_rev, fs,
2836                                                         subpool);
2837           if (err2)
2838             {
2839               svn_error_clear(err);
2840               return svn_error_trace(err2);  /* err2 is bad,
2841                                                  it should not occur */
2842             }
2843           else if (youngest_rev == youngish_rev)
2844             return svn_error_trace(err);
2845           else
2846             svn_error_clear(err);
2847         }
2848       else if (err)
2849         {
2850           return svn_error_trace(err);
2851         }
2852       else
2853         {
2854           /* Set the return value -- our brand spankin' new revision! */
2855           *new_rev = commit_args.new_rev;
2856           break;
2857         }
2858     }
2859
2860   svn_pool_destroy(subpool);
2861   return SVN_NO_ERROR;
2862 }
2863
2864 /* Note:  it is acceptable for this function to call back into
2865    public FS API interfaces because it does not itself use trails.  */
2866 static svn_error_t *
2867 base_merge(const char **conflict_p,
2868            svn_fs_root_t *source_root,
2869            const char *source_path,
2870            svn_fs_root_t *target_root,
2871            const char *target_path,
2872            svn_fs_root_t *ancestor_root,
2873            const char *ancestor_path,
2874            apr_pool_t *pool)
2875 {
2876   dag_node_t *source, *ancestor;
2877   struct get_root_args get_root_args;
2878   struct merge_args merge_args;
2879   svn_fs_txn_t *txn;
2880   svn_error_t *err;
2881   svn_fs_t *fs;
2882
2883   if (! target_root->is_txn_root)
2884     return SVN_FS__NOT_TXN(target_root);
2885
2886   /* Paranoia. */
2887   fs = ancestor_root->fs;
2888   if ((source_root->fs != fs) || (target_root->fs != fs))
2889     {
2890       return svn_error_create
2891         (SVN_ERR_FS_CORRUPT, NULL,
2892          _("Bad merge; ancestor, source, and target not all in same fs"));
2893     }
2894
2895   /* ### kff todo: is there any compelling reason to get the nodes in
2896      one db transaction?  Right now we don't; txn_body_get_root() gets
2897      one node at a time.  This will probably need to change:
2898
2899      Jim Blandy <jimb@zwingli.cygnus.com> writes:
2900      > svn_fs_merge needs to be a single transaction, to protect it against
2901      > people deleting parents of nodes it's working on, etc.
2902   */
2903
2904   /* Get the ancestor node. */
2905   get_root_args.root = ancestor_root;
2906   SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_root, &get_root_args,
2907                                  FALSE, pool));
2908   ancestor = get_root_args.node;
2909
2910   /* Get the source node. */
2911   get_root_args.root = source_root;
2912   SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_root, &get_root_args,
2913                                  FALSE, pool));
2914   source = get_root_args.node;
2915
2916   /* Open a txn for the txn root into which we're merging. */
2917   SVN_ERR(svn_fs_base__open_txn(&txn, fs, target_root->txn, pool));
2918
2919   /* Merge changes between ANCESTOR and SOURCE into TXN. */
2920   merge_args.source_node = source;
2921   merge_args.ancestor_node = ancestor;
2922   merge_args.txn = txn;
2923   merge_args.conflict = svn_stringbuf_create_empty(pool);
2924   err = svn_fs_base__retry_txn(fs, txn_body_merge, &merge_args, FALSE, pool);
2925   if (err)
2926     {
2927       if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2928         *conflict_p = merge_args.conflict->data;
2929       return svn_error_trace(err);
2930     }
2931
2932   return SVN_NO_ERROR;
2933 }
2934
2935
2936 struct rev_get_txn_id_args
2937 {
2938   const char **txn_id;
2939   svn_revnum_t revision;
2940 };
2941
2942
2943 static svn_error_t *
2944 txn_body_rev_get_txn_id(void *baton, trail_t *trail)
2945 {
2946   struct rev_get_txn_id_args *args = baton;
2947   return svn_fs_base__rev_get_txn_id(args->txn_id, trail->fs,
2948                                      args->revision, trail, trail->pool);
2949 }
2950
2951
2952 svn_error_t *
2953 svn_fs_base__deltify(svn_fs_t *fs,
2954                      svn_revnum_t revision,
2955                      apr_pool_t *pool)
2956 {
2957   svn_fs_root_t *root;
2958   const char *txn_id;
2959   struct rev_get_txn_id_args args;
2960   base_fs_data_t *bfd = fs->fsap_data;
2961
2962   if (bfd->format >= SVN_FS_BASE__MIN_MISCELLANY_FORMAT)
2963     {
2964       const char *val;
2965       svn_revnum_t forward_delta_rev = 0;
2966
2967       SVN_ERR(svn_fs_base__miscellaneous_get
2968               (&val, fs, SVN_FS_BASE__MISC_FORWARD_DELTA_UPGRADE, pool));
2969       if (val)
2970         SVN_ERR(svn_revnum_parse(&forward_delta_rev, val, NULL));
2971
2972       /* ### FIXME:  Unnecessarily harsh requirement? (cmpilato). */
2973       if (revision <= forward_delta_rev)
2974         return svn_error_createf
2975           (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2976            _("Cannot deltify revisions prior to r%ld"), forward_delta_rev+1);
2977     }
2978
2979   SVN_ERR(svn_fs_base__revision_root(&root, fs, revision, pool));
2980
2981   args.txn_id = &txn_id;
2982   args.revision = revision;
2983   SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_rev_get_txn_id, &args,
2984                                  FALSE, pool));
2985
2986   return deltify_mutable(fs, root, "/", NULL, svn_node_dir, txn_id, pool);
2987 }
2988
2989 \f
2990 /* Modifying directories */
2991
2992
2993 struct make_dir_args
2994 {
2995   svn_fs_root_t *root;
2996   const char *path;
2997 };
2998
2999
3000 static svn_error_t *
3001 txn_body_make_dir(void *baton,
3002                   trail_t *trail)
3003 {
3004   struct make_dir_args *args = baton;
3005   svn_fs_root_t *root = args->root;
3006   const char *path = args->path;
3007   parent_path_t *parent_path;
3008   dag_node_t *sub_dir;
3009   const char *txn_id = root->txn;
3010
3011   SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
3012                     txn_id, trail, trail->pool));
3013
3014   /* If there's already a sub-directory by that name, complain.  This
3015      also catches the case of trying to make a subdirectory named `/'.  */
3016   if (parent_path->node)
3017     return SVN_FS__ALREADY_EXISTS(root, path);
3018
3019   /* Check to see if some lock is 'reserving' a file-path or dir-path
3020      at that location, or even some child-path;  if so, check that we
3021      can use it. */
3022   if (args->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3023     {
3024       SVN_ERR(svn_fs_base__allow_locked_operation(path, TRUE,
3025                                                   trail, trail->pool));
3026     }
3027
3028   /* Create the subdirectory.  */
3029   SVN_ERR(make_path_mutable(root, parent_path->parent, path,
3030                             trail, trail->pool));
3031   SVN_ERR(svn_fs_base__dag_make_dir(&sub_dir,
3032                                     parent_path->parent->node,
3033                                     parent_path_path(parent_path->parent,
3034                                                      trail->pool),
3035                                     parent_path->entry,
3036                                     txn_id,
3037                                     trail, trail->pool));
3038
3039   /* Make a record of this modification in the changes table. */
3040   return add_change(root->fs, txn_id, path,
3041                     svn_fs_base__dag_get_id(sub_dir),
3042                     svn_fs_path_change_add, FALSE, FALSE,
3043                     trail, trail->pool);
3044 }
3045
3046
3047 static svn_error_t *
3048 base_make_dir(svn_fs_root_t *root,
3049               const char *path,
3050               apr_pool_t *pool)
3051 {
3052   struct make_dir_args args;
3053
3054   if (! root->is_txn_root)
3055     return SVN_FS__NOT_TXN(root);
3056
3057   args.root = root;
3058   args.path = path;
3059   return svn_fs_base__retry_txn(root->fs, txn_body_make_dir, &args,
3060                                 TRUE, pool);
3061 }
3062
3063
3064 struct delete_args
3065 {
3066   svn_fs_root_t *root;
3067   const char *path;
3068 };
3069
3070
3071 /* If this returns SVN_ERR_FS_NO_SUCH_ENTRY, it means that the
3072    basename of PATH is missing from its parent, that is, the final
3073    target of the deletion is missing.  */
3074 static svn_error_t *
3075 txn_body_delete(void *baton,
3076                 trail_t *trail)
3077 {
3078   struct delete_args *args = baton;
3079   svn_fs_root_t *root = args->root;
3080   const char *path = args->path;
3081   parent_path_t *parent_path;
3082   const char *txn_id = root->txn;
3083   base_fs_data_t *bfd = trail->fs->fsap_data;
3084
3085   if (! root->is_txn_root)
3086     return SVN_FS__NOT_TXN(root);
3087
3088   SVN_ERR(open_path(&parent_path, root, path, 0, txn_id,
3089                     trail, trail->pool));
3090
3091   /* We can't remove the root of the filesystem.  */
3092   if (! parent_path->parent)
3093     return svn_error_create(SVN_ERR_FS_ROOT_DIR, NULL,
3094                             _("The root directory cannot be deleted"));
3095
3096   /* Check to see if path (or any child thereof) is locked; if so,
3097      check that we can use the existing lock(s). */
3098   if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3099     {
3100       SVN_ERR(svn_fs_base__allow_locked_operation(path, TRUE,
3101                                                   trail, trail->pool));
3102     }
3103
3104   /* Make the parent directory mutable. */
3105   SVN_ERR(make_path_mutable(root, parent_path->parent, path,
3106                             trail, trail->pool));
3107
3108   /* Decrement mergeinfo counts on the parents of this node by the
3109      count it previously carried, if our format supports it. */
3110   if (bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
3111     {
3112       apr_int64_t mergeinfo_count;
3113       SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, &mergeinfo_count,
3114                                                    parent_path->node,
3115                                                    trail, trail->pool));
3116       SVN_ERR(adjust_parent_mergeinfo_counts(parent_path->parent,
3117                                              -mergeinfo_count, txn_id,
3118                                              trail, trail->pool));
3119     }
3120
3121   /* Do the deletion. */
3122   SVN_ERR(svn_fs_base__dag_delete(parent_path->parent->node,
3123                                   parent_path->entry,
3124                                   txn_id, trail, trail->pool));
3125
3126
3127   /* Make a record of this modification in the changes table. */
3128   return add_change(root->fs, txn_id, path,
3129                     svn_fs_base__dag_get_id(parent_path->node),
3130                     svn_fs_path_change_delete, FALSE, FALSE, trail,
3131                     trail->pool);
3132 }
3133
3134
3135 static svn_error_t *
3136 base_delete_node(svn_fs_root_t *root,
3137                  const char *path,
3138                  apr_pool_t *pool)
3139 {
3140   struct delete_args args;
3141
3142   args.root        = root;
3143   args.path        = path;
3144   return svn_fs_base__retry_txn(root->fs, txn_body_delete, &args,
3145                                 TRUE, pool);
3146 }
3147
3148
3149 struct copy_args
3150 {
3151   svn_fs_root_t *from_root;
3152   const char *from_path;
3153   svn_fs_root_t *to_root;
3154   const char *to_path;
3155   svn_boolean_t preserve_history;
3156 };
3157
3158
3159 static svn_error_t *
3160 txn_body_copy(void *baton,
3161               trail_t *trail)
3162 {
3163   struct copy_args *args = baton;
3164   svn_fs_root_t *from_root = args->from_root;
3165   const char *from_path = args->from_path;
3166   svn_fs_root_t *to_root = args->to_root;
3167   const char *to_path = args->to_path;
3168   dag_node_t *from_node;
3169   parent_path_t *to_parent_path;
3170   const char *txn_id = to_root->txn;
3171
3172   /* Get the NODE for FROM_PATH in FROM_ROOT.*/
3173   SVN_ERR(get_dag(&from_node, from_root, from_path, trail, trail->pool));
3174
3175   /* Build up the parent path from TO_PATH in TO_ROOT.  If the last
3176      component does not exist, it's not that big a deal.  We'll just
3177      make one there. */
3178   SVN_ERR(open_path(&to_parent_path, to_root, to_path,
3179                     open_path_last_optional, txn_id, trail, trail->pool));
3180
3181   /* Check to see if to-path (or any child thereof) is locked, or at
3182      least 'reserved', whether it exists or not; if so, check that we
3183      can use the existing lock(s). */
3184   if (to_root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3185     {
3186       SVN_ERR(svn_fs_base__allow_locked_operation(to_path, TRUE,
3187                                                   trail, trail->pool));
3188     }
3189
3190   /* If the destination node already exists as the same node as the
3191      source (in other words, this operation would result in nothing
3192      happening at all), just do nothing an return successfully,
3193      proud that you saved yourself from a tiresome task. */
3194   if ((to_parent_path->node)
3195       && (svn_fs_base__id_compare(svn_fs_base__dag_get_id(from_node),
3196                                   svn_fs_base__dag_get_id
3197                                   (to_parent_path->node))
3198           == svn_fs_node_unchanged))
3199     return SVN_NO_ERROR;
3200
3201   if (! from_root->is_txn_root)
3202     {
3203       svn_fs_path_change_kind_t kind;
3204       dag_node_t *new_node;
3205       apr_int64_t old_mergeinfo_count = 0, mergeinfo_count;
3206       base_fs_data_t *bfd = trail->fs->fsap_data;
3207
3208       /* If TO_PATH already existed prior to the copy, note that this
3209          operation is a replacement, not an addition. */
3210       if (to_parent_path->node)
3211         kind = svn_fs_path_change_replace;
3212       else
3213         kind = svn_fs_path_change_add;
3214
3215       /* Make sure the target node's parents are mutable.  */
3216       SVN_ERR(make_path_mutable(to_root, to_parent_path->parent,
3217                                 to_path, trail, trail->pool));
3218
3219       /* If this is a replacement operation, we need to know the old
3220          node's mergeinfo count. */
3221       if (to_parent_path->node)
3222         SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL,
3223                                                      &old_mergeinfo_count,
3224                                                      to_parent_path->node,
3225                                                      trail, trail->pool));
3226       /* Do the copy. */
3227       SVN_ERR(svn_fs_base__dag_copy(to_parent_path->parent->node,
3228                                     to_parent_path->entry,
3229                                     from_node,
3230                                     args->preserve_history,
3231                                     from_root->rev,
3232                                     from_path, txn_id, trail, trail->pool));
3233
3234       /* Adjust the mergeinfo counts of the destination's parents if
3235          our format supports it. */
3236       if (bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
3237         {
3238           SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL,
3239                                                        &mergeinfo_count,
3240                                                        from_node, trail,
3241                                                        trail->pool));
3242           SVN_ERR(adjust_parent_mergeinfo_counts
3243                   (to_parent_path->parent,
3244                    mergeinfo_count - old_mergeinfo_count,
3245                    txn_id, trail, trail->pool));
3246         }
3247
3248       /* Make a record of this modification in the changes table. */
3249       SVN_ERR(get_dag(&new_node, to_root, to_path, trail, trail->pool));
3250       SVN_ERR(add_change(to_root->fs, txn_id, to_path,
3251                          svn_fs_base__dag_get_id(new_node),
3252                          kind, FALSE, FALSE, trail, trail->pool));
3253     }
3254   else
3255     {
3256       /* See IZ Issue #436 */
3257       /* Copying from transaction roots not currently available.
3258
3259          ### cmpilato todo someday: make this not so. :-) Note that
3260          when copying from mutable trees, you have to make sure that
3261          you aren't creating a cyclic graph filesystem, and a simple
3262          referencing operation won't cut it.  Currently, we should not
3263          be able to reach this clause, and the interface reports that
3264          this only works from immutable trees anyway, but JimB has
3265          stated that this requirement need not be necessary in the
3266          future. */
3267
3268       SVN_ERR_MALFUNCTION();
3269     }
3270
3271   return SVN_NO_ERROR;
3272 }
3273
3274
3275 /* Set *SAME_P to TRUE if FS1 and FS2 have the same UUID, else set to FALSE.
3276    Use POOL for temporary allocation only.
3277    Note: this code is duplicated between libsvn_fs_fs and libsvn_fs_base. */
3278 static svn_error_t *
3279 fs_same_p(svn_boolean_t *same_p,
3280           svn_fs_t *fs1,
3281           svn_fs_t *fs2,
3282           apr_pool_t *pool)
3283 {
3284   *same_p = ! strcmp(fs1->uuid, fs2->uuid);
3285   return SVN_NO_ERROR;
3286 }
3287
3288 /* Copy the node at FROM_PATH under FROM_ROOT to TO_PATH under
3289    TO_ROOT.  If PRESERVE_HISTORY is set, then the copy is recorded in
3290    the copies table.  Perform temporary allocations in POOL. */
3291 static svn_error_t *
3292 copy_helper(svn_fs_root_t *from_root,
3293             const char *from_path,
3294             svn_fs_root_t *to_root,
3295             const char *to_path,
3296             svn_boolean_t preserve_history,
3297             apr_pool_t *pool)
3298 {
3299   struct copy_args args;
3300   svn_boolean_t same_p;
3301
3302   /* Use an error check, not an assert, because even the caller cannot
3303      guarantee that a filesystem's UUID has not changed "on the fly". */
3304   SVN_ERR(fs_same_p(&same_p, from_root->fs, to_root->fs, pool));
3305   if (! same_p)
3306     return svn_error_createf
3307       (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
3308        _("Cannot copy between two different filesystems ('%s' and '%s')"),
3309        from_root->fs->path, to_root->fs->path);
3310
3311   if (! to_root->is_txn_root)
3312     return SVN_FS__NOT_TXN(to_root);
3313
3314   if (from_root->is_txn_root)
3315     return svn_error_create
3316       (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
3317        _("Copy from mutable tree not currently supported"));
3318
3319   args.from_root         = from_root;
3320   args.from_path         = from_path;
3321   args.to_root           = to_root;
3322   args.to_path           = to_path;
3323   args.preserve_history  = preserve_history;
3324
3325   return svn_fs_base__retry_txn(to_root->fs, txn_body_copy, &args,
3326                                 TRUE, pool);
3327 }
3328
3329 static svn_error_t *
3330 base_copy(svn_fs_root_t *from_root,
3331           const char *from_path,
3332           svn_fs_root_t *to_root,
3333           const char *to_path,
3334           apr_pool_t *pool)
3335 {
3336   return copy_helper(from_root, from_path, to_root, to_path, TRUE, pool);
3337 }
3338
3339
3340 static svn_error_t *
3341 base_revision_link(svn_fs_root_t *from_root,
3342                    svn_fs_root_t *to_root,
3343                    const char *path,
3344                    apr_pool_t *pool)
3345 {
3346   return copy_helper(from_root, path, to_root, path, FALSE, pool);
3347 }
3348
3349
3350 struct copied_from_args
3351 {
3352   svn_fs_root_t *root;      /* Root for the node whose ancestry we seek. */
3353   const char *path;         /* Path for the node whose ancestry we seek. */
3354
3355   svn_revnum_t result_rev;  /* Revision, if any, of the ancestor. */
3356   const char *result_path;  /* Path, if any, of the ancestor. */
3357
3358   apr_pool_t *pool;         /* Allocate `result_path' here. */
3359 };
3360
3361
3362 static svn_error_t *
3363 txn_body_copied_from(void *baton, trail_t *trail)
3364 {
3365   struct copied_from_args *args = baton;
3366   const svn_fs_id_t *node_id, *pred_id;
3367   dag_node_t *node;
3368   svn_fs_t *fs = args->root->fs;
3369
3370   /* Clear the return variables. */
3371   args->result_path = NULL;
3372   args->result_rev = SVN_INVALID_REVNUM;
3373
3374   /* Fetch the NODE in question. */
3375   SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
3376   node_id = svn_fs_base__dag_get_id(node);
3377
3378   /* Check the node's predecessor-ID.  If it doesn't have one, it
3379      isn't a copy. */
3380   SVN_ERR(svn_fs_base__dag_get_predecessor_id(&pred_id, node,
3381                                               trail, trail->pool));
3382   if (! pred_id)
3383     return SVN_NO_ERROR;
3384
3385   /* If NODE's copy-ID is the same as that of its predecessor... */
3386   if (strcmp(svn_fs_base__id_copy_id(node_id),
3387              svn_fs_base__id_copy_id(pred_id)) != 0)
3388     {
3389       /* ... then NODE was either the target of a copy operation,
3390          a copied subtree item.  We examine the actual copy record
3391          to determine which is the case.  */
3392       copy_t *copy;
3393       SVN_ERR(svn_fs_bdb__get_copy(&copy, fs,
3394                                    svn_fs_base__id_copy_id(node_id),
3395                                    trail, trail->pool));
3396       if ((copy->kind == copy_kind_real)
3397           && svn_fs_base__id_eq(copy->dst_noderev_id, node_id))
3398         {
3399           args->result_path = copy->src_path;
3400           SVN_ERR(svn_fs_base__txn_get_revision(&(args->result_rev), fs,
3401                                                 copy->src_txn_id,
3402                                                 trail, trail->pool));
3403         }
3404     }
3405   return SVN_NO_ERROR;
3406 }
3407
3408
3409 static svn_error_t *
3410 base_copied_from(svn_revnum_t *rev_p,
3411                  const char **path_p,
3412                  svn_fs_root_t *root,
3413                  const char *path,
3414                  apr_pool_t *pool)
3415 {
3416   struct copied_from_args args;
3417   apr_pool_t *scratch_pool = svn_pool_create(pool);
3418   args.root = root;
3419   args.path = path;
3420   args.pool = pool;
3421
3422   SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_copied_from, &args,
3423                                  FALSE, scratch_pool));
3424
3425   *rev_p  = args.result_rev;
3426   *path_p = args.result_path ? apr_pstrdup(pool, args.result_path) : NULL;
3427
3428   svn_pool_destroy(scratch_pool);
3429   return SVN_NO_ERROR;
3430 }
3431
3432
3433 \f
3434 /* Files.  */
3435
3436
3437 struct make_file_args
3438 {
3439   svn_fs_root_t *root;
3440   const char *path;
3441 };
3442
3443
3444 static svn_error_t *
3445 txn_body_make_file(void *baton,
3446                    trail_t *trail)
3447 {
3448   struct make_file_args *args = baton;
3449   svn_fs_root_t *root = args->root;
3450   const char *path = args->path;
3451   parent_path_t *parent_path;
3452   dag_node_t *child;
3453   const char *txn_id = root->txn;
3454
3455   SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
3456                     txn_id, trail, trail->pool));
3457
3458   /* If there's already a file by that name, complain.
3459      This also catches the case of trying to make a file named `/'.  */
3460   if (parent_path->node)
3461     return SVN_FS__ALREADY_EXISTS(root, path);
3462
3463   /* Check to see if some lock is 'reserving' a file-path or dir-path
3464      at that location, or even some child-path;  if so, check that we
3465      can use it. */
3466   if (args->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3467     {
3468       SVN_ERR(svn_fs_base__allow_locked_operation(path, TRUE,
3469                                                   trail, trail->pool));
3470     }
3471
3472   /* Create the file.  */
3473   SVN_ERR(make_path_mutable(root, parent_path->parent, path,
3474                             trail, trail->pool));
3475   SVN_ERR(svn_fs_base__dag_make_file(&child,
3476                                      parent_path->parent->node,
3477                                      parent_path_path(parent_path->parent,
3478                                                       trail->pool),
3479                                      parent_path->entry,
3480                                      txn_id,
3481                                      trail, trail->pool));
3482
3483   /* Make a record of this modification in the changes table. */
3484   return add_change(root->fs, txn_id, path,
3485                     svn_fs_base__dag_get_id(child),
3486                     svn_fs_path_change_add, TRUE, FALSE,
3487                     trail, trail->pool);
3488 }
3489
3490
3491 static svn_error_t *
3492 base_make_file(svn_fs_root_t *root,
3493                const char *path,
3494                apr_pool_t *pool)
3495 {
3496   struct make_file_args args;
3497
3498   args.root = root;
3499   args.path = path;
3500   return svn_fs_base__retry_txn(root->fs, txn_body_make_file, &args,
3501                                 TRUE, pool);
3502 }
3503
3504
3505
3506 struct file_length_args
3507 {
3508   svn_fs_root_t *root;
3509   const char *path;
3510   svn_filesize_t length;       /* OUT parameter */
3511 };
3512
3513 static svn_error_t *
3514 txn_body_file_length(void *baton,
3515                      trail_t *trail)
3516 {
3517   struct file_length_args *args = baton;
3518   dag_node_t *file;
3519
3520   /* First create a dag_node_t from the root/path pair. */
3521   SVN_ERR(get_dag(&file, args->root, args->path, trail, trail->pool));
3522
3523   /* Now fetch its length */
3524   return svn_fs_base__dag_file_length(&args->length, file,
3525                                       trail, trail->pool);
3526 }
3527
3528 static svn_error_t *
3529 base_file_length(svn_filesize_t *length_p,
3530                  svn_fs_root_t *root,
3531                  const char *path,
3532                  apr_pool_t *pool)
3533 {
3534   struct file_length_args args;
3535
3536   args.root = root;
3537   args.path = path;
3538   SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_file_length, &args,
3539                                  TRUE, pool));
3540
3541   *length_p = args.length;
3542   return SVN_NO_ERROR;
3543 }
3544
3545
3546 struct file_checksum_args
3547 {
3548   svn_fs_root_t *root;
3549   const char *path;
3550   svn_checksum_kind_t kind;
3551   svn_checksum_t **checksum;  /* OUT parameter */
3552 };
3553
3554 static svn_error_t *
3555 txn_body_file_checksum(void *baton,
3556                        trail_t *trail)
3557 {
3558   struct file_checksum_args *args = baton;
3559   dag_node_t *file;
3560
3561   SVN_ERR(get_dag(&file, args->root, args->path, trail, trail->pool));
3562
3563   return svn_fs_base__dag_file_checksum(args->checksum, args->kind, file,
3564                                         trail, trail->pool);
3565 }
3566
3567 static svn_error_t *
3568 base_file_checksum(svn_checksum_t **checksum,
3569                    svn_checksum_kind_t kind,
3570                    svn_fs_root_t *root,
3571                    const char *path,
3572                    apr_pool_t *pool)
3573 {
3574   struct file_checksum_args args;
3575   apr_pool_t *scratch_pool = svn_pool_create(pool);
3576
3577   args.root = root;
3578   args.path = path;
3579   args.kind = kind;
3580   args.checksum = checksum;
3581   SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_file_checksum, &args,
3582                                  FALSE, scratch_pool));
3583   *checksum = svn_checksum_dup(*checksum, pool);
3584   svn_pool_destroy(scratch_pool);
3585   return SVN_NO_ERROR;
3586 }
3587
3588
3589 /* --- Machinery for svn_fs_file_contents() ---  */
3590
3591
3592 /* Local baton type for txn_body_get_file_contents. */
3593 typedef struct file_contents_baton_t
3594 {
3595   /* The file we want to read. */
3596   svn_fs_root_t *root;
3597   const char *path;
3598
3599   /* The dag_node that will be made from the above. */
3600   dag_node_t *node;
3601
3602   /* The pool in which `file_stream' (below) is allocated. */
3603   apr_pool_t *pool;
3604
3605   /* The readable file stream that will be made from the
3606      dag_node. (And returned to the caller.) */
3607   svn_stream_t *file_stream;
3608
3609 } file_contents_baton_t;
3610
3611
3612 /* Main body of svn_fs_file_contents;  converts a root/path pair into
3613    a readable file stream (in the context of a db txn). */
3614 static svn_error_t *
3615 txn_body_get_file_contents(void *baton, trail_t *trail)
3616 {
3617   file_contents_baton_t *fb = (file_contents_baton_t *) baton;
3618
3619   /* First create a dag_node_t from the root/path pair. */
3620   SVN_ERR(get_dag(&(fb->node), fb->root, fb->path, trail, trail->pool));
3621
3622   /* Then create a readable stream from the dag_node_t. */
3623   return svn_fs_base__dag_get_contents(&(fb->file_stream),
3624                                        fb->node, trail, fb->pool);
3625 }
3626
3627
3628
3629 static svn_error_t *
3630 base_file_contents(svn_stream_t **contents,
3631                    svn_fs_root_t *root,
3632                    const char *path,
3633                    apr_pool_t *pool)
3634 {
3635   file_contents_baton_t *fb = apr_pcalloc(pool, sizeof(*fb));
3636   fb->root = root;
3637   fb->path = path;
3638   fb->pool = pool;
3639
3640   /* Create the readable stream in the context of a db txn.  */
3641   SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_get_file_contents, fb,
3642                                  FALSE, pool));
3643
3644   *contents = fb->file_stream;
3645   return SVN_NO_ERROR;
3646 }
3647
3648 /* --- End machinery for svn_fs_file_contents() ---  */
3649
3650
3651
3652 /* --- Machinery for svn_fs_apply_textdelta() ---  */
3653
3654
3655 /* Local baton type for all the helper functions below. */
3656 typedef struct txdelta_baton_t
3657 {
3658   /* This is the custom-built window consumer given to us by the delta
3659      library;  it uniquely knows how to read data from our designated
3660      "source" stream, interpret the window, and write data to our
3661      designated "target" stream (in this case, our repos file.) */
3662   svn_txdelta_window_handler_t interpreter;
3663   void *interpreter_baton;
3664
3665   /* The original file info */
3666   svn_fs_root_t *root;
3667   const char *path;
3668
3669   /* Derived from the file info */
3670   dag_node_t *node;
3671
3672   svn_stream_t *source_stream;
3673   svn_stream_t *target_stream;
3674   svn_stream_t *string_stream;
3675   svn_stringbuf_t *target_string;
3676
3677   /* Checksums for the base text against which a delta is to be
3678      applied, and for the resultant fulltext, respectively.  Either or
3679      both may be null, in which case ignored. */
3680   svn_checksum_t *base_checksum;
3681   svn_checksum_t *result_checksum;
3682
3683   /* Pool used by db txns */
3684   apr_pool_t *pool;
3685
3686 } txdelta_baton_t;
3687
3688
3689 /* A trail-ready wrapper around svn_fs_base__dag_finalize_edits.
3690  * This closes BATON->target_stream.
3691  *
3692  * Note: If you're confused about how this function relates to another
3693  * of similar name, think of it this way:
3694  *
3695  * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits()
3696  * svn_fs_apply_text()      ==> ... ==> txn_body_fulltext_finalize_edits()
3697  */
3698 static svn_error_t *
3699 txn_body_txdelta_finalize_edits(void *baton, trail_t *trail)
3700 {
3701   txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3702   SVN_ERR(svn_fs_base__dag_finalize_edits(tb->node,
3703                                           tb->result_checksum,
3704                                           tb->root->txn,
3705                                           trail, trail->pool));
3706
3707   /* Make a record of this modification in the changes table. */
3708   return add_change(tb->root->fs, tb->root->txn, tb->path,
3709                     svn_fs_base__dag_get_id(tb->node),
3710                     svn_fs_path_change_modify, TRUE, FALSE, trail,
3711                     trail->pool);
3712 }
3713
3714
3715 /* ### see comment in window_consumer() regarding this function. */
3716
3717 /* Helper function of generic type `svn_write_fn_t'.  Implements a
3718    writable stream which appends to an svn_stringbuf_t. */
3719 static svn_error_t *
3720 write_to_string(void *baton, const char *data, apr_size_t *len)
3721 {
3722   txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3723   svn_stringbuf_appendbytes(tb->target_string, data, *len);
3724   return SVN_NO_ERROR;
3725 }
3726
3727
3728
3729 /* The main window handler returned by svn_fs_apply_textdelta. */
3730 static svn_error_t *
3731 window_consumer(svn_txdelta_window_t *window, void *baton)
3732 {
3733   txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3734
3735   /* Send the window right through to the custom window interpreter.
3736      In theory, the interpreter will then write more data to
3737      cb->target_string. */
3738   SVN_ERR(tb->interpreter(window, tb->interpreter_baton));
3739
3740   /* ### the write_to_string() callback for the txdelta's output stream
3741      ### should be doing all the flush determination logic, not here.
3742      ### in a drastic case, a window could generate a LOT more than the
3743      ### maximum buffer size. we want to flush to the underlying target
3744      ### stream much sooner (e.g. also in a streamy fashion). also, by
3745      ### moving this logic inside the stream, the stream becomes nice
3746      ### and encapsulated: it holds all the logic about buffering and
3747      ### flushing.
3748      ###
3749      ### further: I believe the buffering should be removed from tree.c
3750      ### the buffering should go into the target_stream itself, which
3751      ### is defined by reps-string.c. Specifically, I think the
3752      ### rep_write_contents() function will handle the buffering and
3753      ### the spill to the underlying DB. by locating it there, then
3754      ### anybody who gets a writable stream for FS content can take
3755      ### advantage of the buffering capability. this will be important
3756      ### when we export an FS API function for writing a fulltext into
3757      ### the FS, rather than forcing that fulltext thru apply_textdelta.
3758   */
3759
3760   /* Check to see if we need to purge the portion of the contents that
3761      have been written thus far. */
3762   if ((! window) || (tb->target_string->len > WRITE_BUFFER_SIZE))
3763     {
3764       apr_size_t len = tb->target_string->len;
3765       SVN_ERR(svn_stream_write(tb->target_stream,
3766                                tb->target_string->data,
3767                                &len));
3768       svn_stringbuf_setempty(tb->target_string);
3769     }
3770
3771   /* Is the window NULL?  If so, we're done. */
3772   if (! window)
3773     {
3774       /* Close the internal-use stream.  ### This used to be inside of
3775          txn_body_fulltext_finalize_edits(), but that invoked a nested
3776          Berkeley DB transaction -- scandalous! */
3777       SVN_ERR(svn_stream_close(tb->target_stream));
3778
3779       /* Tell the dag subsystem that we're finished with our edits. */
3780       SVN_ERR(svn_fs_base__retry_txn(tb->root->fs,
3781                                      txn_body_txdelta_finalize_edits, tb,
3782                                      FALSE, tb->pool));
3783     }
3784
3785   return SVN_NO_ERROR;
3786 }
3787
3788
3789 static svn_error_t *
3790 txn_body_apply_textdelta(void *baton, trail_t *trail)
3791 {
3792   txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3793   parent_path_t *parent_path;
3794   const char *txn_id = tb->root->txn;
3795
3796   /* Call open_path with no flags, as we want this to return an error
3797      if the node for which we are searching doesn't exist. */
3798   SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id,
3799                     trail, trail->pool));
3800
3801   /* Check to see if path is locked;  if so, check that we can use it. */
3802   if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3803     SVN_ERR(svn_fs_base__allow_locked_operation(tb->path, FALSE,
3804                                                 trail, trail->pool));
3805
3806   /* Now, make sure this path is mutable. */
3807   SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path,
3808                             trail, trail->pool));
3809   tb->node = parent_path->node;
3810
3811   if (tb->base_checksum)
3812     {
3813       svn_checksum_t *checksum;
3814
3815       /* Until we finalize the node, its data_key points to the old
3816          contents, in other words, the base text. */
3817       SVN_ERR(svn_fs_base__dag_file_checksum(&checksum,
3818                                              tb->base_checksum->kind,
3819                                              tb->node, trail, trail->pool));
3820       /* TODO: This only compares checksums if they are the same kind, but
3821          we're calculating both SHA1 and MD5 checksums somewhere in
3822          reps-strings.c.  Could we keep them both around somehow so this
3823          check could be more comprehensive? */
3824       if (!svn_checksum_match(tb->base_checksum, checksum))
3825         return svn_checksum_mismatch_err(tb->base_checksum, checksum,
3826                             trail->pool,
3827                             _("Base checksum mismatch on '%s'"),
3828                             tb->path);
3829     }
3830
3831   /* Make a readable "source" stream out of the current contents of
3832      ROOT/PATH; obviously, this must done in the context of a db_txn.
3833      The stream is returned in tb->source_stream. */
3834   SVN_ERR(svn_fs_base__dag_get_contents(&(tb->source_stream),
3835                                         tb->node, trail, tb->pool));
3836
3837   /* Make a writable "target" stream */
3838   SVN_ERR(svn_fs_base__dag_get_edit_stream(&(tb->target_stream), tb->node,
3839                                            txn_id, trail, tb->pool));
3840
3841   /* Make a writable "string" stream which writes data to
3842      tb->target_string. */
3843   tb->target_string = svn_stringbuf_create_empty(tb->pool);
3844   tb->string_stream = svn_stream_create(tb, tb->pool);
3845   svn_stream_set_write(tb->string_stream, write_to_string);
3846
3847   /* Now, create a custom window handler that uses our two streams. */
3848   svn_txdelta_apply(tb->source_stream,
3849                     tb->string_stream,
3850                     NULL,
3851                     tb->path,
3852                     tb->pool,
3853                     &(tb->interpreter),
3854                     &(tb->interpreter_baton));
3855
3856   return SVN_NO_ERROR;
3857 }
3858
3859
3860 static svn_error_t *
3861 base_apply_textdelta(svn_txdelta_window_handler_t *contents_p,
3862                      void **contents_baton_p,
3863                      svn_fs_root_t *root,
3864                      const char *path,
3865                      svn_checksum_t *base_checksum,
3866                      svn_checksum_t *result_checksum,
3867                      apr_pool_t *pool)
3868 {
3869   txdelta_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
3870
3871   tb->root = root;
3872   tb->path = path;
3873   tb->pool = pool;
3874   tb->base_checksum = svn_checksum_dup(base_checksum, pool);
3875   tb->result_checksum = svn_checksum_dup(result_checksum, pool);
3876
3877   SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_apply_textdelta, tb,
3878                                  FALSE, pool));
3879
3880   *contents_p = window_consumer;
3881   *contents_baton_p = tb;
3882   return SVN_NO_ERROR;
3883 }
3884
3885 /* --- End machinery for svn_fs_apply_textdelta() ---  */
3886
3887 /* --- Machinery for svn_fs_apply_text() ---  */
3888
3889 /* Baton for svn_fs_apply_text(). */
3890 struct text_baton_t
3891 {
3892   /* The original file info */
3893   svn_fs_root_t *root;
3894   const char *path;
3895
3896   /* Derived from the file info */
3897   dag_node_t *node;
3898
3899   /* The returned stream that will accept the file's new contents. */
3900   svn_stream_t *stream;
3901
3902   /* The actual fs stream that the returned stream will write to. */
3903   svn_stream_t *file_stream;
3904
3905   /* Checksum for the final fulltext written to the file.  May
3906      be null, in which case ignored. */
3907   svn_checksum_t *result_checksum;
3908
3909   /* Pool used by db txns */
3910   apr_pool_t *pool;
3911 };
3912
3913
3914 /* A trail-ready wrapper around svn_fs_base__dag_finalize_edits, but for
3915  * fulltext data, not text deltas.  Closes BATON->file_stream.
3916  *
3917  * Note: If you're confused about how this function relates to another
3918  * of similar name, think of it this way:
3919  *
3920  * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits()
3921  * svn_fs_apply_text()      ==> ... ==> txn_body_fulltext_finalize_edits()
3922  */
3923 static svn_error_t *
3924 txn_body_fulltext_finalize_edits(void *baton, trail_t *trail)
3925 {
3926   struct text_baton_t *tb = baton;
3927   SVN_ERR(svn_fs_base__dag_finalize_edits(tb->node,
3928                                           tb->result_checksum,
3929                                           tb->root->txn,
3930                                           trail, trail->pool));
3931
3932   /* Make a record of this modification in the changes table. */
3933   return add_change(tb->root->fs, tb->root->txn, tb->path,
3934                     svn_fs_base__dag_get_id(tb->node),
3935                     svn_fs_path_change_modify, TRUE, FALSE, trail,
3936                     trail->pool);
3937 }
3938
3939 /* Write function for the publically returned stream. */
3940 static svn_error_t *
3941 text_stream_writer(void *baton,
3942                    const char *data,
3943                    apr_size_t *len)
3944 {
3945   struct text_baton_t *tb = baton;
3946
3947   /* Psst, here's some data.  Pass it on to the -real- file stream. */
3948   return svn_stream_write(tb->file_stream, data, len);
3949 }
3950
3951 /* Close function for the publically returned stream. */
3952 static svn_error_t *
3953 text_stream_closer(void *baton)
3954 {
3955   struct text_baton_t *tb = baton;
3956
3957   /* Close the internal-use stream.  ### This used to be inside of
3958      txn_body_fulltext_finalize_edits(), but that invoked a nested
3959      Berkeley DB transaction -- scandalous! */
3960   SVN_ERR(svn_stream_close(tb->file_stream));
3961
3962   /* Need to tell fs that we're done sending text */
3963   return svn_fs_base__retry_txn(tb->root->fs,
3964                                 txn_body_fulltext_finalize_edits, tb,
3965                                 FALSE, tb->pool);
3966 }
3967
3968
3969 static svn_error_t *
3970 txn_body_apply_text(void *baton, trail_t *trail)
3971 {
3972   struct text_baton_t *tb = baton;
3973   parent_path_t *parent_path;
3974   const char *txn_id = tb->root->txn;
3975
3976   /* Call open_path with no flags, as we want this to return an error
3977      if the node for which we are searching doesn't exist. */
3978   SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id,
3979                     trail, trail->pool));
3980
3981   /* Check to see if path is locked;  if so, check that we can use it. */
3982   if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3983     SVN_ERR(svn_fs_base__allow_locked_operation(tb->path, FALSE,
3984                                                 trail, trail->pool));
3985
3986   /* Now, make sure this path is mutable. */
3987   SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path,
3988                             trail, trail->pool));
3989   tb->node = parent_path->node;
3990
3991   /* Make a writable stream for replacing the file's text. */
3992   SVN_ERR(svn_fs_base__dag_get_edit_stream(&(tb->file_stream), tb->node,
3993                                            txn_id, trail, tb->pool));
3994
3995   /* Create a 'returnable' stream which writes to the file_stream. */
3996   tb->stream = svn_stream_create(tb, tb->pool);
3997   svn_stream_set_write(tb->stream, text_stream_writer);
3998   svn_stream_set_close(tb->stream, text_stream_closer);
3999
4000   return SVN_NO_ERROR;
4001 }
4002
4003
4004 static svn_error_t *
4005 base_apply_text(svn_stream_t **contents_p,
4006                 svn_fs_root_t *root,
4007                 const char *path,
4008                 svn_checksum_t *result_checksum,
4009                 apr_pool_t *pool)
4010 {
4011   struct text_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
4012
4013   tb->root = root;
4014   tb->path = path;
4015   tb->pool = pool;
4016   tb->result_checksum = svn_checksum_dup(result_checksum, pool);
4017
4018   SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_apply_text, tb,
4019                                  FALSE, pool));
4020
4021   *contents_p = tb->stream;
4022   return SVN_NO_ERROR;
4023 }
4024
4025 /* --- End machinery for svn_fs_apply_text() ---  */
4026
4027
4028 /* Note: we're sharing the `things_changed_args' struct with
4029    svn_fs_props_changed(). */
4030
4031 static svn_error_t *
4032 txn_body_contents_changed(void *baton, trail_t *trail)
4033 {
4034   struct things_changed_args *args = baton;
4035   dag_node_t *node1, *node2;
4036   svn_checksum_t *checksum1, *checksum2;
4037   svn_stream_t *stream1, *stream2;
4038   svn_boolean_t same;
4039
4040   SVN_ERR(get_dag(&node1, args->root1, args->path1, trail, trail->pool));
4041   SVN_ERR(get_dag(&node2, args->root2, args->path2, trail, trail->pool));
4042   SVN_ERR(svn_fs_base__things_different(NULL, args->changed_p,
4043                                         node1, node2, trail, trail->pool));
4044
4045   /* Is there a potential false positive and do we want to correct it? */
4046   if (!args->strict || !*args->changed_p)
4047     return SVN_NO_ERROR;
4048
4049   /* Different representations. They might still have equal contents. */
4050
4051   /* Compare MD5 checksums.  These should be readily accessible. */
4052   SVN_ERR(svn_fs_base__dag_file_checksum(&checksum1, svn_checksum_md5,
4053                                          node1, trail, trail->pool));
4054   SVN_ERR(svn_fs_base__dag_file_checksum(&checksum2, svn_checksum_md5,
4055                                          node2, trail, trail->pool));
4056
4057   /* Different MD5 checksums -> different contents */
4058   if (!svn_checksum_match(checksum1, checksum2))
4059     return SVN_NO_ERROR;
4060
4061   /* Paranoia. Compare SHA1 checksums because that's the level of
4062      confidence we require for e.g. the working copy. */
4063   SVN_ERR(svn_fs_base__dag_file_checksum(&checksum1, svn_checksum_sha1,
4064                                          node1, trail, trail->pool));
4065   SVN_ERR(svn_fs_base__dag_file_checksum(&checksum2, svn_checksum_sha1,
4066                                          node2, trail, trail->pool));
4067
4068   /* Different SHA1 checksums -> different contents */
4069   if (checksum1 && checksum2)
4070     {
4071       *args->changed_p = !svn_checksum_match(checksum1, checksum2);
4072       return SVN_NO_ERROR;
4073     }
4074
4075   /* SHA1 checksums are not available for very old reps / repositories. */
4076   SVN_ERR(svn_fs_base__dag_get_contents(&stream1, node1, trail, trail->pool));
4077   SVN_ERR(svn_fs_base__dag_get_contents(&stream2, node2, trail, trail->pool));
4078   SVN_ERR(svn_stream_contents_same2(&same, stream1, stream2, trail->pool));
4079
4080   /* Now, it's definitive. */
4081   *args->changed_p = !same;
4082   return SVN_NO_ERROR;
4083 }
4084
4085
4086 /* Note:  it is acceptable for this function to call back into
4087    top-level interfaces because it does not itself use trails.  */
4088 static svn_error_t *
4089 base_contents_changed(svn_boolean_t *changed_p,
4090                       svn_fs_root_t *root1,
4091                       const char *path1,
4092                       svn_fs_root_t *root2,
4093                       const char *path2,
4094                       svn_boolean_t strict,
4095                       apr_pool_t *pool)
4096 {
4097   struct things_changed_args args;
4098
4099   /* Check that roots are in the same fs. */
4100   if (root1->fs != root2->fs)
4101     return svn_error_create
4102       (SVN_ERR_FS_GENERAL, NULL,
4103        _("Cannot compare file contents between two different filesystems"));
4104
4105   /* Check that both paths are files. */
4106   {
4107     svn_node_kind_t kind;
4108
4109     SVN_ERR(base_check_path(&kind, root1, path1, pool));
4110     if (kind != svn_node_file)
4111       return svn_error_createf
4112         (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path1);
4113
4114     SVN_ERR(base_check_path(&kind, root2, path2, pool));
4115     if (kind != svn_node_file)
4116       return svn_error_createf
4117         (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path2);
4118   }
4119
4120   args.root1      = root1;
4121   args.root2      = root2;
4122   args.path1      = path1;
4123   args.path2      = path2;
4124   args.changed_p  = changed_p;
4125   args.pool       = pool;
4126   args.strict     = strict;
4127
4128   return svn_fs_base__retry_txn(root1->fs, txn_body_contents_changed, &args,
4129                                 TRUE, pool);
4130 }
4131
4132
4133 \f
4134 /* Public interface to computing file text deltas.  */
4135
4136 /* Note:  it is acceptable for this function to call back into
4137    public FS API interfaces because it does not itself use trails.  */
4138 static svn_error_t *
4139 base_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
4140                            svn_fs_root_t *source_root,
4141                            const char *source_path,
4142                            svn_fs_root_t *target_root,
4143                            const char *target_path,
4144                            apr_pool_t *pool)
4145 {
4146   svn_stream_t *source, *target;
4147   svn_txdelta_stream_t *delta_stream;
4148
4149   /* Get read functions for the source file contents.  */
4150   if (source_root && source_path)
4151     SVN_ERR(base_file_contents(&source, source_root, source_path, pool));
4152   else
4153     source = svn_stream_empty(pool);
4154
4155   /* Get read functions for the target file contents.  */
4156   SVN_ERR(base_file_contents(&target, target_root, target_path, pool));
4157
4158   /* Create a delta stream that turns the ancestor into the target.  */
4159   svn_txdelta2(&delta_stream, source, target, TRUE, pool);
4160
4161   *stream_p = delta_stream;
4162   return SVN_NO_ERROR;
4163 }
4164
4165
4166 \f
4167 /* Finding Changes */
4168
4169 struct paths_changed_args
4170 {
4171   apr_hash_t *changes;
4172   svn_fs_root_t *root;
4173 };
4174
4175
4176 static svn_error_t *
4177 txn_body_paths_changed(void *baton,
4178                        trail_t *trail)
4179 {
4180   /* WARNING: This is called *without* the protection of a Berkeley DB
4181      transaction.  If you modify this function, keep that in mind. */
4182
4183   struct paths_changed_args *args = baton;
4184   const char *txn_id;
4185   svn_fs_t *fs = args->root->fs;
4186
4187   /* Get the transaction ID from ROOT. */
4188   if (! args->root->is_txn_root)
4189     SVN_ERR(svn_fs_base__rev_get_txn_id(&txn_id, fs, args->root->rev,
4190                                         trail, trail->pool));
4191   else
4192     txn_id = args->root->txn;
4193
4194   return svn_fs_bdb__changes_fetch(&(args->changes), fs, txn_id,
4195                                    trail, trail->pool);
4196 }
4197
4198
4199 static svn_error_t *
4200 base_paths_changed(apr_hash_t **changed_paths_p,
4201                    svn_fs_root_t *root,
4202                    apr_pool_t *pool)
4203 {
4204   struct paths_changed_args args;
4205   args.root = root;
4206   args.changes = NULL;
4207   SVN_ERR(svn_fs_base__retry(root->fs, txn_body_paths_changed, &args,
4208                              FALSE, pool));
4209   *changed_paths_p = args.changes;
4210   return SVN_NO_ERROR;
4211 }
4212
4213
4214 \f
4215 /* Our coolio opaque history object. */
4216 typedef struct base_history_data_t
4217 {
4218   /* filesystem object */
4219   svn_fs_t *fs;
4220
4221   /* path and revision of historical location */
4222   const char *path;
4223   svn_revnum_t revision;
4224
4225   /* internal-use hints about where to resume the history search. */
4226   const char *path_hint;
4227   svn_revnum_t rev_hint;
4228
4229   /* FALSE until the first call to svn_fs_history_prev(). */
4230   svn_boolean_t is_interesting;
4231 } base_history_data_t;
4232
4233
4234 static svn_fs_history_t *assemble_history(svn_fs_t *fs, const char *path,
4235                                           svn_revnum_t revision,
4236                                           svn_boolean_t is_interesting,
4237                                           const char *path_hint,
4238                                           svn_revnum_t rev_hint,
4239                                           apr_pool_t *pool);
4240
4241
4242 static svn_error_t *
4243 base_node_history(svn_fs_history_t **history_p,
4244                   svn_fs_root_t *root,
4245                   const char *path,
4246                   apr_pool_t *result_pool,
4247                   apr_pool_t *scratch_pool)
4248 {
4249   svn_node_kind_t kind;
4250
4251   /* We require a revision root. */
4252   if (root->is_txn_root)
4253     return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
4254
4255   /* And we require that the path exist in the root. */
4256   SVN_ERR(base_check_path(&kind, root, path, scratch_pool));
4257   if (kind == svn_node_none)
4258     return SVN_FS__NOT_FOUND(root, path);
4259
4260   /* Okay, all seems well.  Build our history object and return it. */
4261   *history_p = assemble_history(root->fs,
4262                                 svn_fs__canonicalize_abspath(path,
4263                                                              result_pool),
4264                                 root->rev, FALSE, NULL,
4265                                 SVN_INVALID_REVNUM, result_pool);
4266   return SVN_NO_ERROR;
4267 }
4268
4269
4270 /* Examine the PARENT_PATH structure chain to determine how copy IDs
4271    would be doled out in the event that PARENT_PATH was made mutable.
4272    Return the ID of the copy that last affected PARENT_PATH (and the
4273    COPY itself, if we've already fetched it).
4274 */
4275 static svn_error_t *
4276 examine_copy_inheritance(const char **copy_id,
4277                          copy_t **copy,
4278                          svn_fs_t *fs,
4279                          parent_path_t *parent_path,
4280                          trail_t *trail,
4281                          apr_pool_t *pool)
4282 {
4283   /* The default response -- our current copy ID, and no fetched COPY. */
4284   *copy_id = svn_fs_base__id_copy_id
4285     (svn_fs_base__dag_get_id(parent_path->node));
4286   *copy = NULL;
4287
4288   /* If we have no parent (we are looking at the root node), or if
4289      this node is supposed to inherit from itself, return that fact. */
4290   if (! parent_path->parent)
4291     return SVN_NO_ERROR;
4292
4293   /* We could be a branch destination (which would answer our question
4294      altogether)!  But then, again, we might just have been modified
4295      in this revision, so all bets are off. */
4296   if (parent_path->copy_inherit == copy_id_inherit_self)
4297     {
4298       /* A copy ID of "0" means we've never been branched.  Therefore,
4299          there are no copies relevant to our history. */
4300       if (((*copy_id)[0] == '0') && ((*copy_id)[1] == '\0'))
4301         return SVN_NO_ERROR;
4302
4303       /* Get the COPY record.  If it was a real copy (not an implicit
4304          one), we have our answer.  Otherwise, we fall through to the
4305          recursive case. */
4306       SVN_ERR(svn_fs_bdb__get_copy(copy, fs, *copy_id, trail, pool));
4307       if ((*copy)->kind != copy_kind_soft)
4308         return SVN_NO_ERROR;
4309     }
4310
4311   /* Otherwise, our answer is dependent upon our parent. */
4312   return examine_copy_inheritance(copy_id, copy, fs,
4313                                   parent_path->parent, trail, pool);
4314 }
4315
4316
4317 struct history_prev_args
4318 {
4319   svn_fs_history_t **prev_history_p;
4320   svn_fs_history_t *history;
4321   svn_boolean_t cross_copies;
4322   apr_pool_t *pool;
4323 };
4324
4325
4326 static svn_error_t *
4327 txn_body_history_prev(void *baton, trail_t *trail)
4328 {
4329   struct history_prev_args *args = baton;
4330   svn_fs_history_t **prev_history = args->prev_history_p;
4331   svn_fs_history_t *history = args->history;
4332   base_history_data_t *bhd = history->fsap_data;
4333   const char *commit_path, *src_path, *path = bhd->path;
4334   svn_revnum_t commit_rev, src_rev, dst_rev, revision = bhd->revision;
4335   apr_pool_t *retpool = args->pool;
4336   svn_fs_t *fs = bhd->fs;
4337   parent_path_t *parent_path;
4338   dag_node_t *node;
4339   svn_fs_root_t *root;
4340   const svn_fs_id_t *node_id;
4341   const char *end_copy_id = NULL;
4342   struct revision_root_args rr_args;
4343   svn_boolean_t reported = bhd->is_interesting;
4344   const char *txn_id;
4345   copy_t *copy = NULL;
4346   svn_boolean_t retry = FALSE;
4347
4348   /* Initialize our return value. */
4349   *prev_history = NULL;
4350
4351   /* If our last history report left us hints about where to pickup
4352      the chase, then our last report was on the destination of a
4353      copy.  If we are crossing copies, start from those locations,
4354      otherwise, we're all done here.  */
4355   if (bhd->path_hint && SVN_IS_VALID_REVNUM(bhd->rev_hint))
4356     {
4357       reported = FALSE;
4358       if (! args->cross_copies)
4359         return SVN_NO_ERROR;
4360       path = bhd->path_hint;
4361       revision = bhd->rev_hint;
4362     }
4363
4364   /* Construct a ROOT for the current revision. */
4365   rr_args.root_p = &root;
4366   rr_args.rev = revision;
4367   SVN_ERR(txn_body_revision_root(&rr_args, trail));
4368
4369   /* Open PATH/REVISION, and get its node and a bunch of other
4370      goodies.  */
4371   SVN_ERR(svn_fs_base__rev_get_txn_id(&txn_id, fs, revision, trail,
4372                                       trail->pool));
4373   SVN_ERR(open_path(&parent_path, root, path, 0, txn_id,
4374                     trail, trail->pool));
4375   node = parent_path->node;
4376   node_id = svn_fs_base__dag_get_id(node);
4377   commit_path = svn_fs_base__dag_get_created_path(node);
4378   SVN_ERR(svn_fs_base__dag_get_revision(&commit_rev, node,
4379                                         trail, trail->pool));
4380
4381   /* The Subversion filesystem is written in such a way that a given
4382      line of history may have at most one interesting history point
4383      per filesystem revision.  Either that node was edited (and
4384      possibly copied), or it was copied but not edited.  And a copy
4385      source cannot be from the same revision as its destination.  So,
4386      if our history revision matches its node's commit revision, we
4387      know that ... */
4388   if (revision == commit_rev)
4389     {
4390       if (! reported)
4391         {
4392           /* ... we either have not yet reported on this revision (and
4393              need now to do so) ... */
4394           *prev_history = assemble_history(fs,
4395                                            apr_pstrdup(retpool, commit_path),
4396                                            commit_rev, TRUE, NULL,
4397                                            SVN_INVALID_REVNUM, retpool);
4398           return SVN_NO_ERROR;
4399         }
4400       else
4401         {
4402           /* ... or we *have* reported on this revision, and must now
4403              progress toward this node's predecessor (unless there is
4404              no predecessor, in which case we're all done!). */
4405           const svn_fs_id_t *pred_id;
4406
4407           SVN_ERR(svn_fs_base__dag_get_predecessor_id(&pred_id, node,
4408                                                       trail, trail->pool));
4409           if (! pred_id)
4410             return SVN_NO_ERROR;
4411
4412           /* Replace NODE and friends with the information from its
4413              predecessor. */
4414           SVN_ERR(svn_fs_base__dag_get_node(&node, fs, pred_id,
4415                                             trail, trail->pool));
4416           node_id = svn_fs_base__dag_get_id(node);
4417           commit_path = svn_fs_base__dag_get_created_path(node);
4418           SVN_ERR(svn_fs_base__dag_get_revision(&commit_rev, node,
4419                                                 trail, trail->pool));
4420         }
4421     }
4422
4423   /* Calculate a possibly relevant copy ID. */
4424   SVN_ERR(examine_copy_inheritance(&end_copy_id, &copy, fs,
4425                                    parent_path, trail, trail->pool));
4426
4427   /* Initialize some state variables. */
4428   src_path = NULL;
4429   src_rev = SVN_INVALID_REVNUM;
4430   dst_rev = SVN_INVALID_REVNUM;
4431
4432   /* If our current copy ID (which is either the real copy ID of our
4433      node, or the last copy ID which would affect our node if it were
4434      to be made mutable) diffs at all from that of its predecessor
4435      (which is either a real predecessor, or is the node itself
4436      playing the predecessor role to an imaginary mutable successor),
4437      then we need to report a copy.  */
4438   if (strcmp(svn_fs_base__id_copy_id(node_id), end_copy_id) != 0)
4439     {
4440       const char *remainder;
4441       dag_node_t *dst_node;
4442       const char *copy_dst;
4443
4444       /* Get the COPY record if we haven't already fetched it. */
4445       if (! copy)
4446         SVN_ERR(svn_fs_bdb__get_copy(&copy, fs, end_copy_id, trail,
4447                                      trail->pool));
4448
4449       /* Figure out the destination path of the copy operation. */
4450       SVN_ERR(svn_fs_base__dag_get_node(&dst_node, fs,
4451                                         copy->dst_noderev_id,
4452                                         trail, trail->pool));
4453       copy_dst = svn_fs_base__dag_get_created_path(dst_node);
4454
4455       /* If our current path was the very destination of the copy,
4456          then our new current path will be the copy source.  If our
4457          current path was instead the *child* of the destination of
4458          the copy, then figure out its previous location by taking its
4459          path relative to the copy destination and appending that to
4460          the copy source.  Finally, if our current path doesn't meet
4461          one of these other criteria ... ### for now just fallback to
4462          the old copy hunt algorithm. */
4463       remainder = svn_fspath__skip_ancestor(copy_dst, path);
4464
4465       if (remainder)
4466         {
4467           /* If we get here, then our current path is the destination
4468              of, or the child of the destination of, a copy.  Fill
4469              in the return values and get outta here.  */
4470           SVN_ERR(svn_fs_base__txn_get_revision
4471                   (&src_rev, fs, copy->src_txn_id, trail, trail->pool));
4472           SVN_ERR(svn_fs_base__txn_get_revision
4473                   (&dst_rev, fs,
4474                    svn_fs_base__id_txn_id(copy->dst_noderev_id),
4475                    trail, trail->pool));
4476           src_path = svn_fspath__join(copy->src_path, remainder,
4477                                      trail->pool);
4478           if (copy->kind == copy_kind_soft)
4479             retry = TRUE;
4480         }
4481     }
4482
4483   /* If we calculated a copy source path and revision, and the
4484      copy source revision doesn't pre-date a revision in which we
4485      *know* our node was modified, we'll make a 'copy-style' history
4486      object. */
4487   if (src_path && SVN_IS_VALID_REVNUM(src_rev) && (src_rev >= commit_rev))
4488     {
4489       /* It's possible for us to find a copy location that is the same
4490          as the history point we've just reported.  If that happens,
4491          we simply need to take another trip through this history
4492          search. */
4493       if ((dst_rev == revision) && reported)
4494         retry = TRUE;
4495
4496       *prev_history = assemble_history(fs, apr_pstrdup(retpool, path),
4497                                        dst_rev, ! retry,
4498                                        src_path, src_rev, retpool);
4499     }
4500   else
4501     {
4502       *prev_history = assemble_history(fs, apr_pstrdup(retpool, commit_path),
4503                                        commit_rev, TRUE, NULL,
4504                                        SVN_INVALID_REVNUM, retpool);
4505     }
4506
4507   return SVN_NO_ERROR;
4508 }
4509
4510
4511 static svn_error_t *
4512 base_history_prev(svn_fs_history_t **prev_history_p,
4513                   svn_fs_history_t *history,
4514                   svn_boolean_t cross_copies,
4515                   apr_pool_t *result_pool,
4516                   apr_pool_t *scratch_pool)
4517 {
4518   svn_fs_history_t *prev_history = NULL;
4519   base_history_data_t *bhd = history->fsap_data;
4520   svn_fs_t *fs = bhd->fs;
4521
4522   /* Special case: the root directory changes in every single
4523      revision, no exceptions.  And, the root can't be the target (or
4524      child of a target -- duh) of a copy.  So, if that's our path,
4525      then we need only decrement our revision by 1, and there you go. */
4526   if (strcmp(bhd->path, "/") == 0)
4527     {
4528       if (! bhd->is_interesting)
4529         prev_history = assemble_history(fs, "/", bhd->revision,
4530                                         1, NULL, SVN_INVALID_REVNUM,
4531                                         result_pool);
4532       else if (bhd->revision > 0)
4533         prev_history = assemble_history(fs, "/", bhd->revision - 1,
4534                                         1, NULL, SVN_INVALID_REVNUM,
4535                                         result_pool);
4536     }
4537   else
4538     {
4539       struct history_prev_args args;
4540       prev_history = history;
4541
4542       while (1)
4543         {
4544           /* Get a trail, and get to work. */
4545
4546           args.prev_history_p = &prev_history;
4547           args.history = prev_history;
4548           args.cross_copies = cross_copies;
4549           args.pool = result_pool;
4550           SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_history_prev, &args,
4551                                          FALSE, result_pool));
4552           if (! prev_history)
4553             break;
4554           bhd = prev_history->fsap_data;
4555           if (bhd->is_interesting)
4556             break;
4557         }
4558     }
4559
4560   *prev_history_p = prev_history;
4561   return SVN_NO_ERROR;
4562 }
4563
4564
4565 static svn_error_t *
4566 base_history_location(const char **path,
4567                       svn_revnum_t *revision,
4568                       svn_fs_history_t *history,
4569                       apr_pool_t *pool)
4570 {
4571   base_history_data_t *bhd = history->fsap_data;
4572
4573   *path = apr_pstrdup(pool, bhd->path);
4574   *revision = bhd->revision;
4575   return SVN_NO_ERROR;
4576 }
4577
4578
4579 static history_vtable_t history_vtable = {
4580   base_history_prev,
4581   base_history_location
4582 };
4583
4584
4585
4586 struct closest_copy_args
4587 {
4588   svn_fs_root_t **root_p;
4589   const char **path_p;
4590   svn_fs_root_t *root;
4591   const char *path;
4592   apr_pool_t *pool;
4593 };
4594
4595
4596 static svn_error_t *
4597 txn_body_closest_copy(void *baton, trail_t *trail)
4598 {
4599   struct closest_copy_args *args = baton;
4600   svn_fs_root_t *root = args->root;
4601   const char *path = args->path;
4602   svn_fs_t *fs = root->fs;
4603   parent_path_t *parent_path;
4604   const svn_fs_id_t *node_id;
4605   const char *txn_id, *copy_id;
4606   copy_t *copy = NULL;
4607   svn_fs_root_t *copy_dst_root;
4608   dag_node_t *path_node_in_copy_dst, *copy_dst_node, *copy_dst_root_node;
4609   const char *copy_dst_path;
4610   svn_revnum_t copy_dst_rev, created_rev;
4611   svn_error_t *err;
4612
4613   *(args->path_p) = NULL;
4614   *(args->root_p) = NULL;
4615
4616   /* Get the transaction ID associated with our root. */
4617   if (root->is_txn_root)
4618     txn_id = root->txn;
4619   else
4620     SVN_ERR(svn_fs_base__rev_get_txn_id(&txn_id, fs, root->rev,
4621                                         trail, trail->pool));
4622
4623   /* Open PATH in ROOT -- it must exist. */
4624   SVN_ERR(open_path(&parent_path, root, path, 0, txn_id,
4625                     trail, trail->pool));
4626   node_id = svn_fs_base__dag_get_id(parent_path->node);
4627
4628   /* Now, examine the copy inheritance rules in play should our path
4629      be made mutable in the future (if it isn't already).  This will
4630      tell us about the youngest affecting copy.  */
4631   SVN_ERR(examine_copy_inheritance(&copy_id, &copy, fs, parent_path,
4632                                    trail, trail->pool));
4633
4634   /* Easy out:  if the copy ID is 0, there's nothing of interest here. */
4635   if (((copy_id)[0] == '0') && ((copy_id)[1] == '\0'))
4636     return SVN_NO_ERROR;
4637
4638   /* Fetch our copy if examine_copy_inheritance() didn't do it for us. */
4639   if (! copy)
4640     SVN_ERR(svn_fs_bdb__get_copy(&copy, fs, copy_id, trail, trail->pool));
4641
4642   /* Figure out the destination path and revision of the copy operation. */
4643   SVN_ERR(svn_fs_base__dag_get_node(&copy_dst_node, fs, copy->dst_noderev_id,
4644                                     trail, trail->pool));
4645   copy_dst_path = svn_fs_base__dag_get_created_path(copy_dst_node);
4646   SVN_ERR(svn_fs_base__dag_get_revision(&copy_dst_rev, copy_dst_node,
4647                                         trail, trail->pool));
4648
4649   /* Turn that revision into a revision root. */
4650   SVN_ERR(svn_fs_base__dag_revision_root(&copy_dst_root_node, fs,
4651                                          copy_dst_rev, trail, args->pool));
4652   copy_dst_root = make_revision_root(fs, copy_dst_rev,
4653                                      copy_dst_root_node, args->pool);
4654
4655   /* It is possible that this node was created from scratch at some
4656      revision between COPY_DST_REV and the transaction associated with
4657      our ROOT.  Make sure that PATH exists as of COPY_DST_REV and is
4658      related to this node-rev. */
4659   err = get_dag(&path_node_in_copy_dst, copy_dst_root, path,
4660                 trail, trail->pool);
4661   if (err)
4662     {
4663       if ((err->apr_err == SVN_ERR_FS_NOT_FOUND)
4664           || (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY))
4665         {
4666           svn_error_clear(err);
4667           return SVN_NO_ERROR;
4668         }
4669       return svn_error_trace(err);
4670     }
4671   if ((svn_fs_base__dag_node_kind(path_node_in_copy_dst) == svn_node_none)
4672       || (! (svn_fs_base__id_check_related
4673              (node_id, svn_fs_base__dag_get_id(path_node_in_copy_dst)))))
4674     {
4675       return SVN_NO_ERROR;
4676     }
4677
4678   /* One final check must be done here.  If you copy a directory and
4679      create a new entity somewhere beneath that directory in the same
4680      txn, then we can't claim that the copy affected the new entity.
4681      For example, if you do:
4682
4683         copy dir1 dir2
4684         create dir2/new-thing
4685         commit
4686
4687      then dir2/new-thing was not affected by the copy of dir1 to dir2.
4688      We detect this situation by asking if PATH@COPY_DST_REV's
4689      created-rev is COPY_DST_REV, and that node-revision has no
4690      predecessors, then there is no relevant closest copy.
4691   */
4692   SVN_ERR(svn_fs_base__dag_get_revision(&created_rev, path_node_in_copy_dst,
4693                                         trail, trail->pool));
4694   if (created_rev == copy_dst_rev)
4695     {
4696       const svn_fs_id_t *pred_id;
4697       SVN_ERR(svn_fs_base__dag_get_predecessor_id(&pred_id,
4698                                                   path_node_in_copy_dst,
4699                                                   trail, trail->pool));
4700       if (! pred_id)
4701         return SVN_NO_ERROR;
4702     }
4703
4704   *(args->path_p) = apr_pstrdup(args->pool, copy_dst_path);
4705   *(args->root_p) = copy_dst_root;
4706
4707   return SVN_NO_ERROR;
4708 }
4709
4710
4711 static svn_error_t *
4712 base_closest_copy(svn_fs_root_t **root_p,
4713                   const char **path_p,
4714                   svn_fs_root_t *root,
4715                   const char *path,
4716                   apr_pool_t *pool)
4717 {
4718   struct closest_copy_args args;
4719   svn_fs_t *fs = root->fs;
4720   svn_fs_root_t *closest_root = NULL;
4721   const char *closest_path = NULL;
4722
4723   args.root_p = &closest_root;
4724   args.path_p = &closest_path;
4725   args.root = root;
4726   args.path = path;
4727   args.pool = pool;
4728   SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_closest_copy, &args,
4729                                  FALSE, pool));
4730   *root_p = closest_root;
4731   *path_p = closest_path;
4732   return SVN_NO_ERROR;
4733 }
4734
4735
4736 /* Return a new history object (marked as "interesting") for PATH and
4737    REVISION, allocated in POOL, and with its members set to the values
4738    of the parameters provided.  Note that PATH and PATH_HINT are not
4739    duped into POOL -- it is the responsibility of the caller to ensure
4740    that this happens. */
4741 static svn_fs_history_t *
4742 assemble_history(svn_fs_t *fs,
4743                  const char *path,
4744                  svn_revnum_t revision,
4745                  svn_boolean_t is_interesting,
4746                  const char *path_hint,
4747                  svn_revnum_t rev_hint,
4748                  apr_pool_t *pool)
4749 {
4750   svn_fs_history_t *history = apr_pcalloc(pool, sizeof(*history));
4751   base_history_data_t *bhd = apr_pcalloc(pool, sizeof(*bhd));
4752   bhd->path = path;
4753   bhd->revision = revision;
4754   bhd->is_interesting = is_interesting;
4755   bhd->path_hint = path_hint;
4756   bhd->rev_hint = rev_hint;
4757   bhd->fs = fs;
4758   history->vtable = &history_vtable;
4759   history->fsap_data = bhd;
4760   return history;
4761 }
4762
4763
4764 svn_error_t *
4765 svn_fs_base__get_path_kind(svn_node_kind_t *kind,
4766                            const char *path,
4767                            trail_t *trail,
4768                            apr_pool_t *pool)
4769 {
4770   svn_revnum_t head_rev;
4771   svn_fs_root_t *root;
4772   dag_node_t *root_dir, *path_node;
4773   svn_error_t *err;
4774
4775   /* Get HEAD revision, */
4776   SVN_ERR(svn_fs_bdb__youngest_rev(&head_rev, trail->fs, trail, pool));
4777
4778   /* Then convert it into a root_t, */
4779   SVN_ERR(svn_fs_base__dag_revision_root(&root_dir, trail->fs, head_rev,
4780                                          trail, pool));
4781   root = make_revision_root(trail->fs, head_rev, root_dir, pool);
4782
4783   /* And get the dag_node for path in the root_t. */
4784   err = get_dag(&path_node, root, path, trail, pool);
4785   if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND))
4786     {
4787       svn_error_clear(err);
4788       *kind = svn_node_none;
4789       return SVN_NO_ERROR;
4790     }
4791   else if (err)
4792     return svn_error_trace(err);
4793
4794   *kind = svn_fs_base__dag_node_kind(path_node);
4795   return SVN_NO_ERROR;
4796 }
4797
4798
4799 svn_error_t *
4800 svn_fs_base__get_path_created_rev(svn_revnum_t *rev,
4801                                   const char *path,
4802                                   trail_t *trail,
4803                                   apr_pool_t *pool)
4804 {
4805   svn_revnum_t head_rev, created_rev;
4806   svn_fs_root_t *root;
4807   dag_node_t *root_dir, *path_node;
4808   svn_error_t *err;
4809
4810   /* Get HEAD revision, */
4811   SVN_ERR(svn_fs_bdb__youngest_rev(&head_rev, trail->fs, trail, pool));
4812
4813   /* Then convert it into a root_t, */
4814   SVN_ERR(svn_fs_base__dag_revision_root(&root_dir, trail->fs, head_rev,
4815                                          trail, pool));
4816   root = make_revision_root(trail->fs, head_rev, root_dir, pool);
4817
4818   /* And get the dag_node for path in the root_t. */
4819   err = get_dag(&path_node, root, path, trail, pool);
4820   if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND))
4821     {
4822       svn_error_clear(err);
4823       *rev = SVN_INVALID_REVNUM;
4824       return SVN_NO_ERROR;
4825     }
4826   else if (err)
4827     return svn_error_trace(err);
4828
4829   /* Find the created_rev of the dag_node. */
4830   SVN_ERR(svn_fs_base__dag_get_revision(&created_rev, path_node,
4831                                         trail, pool));
4832
4833   *rev = created_rev;
4834   return SVN_NO_ERROR;
4835 }
4836
4837
4838 \f
4839 /*** Finding the Origin of a Line of History ***/
4840
4841 /* Set *PREV_PATH and *PREV_REV to the path and revision which
4842    represent the location at which PATH in FS was located immediately
4843    prior to REVISION iff there was a copy operation (to PATH or one of
4844    its parent directories) between that previous location and
4845    PATH@REVISION.
4846
4847    If there was no such copy operation in that portion of PATH's
4848    history, set *PREV_PATH to NULL and *PREV_REV to SVN_INVALID_REVNUM.
4849
4850    WARNING:  Do *not* call this from inside a trail. */
4851 static svn_error_t *
4852 prev_location(const char **prev_path,
4853               svn_revnum_t *prev_rev,
4854               svn_fs_t *fs,
4855               svn_fs_root_t *root,
4856               const char *path,
4857               apr_pool_t *pool)
4858 {
4859   const char *copy_path, *copy_src_path, *remainder;
4860   svn_fs_root_t *copy_root;
4861   svn_revnum_t copy_src_rev;
4862
4863   /* Ask about the most recent copy which affected PATH@REVISION.  If
4864      there was no such copy, we're done.  */
4865   SVN_ERR(base_closest_copy(&copy_root, &copy_path, root, path, pool));
4866   if (! copy_root)
4867     {
4868       *prev_rev = SVN_INVALID_REVNUM;
4869       *prev_path = NULL;
4870       return SVN_NO_ERROR;
4871     }
4872
4873   /* Ultimately, it's not the path of the closest copy's source that
4874      we care about -- it's our own path's location in the copy source
4875      revision.  So we'll tack the relative path that expresses the
4876      difference between the copy destination and our path in the copy
4877      revision onto the copy source path to determine this information.
4878
4879      In other words, if our path is "/branches/my-branch/foo/bar", and
4880      we know that the closest relevant copy was a copy of "/trunk" to
4881      "/branches/my-branch", then that relative path under the copy
4882      destination is "/foo/bar".  Tacking that onto the copy source
4883      path tells us that our path was located at "/trunk/foo/bar"
4884      before the copy.
4885   */
4886   SVN_ERR(base_copied_from(&copy_src_rev, &copy_src_path,
4887                            copy_root, copy_path, pool));
4888   remainder = svn_fspath__skip_ancestor(copy_path, path);
4889   *prev_path = svn_fspath__join(copy_src_path, remainder, pool);
4890   *prev_rev = copy_src_rev;
4891   return SVN_NO_ERROR;
4892 }
4893
4894
4895 struct id_created_rev_args {
4896   svn_revnum_t revision;
4897   const svn_fs_id_t *id;
4898   const char *path;
4899 };
4900
4901
4902 static svn_error_t *
4903 txn_body_id_created_rev(void *baton, trail_t *trail)
4904 {
4905   struct id_created_rev_args *args = baton;
4906   dag_node_t *node;
4907
4908   SVN_ERR(svn_fs_base__dag_get_node(&node, trail->fs, args->id,
4909                                     trail, trail->pool));
4910   return svn_fs_base__dag_get_revision(&(args->revision), node,
4911                                        trail, trail->pool);
4912 }
4913
4914
4915 struct get_set_node_origin_args {
4916   const svn_fs_id_t *origin_id;
4917   const char *node_id;
4918 };
4919
4920
4921 static svn_error_t *
4922 txn_body_get_node_origin(void *baton, trail_t *trail)
4923 {
4924   struct get_set_node_origin_args *args = baton;
4925   return svn_fs_bdb__get_node_origin(&(args->origin_id), trail->fs,
4926                                      args->node_id, trail, trail->pool);
4927 }
4928
4929 static svn_error_t *
4930 txn_body_set_node_origin(void *baton, trail_t *trail)
4931 {
4932   struct get_set_node_origin_args *args = baton;
4933   return svn_fs_bdb__set_node_origin(trail->fs, args->node_id,
4934                                      args->origin_id, trail, trail->pool);
4935 }
4936
4937 static svn_error_t *
4938 base_node_origin_rev(svn_revnum_t *revision,
4939                      svn_fs_root_t *root,
4940                      const char *path,
4941                      apr_pool_t *pool)
4942 {
4943   svn_fs_t *fs = root->fs;
4944   base_fs_data_t *bfd = fs->fsap_data;
4945   struct get_set_node_origin_args args;
4946   const svn_fs_id_t *origin_id = NULL;
4947   struct id_created_rev_args icr_args;
4948
4949   /* Canonicalize the input path so that the path-math that
4950      prev_location() does below will work. */
4951   path = svn_fs__canonicalize_abspath(path, pool);
4952
4953   /* Special-case the root node (for performance reasons) */
4954   if (strcmp(path, "/") == 0)
4955     {
4956       *revision = 0;
4957       return SVN_NO_ERROR;
4958     }
4959
4960   /* If we have support for the node-origins table, we'll try to use
4961      it. */
4962   if (bfd->format >= SVN_FS_BASE__MIN_NODE_ORIGINS_FORMAT)
4963     {
4964       const svn_fs_id_t *id;
4965       svn_error_t *err;
4966
4967       SVN_ERR(base_node_id(&id, root, path, pool));
4968       args.node_id = svn_fs_base__id_node_id(id);
4969       err = svn_fs_base__retry_txn(root->fs, txn_body_get_node_origin, &args,
4970                                    FALSE, pool);
4971
4972       /* If we got a value for the origin node-revision-ID, that's
4973          great.  If we didn't, that's sad but non-fatal -- we'll just
4974          figure it out the hard way, then record it so we don't have
4975          suffer again the next time. */
4976       if (! err)
4977         {
4978           origin_id = args.origin_id;
4979         }
4980       else if (err->apr_err == SVN_ERR_FS_NO_SUCH_NODE_ORIGIN)
4981         {
4982           svn_error_clear(err);
4983           err = SVN_NO_ERROR;
4984         }
4985       SVN_ERR(err);
4986     }
4987
4988   /* If we haven't yet found a node origin ID, we'll go spelunking for one. */
4989   if (! origin_id)
4990     {
4991       svn_fs_root_t *curroot = root;
4992       apr_pool_t *subpool = svn_pool_create(pool);
4993       apr_pool_t *predidpool = svn_pool_create(pool);
4994       svn_stringbuf_t *lastpath =
4995         svn_stringbuf_create(path, pool);
4996       svn_revnum_t lastrev = SVN_INVALID_REVNUM;
4997       const svn_fs_id_t *pred_id;
4998
4999       /* Walk the closest-copy chain back to the first copy in our history.
5000
5001          NOTE: We merely *assume* that this is faster than walking the
5002          predecessor chain, because we *assume* that copies of parent
5003          directories happen less often than modifications to a given item. */
5004       while (1)
5005         {
5006           svn_revnum_t currev;
5007           const char *curpath = lastpath->data;
5008
5009           /* Get a root pointing to LASTREV.  (The first time around,
5010              LASTREV is invalid, but that's cool because CURROOT is
5011              already initialized.)  */
5012           if (SVN_IS_VALID_REVNUM(lastrev))
5013             SVN_ERR(svn_fs_base__revision_root(&curroot, fs,
5014                                                lastrev, subpool));
5015
5016           /* Find the previous location using the closest-copy shortcut. */
5017           SVN_ERR(prev_location(&curpath, &currev, fs, curroot,
5018                                 curpath, subpool));
5019           if (! curpath)
5020             break;
5021
5022           /* Update our LASTPATH and LASTREV variables (which survive
5023              SUBPOOL). */
5024           svn_stringbuf_set(lastpath, curpath);
5025           lastrev = currev;
5026         }
5027
5028       /* Walk the predecessor links back to origin. */
5029       SVN_ERR(base_node_id(&pred_id, curroot, lastpath->data, pool));
5030       while (1)
5031         {
5032           struct txn_pred_id_args pid_args;
5033           svn_pool_clear(subpool);
5034           pid_args.id = pred_id;
5035           pid_args.pred_id = NULL;
5036           pid_args.pool = subpool;
5037           SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_id, &pid_args,
5038                                          FALSE, subpool));
5039           if (! pid_args.pred_id)
5040             break;
5041           svn_pool_clear(predidpool);
5042           pred_id = svn_fs_base__id_copy(pid_args.pred_id, predidpool);
5043         }
5044
5045       /* Okay.  PRED_ID should hold our origin ID now.  */
5046       origin_id = svn_fs_base__id_copy(pred_id, pool);
5047
5048       /* If our filesystem version supports it, let's remember this
5049          value from now on.  */
5050       if (bfd->format >= SVN_FS_BASE__MIN_NODE_ORIGINS_FORMAT)
5051         {
5052           args.origin_id = origin_id;
5053           SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_set_node_origin,
5054                                          &args, TRUE, subpool));
5055         }
5056
5057       svn_pool_destroy(predidpool);
5058       svn_pool_destroy(subpool);
5059     }
5060
5061   /* Okay.  We have an origin node-revision-ID.  Let's get a created
5062      revision from it. */
5063   icr_args.id = origin_id;
5064   SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_id_created_rev, &icr_args,
5065                                  TRUE, pool));
5066   *revision = icr_args.revision;
5067   return SVN_NO_ERROR;
5068 }
5069
5070
5071 \f
5072 /* Mergeinfo Queries */
5073
5074
5075 /* Examine directory NODE's immediately children for mergeinfo.
5076
5077    For those which have explicit mergeinfo, invoke RECEIVER with
5078    RECEIVER_BATON.
5079
5080    For those which don't, but sit atop trees which contain mergeinfo
5081    somewhere deeper, add them to *CHILDREN_ATOP_MERGEINFO_TREES, a
5082    hash mapping dirent names to dag_node_t * objects, allocated
5083    from that hash's pool.
5084
5085    For those which neither have explicit mergeinfo nor sit atop trees
5086    which contain mergeinfo, ignore them.
5087
5088    Use TRAIL->pool for temporary allocations. */
5089
5090 struct get_mergeinfo_data_and_entries_baton
5091 {
5092   apr_hash_t *children_atop_mergeinfo_trees;
5093   dag_node_t *node;
5094   const char *node_path;
5095   svn_fs_mergeinfo_receiver_t receiver;
5096   void *receiver_baton;
5097 };
5098
5099 static svn_error_t *
5100 txn_body_get_mergeinfo_data_and_entries(void *baton, trail_t *trail)
5101 {
5102   struct get_mergeinfo_data_and_entries_baton *args = baton;
5103   dag_node_t *node = args->node;
5104   apr_hash_t *entries;
5105   apr_hash_index_t *hi;
5106   apr_pool_t *iterpool = svn_pool_create(trail->pool);
5107   apr_pool_t *children_pool =
5108     apr_hash_pool_get(args->children_atop_mergeinfo_trees);
5109
5110   SVN_ERR_ASSERT(svn_fs_base__dag_node_kind(node) == svn_node_dir);
5111
5112   SVN_ERR(svn_fs_base__dag_dir_entries(&entries, node, trail, trail->pool));
5113   for (hi = apr_hash_first(trail->pool, entries); hi; hi = apr_hash_next(hi))
5114     {
5115       void *val;
5116       svn_fs_dirent_t *dirent;
5117       const svn_fs_id_t *child_id;
5118       dag_node_t *child_node;
5119       svn_boolean_t has_mergeinfo;
5120       apr_int64_t kid_count;
5121
5122       svn_pool_clear(iterpool);
5123       apr_hash_this(hi, NULL, NULL, &val);
5124       dirent = val;
5125       child_id = dirent->id;
5126
5127       /* Get the node for this child. */
5128       SVN_ERR(svn_fs_base__dag_get_node(&child_node, trail->fs, child_id,
5129                                         trail, iterpool));
5130
5131       /* Query the child node's mergeinfo stats. */
5132       SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(&has_mergeinfo, &kid_count,
5133                                                    child_node, trail,
5134                                                    iterpool));
5135
5136       /* If the child has mergeinfo, add it to the result catalog. */
5137       if (has_mergeinfo)
5138         {
5139           apr_hash_t *plist;
5140           svn_mergeinfo_t child_mergeinfo;
5141           svn_string_t *pval;
5142           svn_error_t *err;
5143
5144           SVN_ERR(svn_fs_base__dag_get_proplist(&plist, child_node,
5145                                                 trail, iterpool));
5146           pval = svn_hash_gets(plist, SVN_PROP_MERGEINFO);
5147           if (! pval)
5148             {
5149               svn_string_t *id_str = svn_fs_base__id_unparse(child_id,
5150                                                              iterpool);
5151               return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
5152                                        _("Node-revision '%s' claims to have "
5153                                          "mergeinfo but doesn't"),
5154                                        id_str->data);
5155             }
5156           /* Issue #3896: If syntactically invalid mergeinfo is present on
5157              CHILD_NODE then treat it as if no mergeinfo is present rather
5158              than raising a parse error. */
5159           err = svn_mergeinfo_parse(&child_mergeinfo, pval->data,
5160                                     iterpool);
5161           if (err)
5162             {
5163               if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
5164                 svn_error_clear(err);
5165               else
5166                 return svn_error_trace(err);
5167             }
5168           else
5169             {
5170               SVN_ERR(args->receiver(svn_fspath__join(args->node_path,
5171                                                       dirent->name,
5172                                                       iterpool),
5173                                      child_mergeinfo,
5174                                      args->receiver_baton,
5175                                      iterpool));
5176             }
5177         }
5178
5179       /* If the child has descendants with mergeinfo -- that is, if
5180          the count of descendants beneath it carrying mergeinfo, not
5181          including itself, is non-zero -- then add it to the
5182          children_atop_mergeinfo_trees hash to be crawled later. */
5183       if ((kid_count - (has_mergeinfo ? 1 : 0)) > 0)
5184         {
5185           if (svn_fs_base__dag_node_kind(child_node) != svn_node_dir)
5186             {
5187               svn_string_t *id_str = svn_fs_base__id_unparse(child_id,
5188                                                              iterpool);
5189               return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
5190                                        _("Node-revision '%s' claims to sit "
5191                                          "atop a tree containing mergeinfo "
5192                                          "but is not a directory"),
5193                                        id_str->data);
5194             }
5195           svn_hash_sets(args->children_atop_mergeinfo_trees,
5196                         apr_pstrdup(children_pool, dirent->name),
5197                         svn_fs_base__dag_dup(child_node, children_pool));
5198         }
5199     }
5200
5201   svn_pool_destroy(iterpool);
5202   return SVN_NO_ERROR;
5203 }
5204
5205 static svn_error_t *
5206 crawl_directory_for_mergeinfo(svn_fs_t *fs,
5207                               dag_node_t *node,
5208                               const char *node_path,
5209                               svn_fs_mergeinfo_receiver_t receiver,
5210                               void *baton,
5211                               apr_pool_t *pool)
5212 {
5213   struct get_mergeinfo_data_and_entries_baton gmdae_args;
5214   apr_hash_t *children_atop_mergeinfo_trees = apr_hash_make(pool);
5215   apr_hash_index_t *hi;
5216   apr_pool_t *iterpool;
5217
5218   /* Add mergeinfo for immediate children that have it, and fetch
5219      immediate children that *don't* have it but sit atop trees that do. */
5220   gmdae_args.children_atop_mergeinfo_trees = children_atop_mergeinfo_trees;
5221   gmdae_args.node = node;
5222   gmdae_args.node_path = node_path;
5223   gmdae_args.receiver = receiver;
5224   gmdae_args.receiver_baton = baton;
5225   SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_mergeinfo_data_and_entries,
5226                                  &gmdae_args, FALSE, pool));
5227
5228   /* If no children sit atop trees with mergeinfo, we're done.
5229      Otherwise, recurse on those children. */
5230
5231   if (apr_hash_count(children_atop_mergeinfo_trees) == 0)
5232     return SVN_NO_ERROR;
5233
5234   iterpool = svn_pool_create(pool);
5235   for (hi = apr_hash_first(pool, children_atop_mergeinfo_trees);
5236        hi;
5237        hi = apr_hash_next(hi))
5238     {
5239       const void *key;
5240       void *val;
5241       svn_pool_clear(iterpool);
5242       apr_hash_this(hi, &key, NULL, &val);
5243       SVN_ERR(crawl_directory_for_mergeinfo(fs, val,
5244                                             svn_fspath__join(node_path, key,
5245                                                              iterpool),
5246                                             receiver, baton, iterpool));
5247     }
5248   svn_pool_destroy(iterpool);
5249   return SVN_NO_ERROR;
5250 }
5251
5252
5253 /* Calculate the mergeinfo for PATH under revision ROOT using
5254    inheritance type INHERIT.  Set *MERGEINFO to the mergeinfo, or to
5255    NULL if there is none.  Results are allocated in POOL; TRAIL->pool
5256    is used for temporary allocations.  */
5257
5258 struct get_mergeinfo_for_path_baton
5259 {
5260   svn_mergeinfo_t *mergeinfo;
5261   svn_fs_root_t *root;
5262   const char *path;
5263   svn_mergeinfo_inheritance_t inherit;
5264   svn_boolean_t adjust_inherited_mergeinfo;
5265   apr_pool_t *pool;
5266 };
5267
5268 static svn_error_t *
5269 txn_body_get_mergeinfo_for_path(void *baton, trail_t *trail)
5270 {
5271   struct get_mergeinfo_for_path_baton *args = baton;
5272   parent_path_t *parent_path, *nearest_ancestor;
5273   apr_hash_t *proplist;
5274   svn_string_t *mergeinfo_string;
5275   apr_pool_t *iterpool;
5276   dag_node_t *node = NULL;
5277
5278   *(args->mergeinfo) = NULL;
5279
5280   SVN_ERR(open_path(&parent_path, args->root, args->path, 0,
5281                     NULL, trail, trail->pool));
5282
5283   /* Init the nearest ancestor. */
5284   nearest_ancestor = parent_path;
5285   if (args->inherit == svn_mergeinfo_nearest_ancestor)
5286     {
5287       if (! parent_path->parent)
5288         return SVN_NO_ERROR;
5289       nearest_ancestor = parent_path->parent;
5290     }
5291
5292   iterpool = svn_pool_create(trail->pool);
5293   while (TRUE)
5294     {
5295       svn_boolean_t has_mergeinfo;
5296       apr_int64_t count;
5297
5298       svn_pool_clear(iterpool);
5299
5300       node = nearest_ancestor->node;
5301       SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(&has_mergeinfo, &count,
5302                                                    node, trail, iterpool));
5303       if (has_mergeinfo)
5304         break;
5305
5306       /* No need to loop if we're looking for explicit mergeinfo. */
5307       if (args->inherit == svn_mergeinfo_explicit)
5308         {
5309           svn_pool_destroy(iterpool);
5310           return SVN_NO_ERROR;
5311         }
5312
5313       nearest_ancestor = nearest_ancestor->parent;
5314
5315       /* Run out?  There's no mergeinfo. */
5316       if (! nearest_ancestor)
5317         {
5318           svn_pool_destroy(iterpool);
5319           return SVN_NO_ERROR;
5320         }
5321     }
5322   svn_pool_destroy(iterpool);
5323
5324   SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, node, trail, trail->pool));
5325   mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
5326   if (! mergeinfo_string)
5327     {
5328       svn_string_t *id_str =
5329         svn_fs_base__id_unparse(svn_fs_base__dag_get_id(node), trail->pool);
5330       return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
5331                                _("Node-revision '%s' claims to have "
5332                                  "mergeinfo but doesn't"), id_str->data);
5333     }
5334
5335   /* Parse the mergeinfo; store the result in ARGS->MERGEINFO. */
5336   {
5337     /* Issue #3896: If a node has syntactically invalid mergeinfo, then
5338        treat it as if no mergeinfo is present rather than raising a parse
5339        error. */
5340     svn_error_t *err = svn_mergeinfo_parse(args->mergeinfo,
5341                                            mergeinfo_string->data,
5342                                            args->pool);
5343     if (err)
5344       {
5345         if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
5346           {
5347             svn_error_clear(err);
5348             err = NULL;
5349             args->mergeinfo = NULL;
5350           }
5351         return svn_error_trace(err);
5352       }
5353   }
5354
5355   /* If our nearest ancestor is the very path we inquired about, we
5356      can return the mergeinfo results directly.  Otherwise, we're
5357      inheriting the mergeinfo, so we need to a) remove non-inheritable
5358      ranges and b) telescope the merged-from paths. */
5359   if (args->adjust_inherited_mergeinfo && (nearest_ancestor != parent_path))
5360     {
5361       svn_mergeinfo_t tmp_mergeinfo;
5362
5363       SVN_ERR(svn_mergeinfo_inheritable2(&tmp_mergeinfo, *args->mergeinfo,
5364                                          NULL, SVN_INVALID_REVNUM,
5365                                          SVN_INVALID_REVNUM, TRUE,
5366                                          trail->pool, trail->pool));
5367       SVN_ERR(svn_fs__append_to_merged_froms(args->mergeinfo, tmp_mergeinfo,
5368                                              parent_path_relpath(
5369                                                parent_path, nearest_ancestor,
5370                                                trail->pool),
5371                                              args->pool));
5372     }
5373
5374   return SVN_NO_ERROR;
5375 }
5376
5377 /* Set **NODE to the dag node for PATH in ROOT (allocated in POOL),
5378    and query its mergeinfo stats, setting HAS_MERGEINFO and
5379    CHILD_MERGEINFO_COUNT appropriately. */
5380
5381 struct get_node_mergeinfo_stats_baton
5382 {
5383   dag_node_t *node;
5384   svn_boolean_t has_mergeinfo;
5385   apr_int64_t child_mergeinfo_count;
5386   svn_fs_root_t *root;
5387   const char *path;
5388 };
5389
5390 static svn_error_t *
5391 txn_body_get_node_mergeinfo_stats(void *baton, trail_t *trail)
5392 {
5393   struct get_node_mergeinfo_stats_baton *args = baton;
5394
5395   SVN_ERR(get_dag(&(args->node), args->root, args->path,
5396                   trail, trail->pool));
5397   return svn_fs_base__dag_get_mergeinfo_stats(&(args->has_mergeinfo),
5398                                               &(args->child_mergeinfo_count),
5399                                               args->node, trail,
5400                                               trail->pool);
5401 }
5402
5403
5404 /* Find all the mergeinfo for a set of PATHS under ROOT and report it
5405    through RECEIVER with BATON.  INHERITED, INCLUDE_DESCENDANTS and
5406    ADJUST_INHERITED_MERGEINFO are the same as in the FS API.
5407
5408    Allocate temporary values are allocated in SCRATCH_POOL. */
5409 static svn_error_t *
5410 get_mergeinfos_for_paths(svn_fs_root_t *root,
5411                          const apr_array_header_t *paths,
5412                          svn_mergeinfo_inheritance_t inherit,
5413                          svn_boolean_t include_descendants,
5414                          svn_boolean_t adjust_inherited_mergeinfo,
5415                          svn_fs_mergeinfo_receiver_t receiver,
5416                          void *baton,
5417                          apr_pool_t *scratch_pool)
5418 {
5419   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5420   int i;
5421
5422   for (i = 0; i < paths->nelts; i++)
5423     {
5424       svn_mergeinfo_t path_mergeinfo;
5425       struct get_mergeinfo_for_path_baton gmfp_args;
5426       const char *path = APR_ARRAY_IDX(paths, i, const char *);
5427
5428       svn_pool_clear(iterpool);
5429
5430       path = svn_fs__canonicalize_abspath(path, iterpool);
5431
5432       /* Get the mergeinfo for PATH itself. */
5433       gmfp_args.mergeinfo = &path_mergeinfo;
5434       gmfp_args.root = root;
5435       gmfp_args.path = path;
5436       gmfp_args.inherit = inherit;
5437       gmfp_args.pool = iterpool;
5438       gmfp_args.adjust_inherited_mergeinfo = adjust_inherited_mergeinfo;
5439       SVN_ERR(svn_fs_base__retry_txn(root->fs,
5440                                      txn_body_get_mergeinfo_for_path,
5441                                      &gmfp_args, FALSE, iterpool));
5442       if (path_mergeinfo)
5443         SVN_ERR(receiver(path, path_mergeinfo, baton, iterpool));
5444
5445       /* If we're including descendants, do so. */
5446       if (include_descendants)
5447         {
5448           svn_boolean_t do_crawl;
5449           struct get_node_mergeinfo_stats_baton gnms_args;
5450
5451           /* Query the node and its mergeinfo stats. */
5452           gnms_args.root = root;
5453           gnms_args.path = path;
5454           SVN_ERR(svn_fs_base__retry_txn(root->fs,
5455                                          txn_body_get_node_mergeinfo_stats,
5456                                          &gnms_args, FALSE, iterpool));
5457
5458           /* Determine if there's anything worth crawling here. */
5459           if (svn_fs_base__dag_node_kind(gnms_args.node) != svn_node_dir)
5460             do_crawl = FALSE;
5461           else
5462             do_crawl = ((gnms_args.child_mergeinfo_count > 1)
5463                         || ((gnms_args.child_mergeinfo_count == 1)
5464                             && (! gnms_args.has_mergeinfo)));
5465
5466           /* If it's worth crawling, crawl. */
5467           if (do_crawl)
5468             SVN_ERR(crawl_directory_for_mergeinfo(root->fs, gnms_args.node,
5469                                                   path, receiver, baton,
5470                                                   iterpool));
5471         }
5472     }
5473   svn_pool_destroy(iterpool);
5474
5475   return SVN_NO_ERROR;
5476 }
5477
5478
5479 /* Implements svn_fs_get_mergeinfo. */
5480 static svn_error_t *
5481 base_get_mergeinfo(svn_fs_root_t *root,
5482                    const apr_array_header_t *paths,
5483                    svn_mergeinfo_inheritance_t inherit,
5484                    svn_boolean_t include_descendants,
5485                    svn_boolean_t adjust_inherited_mergeinfo,
5486                    svn_fs_mergeinfo_receiver_t receiver,
5487                    void *baton,
5488                    apr_pool_t *scratch_pool)
5489 {
5490   /* Verify that our filesystem version supports mergeinfo stuff. */
5491   SVN_ERR(svn_fs_base__test_required_feature_format
5492           (root->fs, "mergeinfo", SVN_FS_BASE__MIN_MERGEINFO_FORMAT));
5493
5494   /* We require a revision root. */
5495   if (root->is_txn_root)
5496     return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
5497
5498   /* Retrieve a path -> mergeinfo mapping. */
5499   return get_mergeinfos_for_paths(root, paths,
5500                                   inherit, include_descendants,
5501                                   adjust_inherited_mergeinfo,
5502                                   receiver, baton,
5503                                   scratch_pool);
5504 }
5505
5506
5507 \f
5508 /* Creating root objects.  */
5509
5510
5511 static root_vtable_t root_vtable = {
5512   base_paths_changed,
5513   NULL,
5514   base_check_path,
5515   base_node_history,
5516   base_node_id,
5517   base_node_relation,
5518   base_node_created_rev,
5519   base_node_origin_rev,
5520   base_node_created_path,
5521   base_delete_node,
5522   base_copy,
5523   base_revision_link,
5524   base_copied_from,
5525   base_closest_copy,
5526   base_node_prop,
5527   base_node_proplist,
5528   base_node_has_props,
5529   base_change_node_prop,
5530   base_props_changed,
5531   base_dir_entries,
5532   base_dir_optimal_order,
5533   base_make_dir,
5534   base_file_length,
5535   base_file_checksum,
5536   base_file_contents,
5537   NULL,
5538   base_make_file,
5539   base_apply_textdelta,
5540   base_apply_text,
5541   base_contents_changed,
5542   base_get_file_delta_stream,
5543   base_merge,
5544   base_get_mergeinfo,
5545 };
5546
5547
5548 /* Construct a new root object in FS, allocated from POOL.  */
5549 static svn_fs_root_t *
5550 make_root(svn_fs_t *fs,
5551           apr_pool_t *pool)
5552 {
5553   svn_fs_root_t *root = apr_pcalloc(pool, sizeof(*root));
5554   base_root_data_t *brd = apr_palloc(pool, sizeof(*brd));
5555
5556   root->fs = fs;
5557   root->pool = pool;
5558
5559   /* Init the node ID cache. */
5560   brd->node_cache = apr_hash_make(pool);
5561   brd->node_cache_idx = 0;
5562   root->vtable = &root_vtable;
5563   root->fsap_data = brd;
5564
5565   return root;
5566 }
5567
5568
5569 /* Construct a root object referring to the root of REVISION in FS,
5570    whose root directory is ROOT_DIR.  Create the new root in POOL.  */
5571 static svn_fs_root_t *
5572 make_revision_root(svn_fs_t *fs,
5573                    svn_revnum_t rev,
5574                    dag_node_t *root_dir,
5575                    apr_pool_t *pool)
5576 {
5577   svn_fs_root_t *root = make_root(fs, pool);
5578   base_root_data_t *brd = root->fsap_data;
5579
5580   root->is_txn_root = FALSE;
5581   root->rev = rev;
5582   brd->root_dir = root_dir;
5583
5584   return root;
5585 }
5586
5587
5588 /* Construct a root object referring to the root of the transaction
5589    named TXN and based on revision BASE_REV in FS.  FLAGS represents
5590    the behavior of the transaction.  Create the new root in POOL.  */
5591 static svn_fs_root_t *
5592 make_txn_root(svn_fs_t *fs,
5593               const char *txn,
5594               svn_revnum_t base_rev,
5595               apr_uint32_t flags,
5596               apr_pool_t *pool)
5597 {
5598   svn_fs_root_t *root = make_root(fs, pool);
5599   root->is_txn_root = TRUE;
5600   root->txn = apr_pstrdup(root->pool, txn);
5601   root->txn_flags = flags;
5602   root->rev = base_rev;
5603
5604   return root;
5605 }