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