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