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