source: valtobtest/subversion-1.6.2/subversion/libsvn_fs_base/tree.c @ 3

Last change on this file since 3 was 3, checked in by valtob, 15 years ago

subversion source 1.6.2 as test

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