tree.c revision 289166
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 <apr_pools.h>
42#include <apr_hash.h>
43
44#include "svn_hash.h"
45#include "svn_private_config.h"
46#include "svn_pools.h"
47#include "svn_error.h"
48#include "svn_path.h"
49#include "svn_mergeinfo.h"
50#include "svn_fs.h"
51#include "svn_props.h"
52
53#include "fs.h"
54#include "key-gen.h"
55#include "dag.h"
56#include "lock.h"
57#include "tree.h"
58#include "fs_fs.h"
59#include "id.h"
60#include "temp_serializer.h"
61
62#include "private/svn_mergeinfo_private.h"
63#include "private/svn_subr_private.h"
64#include "private/svn_fs_util.h"
65#include "private/svn_fspath.h"
66#include "../libsvn_fs/fs-loader.h"
67
68
69/* ### I believe this constant will become internal to reps-strings.c.
70   ### see the comment in window_consumer() for more information. */
71
72/* ### the comment also seems to need tweaking: the log file stuff
73   ### is no longer an issue... */
74/* Data written to the filesystem through the svn_fs_apply_textdelta()
75   interface is cached in memory until the end of the data stream, or
76   until a size trigger is hit.  Define that trigger here (in bytes).
77   Setting the value to 0 will result in no filesystem buffering at
78   all.  The value only really matters when dealing with file contents
79   bigger than the value itself.  Above that point, large values here
80   allow the filesystem to buffer more data in memory before flushing
81   to the database, which increases memory usage but greatly decreases
82   the amount of disk access (and log-file generation) in database.
83   Smaller values will limit your overall memory consumption, but can
84   drastically hurt throughput by necessitating more write operations
85   to the database (which also generates more log-files).  */
86#define WRITE_BUFFER_SIZE          512000
87
88
89
90/* The root structures.
91
92   Why do they contain different data?  Well, transactions are mutable
93   enough that it isn't safe to cache the DAG node for the root
94   directory or the hash of copyfrom data: somebody else might modify
95   them concurrently on disk!  (Why is the DAG node cache safer than
96   the root DAG node?  When cloning transaction DAG nodes in and out
97   of the cache, all of the possibly-mutable data from the
98   node_revision_t inside the dag_node_t is dropped.)  Additionally,
99   revisions are immutable enough that their DAG node cache can be
100   kept in the FS object and shared among multiple revision root
101   objects.
102*/
103typedef struct fs_rev_root_data_t
104{
105  /* A dag node for the revision's root directory. */
106  dag_node_t *root_dir;
107
108  /* Cache structure for mapping const char * PATH to const char
109     *COPYFROM_STRING, so that paths_changed can remember all the
110     copyfrom information in the changes file.
111     COPYFROM_STRING has the format "REV PATH", or is the empty string if
112     the path was added without history. */
113  apr_hash_t *copyfrom_cache;
114
115} fs_rev_root_data_t;
116
117typedef struct fs_txn_root_data_t
118{
119  const char *txn_id;
120
121  /* Cache of txn DAG nodes (without their nested noderevs, because
122   * it's mutable). Same keys/values as ffd->rev_node_cache. */
123  svn_cache__t *txn_node_cache;
124} fs_txn_root_data_t;
125
126/* Declared here to resolve the circular dependencies. */
127static svn_error_t * get_dag(dag_node_t **dag_node_p,
128                             svn_fs_root_t *root,
129                             const char *path,
130                             apr_pool_t *pool);
131
132static svn_fs_root_t *make_revision_root(svn_fs_t *fs, svn_revnum_t rev,
133                                         dag_node_t *root_dir,
134                                         apr_pool_t *pool);
135
136static svn_error_t *make_txn_root(svn_fs_root_t **root_p,
137                                  svn_fs_t *fs, const char *txn,
138                                  svn_revnum_t base_rev, apr_uint32_t flags,
139                                  apr_pool_t *pool);
140
141
142/*** Node Caching ***/
143
144/* 1st level cache */
145
146/* An entry in the first-level cache.  REVISION and PATH form the key that
147   will ultimately be matched.
148 */
149typedef struct cache_entry_t
150{
151  /* hash value derived from PATH, REVISION.
152     Used to short-circuit failed lookups. */
153  apr_uint32_t hash_value;
154
155  /* revision to which the NODE belongs */
156  svn_revnum_t revision;
157
158  /* path of the NODE */
159  char *path;
160
161  /* cached value of strlen(PATH). */
162  apr_size_t path_len;
163
164  /* the node allocated in the cache's pool. NULL for empty entries. */
165  dag_node_t *node;
166} cache_entry_t;
167
168/* Number of entries in the cache.  Keep this low to keep pressure on the
169   CPU caches low as well.  A binary value is most efficient.  If we walk
170   a directory tree, we want enough entries to store nodes for all files
171   without overwriting the nodes for the parent folder.  That way, there
172   will be no unnecessary misses (except for a few random ones caused by
173   hash collision).
174
175   The actual number of instances may be higher but entries that got
176   overwritten are no longer visible.
177 */
178enum { BUCKET_COUNT = 256 };
179
180/* The actual cache structure.  All nodes will be allocated in POOL.
181   When the number of INSERTIONS (i.e. objects created form that pool)
182   exceeds a certain threshold, the pool will be cleared and the cache
183   with it.
184 */
185struct fs_fs_dag_cache_t
186{
187  /* fixed number of (possibly empty) cache entries */
188  cache_entry_t buckets[BUCKET_COUNT];
189
190  /* pool used for all node allocation */
191  apr_pool_t *pool;
192
193  /* number of entries created from POOL since the last cleanup */
194  apr_size_t insertions;
195
196  /* Property lookups etc. have a very high locality (75% re-hit).
197     Thus, remember the last hit location for optimistic lookup. */
198  apr_size_t last_hit;
199};
200
201fs_fs_dag_cache_t*
202svn_fs_fs__create_dag_cache(apr_pool_t *pool)
203{
204  fs_fs_dag_cache_t *result = apr_pcalloc(pool, sizeof(*result));
205  result->pool = svn_pool_create(pool);
206
207  return result;
208}
209
210/* Clears the CACHE at regular intervals (destroying all cached nodes)
211 */
212static void
213auto_clear_dag_cache(fs_fs_dag_cache_t* cache)
214{
215  if (cache->insertions > BUCKET_COUNT)
216    {
217      svn_pool_clear(cache->pool);
218
219      memset(cache->buckets, 0, sizeof(cache->buckets));
220      cache->insertions = 0;
221    }
222}
223
224/* For the given REVISION and PATH, return the respective entry in CACHE.
225   If the entry is empty, its NODE member will be NULL and the caller
226   may then set it to the corresponding DAG node allocated in CACHE->POOL.
227 */
228static cache_entry_t *
229cache_lookup( fs_fs_dag_cache_t *cache
230            , svn_revnum_t revision
231            , const char *path)
232{
233  apr_size_t i, bucket_index;
234  apr_size_t path_len = strlen(path);
235  apr_uint32_t hash_value = (apr_uint32_t)revision;
236
237#if SVN_UNALIGNED_ACCESS_IS_OK
238  /* "randomizing" / distributing factor used in our hash function */
239  const apr_uint32_t factor = 0xd1f3da69;
240#endif
241
242  /* optimistic lookup: hit the same bucket again? */
243  cache_entry_t *result = &cache->buckets[cache->last_hit];
244  if (   (result->revision == revision)
245      && (result->path_len == path_len)
246      && !memcmp(result->path, path, path_len))
247    {
248      return result;
249    }
250
251  /* need to do a full lookup.  Calculate the hash value
252     (HASH_VALUE has been initialized to REVISION). */
253  i = 0;
254#if SVN_UNALIGNED_ACCESS_IS_OK
255  /* We relax the dependency chain between iterations by processing
256     two chunks from the input per hash_value self-multiplication.
257     The HASH_VALUE update latency is now 1 MUL latency + 1 ADD latency
258     per 2 chunks instead of 1 chunk.
259   */
260  for (; i + 8 <= path_len; i += 8)
261    hash_value = hash_value * factor * factor
262               + (  *(const apr_uint32_t*)(path + i) * factor
263                  + *(const apr_uint32_t*)(path + i + 4));
264#endif
265
266  for (; i < path_len; ++i)
267    /* Help GCC to minimize the HASH_VALUE update latency by splitting the
268       MUL 33 of the naive implementation: h = h * 33 + path[i].  This
269       shortens the dependency chain from 1 shift + 2 ADDs to 1 shift + 1 ADD.
270     */
271    hash_value = hash_value * 32 + (hash_value + (unsigned char)path[i]);
272
273  bucket_index = hash_value + (hash_value >> 16);
274  bucket_index = (bucket_index + (bucket_index >> 8)) % BUCKET_COUNT;
275
276  /* access the corresponding bucket and remember its location */
277  result = &cache->buckets[bucket_index];
278  cache->last_hit = bucket_index;
279
280  /* if it is *NOT* a match,  clear the bucket, expect the caller to fill
281     in the node and count it as an insertion */
282  if (   (result->hash_value != hash_value)
283      || (result->revision != revision)
284      || (result->path_len != path_len)
285      || memcmp(result->path, path, path_len))
286    {
287      result->hash_value = hash_value;
288      result->revision = revision;
289      if (result->path_len < path_len)
290        result->path = apr_palloc(cache->pool, path_len + 1);
291      result->path_len = path_len;
292      memcpy(result->path, path, path_len + 1);
293
294      result->node = NULL;
295
296      cache->insertions++;
297    }
298
299  return result;
300}
301
302/* 2nd level cache */
303
304/* Find and return the DAG node cache for ROOT and the key that
305   should be used for PATH. */
306static void
307locate_cache(svn_cache__t **cache,
308             const char **key,
309             svn_fs_root_t *root,
310             const char *path,
311             apr_pool_t *pool)
312{
313  if (root->is_txn_root)
314    {
315      fs_txn_root_data_t *frd = root->fsap_data;
316      if (cache) *cache = frd->txn_node_cache;
317      if (key && path) *key = path;
318    }
319  else
320    {
321      fs_fs_data_t *ffd = root->fs->fsap_data;
322      if (cache) *cache = ffd->rev_node_cache;
323      if (key && path) *key
324        = svn_fs_fs__combine_number_and_string(root->rev, path, pool);
325    }
326}
327
328/* Return NODE_P for PATH from ROOT's node cache, or NULL if the node
329   isn't cached; read it from the FS. *NODE_P is allocated in POOL. */
330static svn_error_t *
331dag_node_cache_get(dag_node_t **node_p,
332                   svn_fs_root_t *root,
333                   const char *path,
334                   apr_pool_t *pool)
335{
336  svn_boolean_t found;
337  dag_node_t *node = NULL;
338  svn_cache__t *cache;
339  const char *key;
340
341  SVN_ERR_ASSERT(*path == '/');
342
343  if (!root->is_txn_root)
344    {
345      /* immutable DAG node. use the global caches for it */
346
347      fs_fs_data_t *ffd = root->fs->fsap_data;
348      cache_entry_t *bucket;
349
350      auto_clear_dag_cache(ffd->dag_node_cache);
351      bucket = cache_lookup(ffd->dag_node_cache, root->rev, path);
352      if (bucket->node == NULL)
353        {
354          locate_cache(&cache, &key, root, path, pool);
355          SVN_ERR(svn_cache__get((void **)&node, &found, cache, key, pool));
356          if (found && node)
357            {
358              /* Patch up the FS, since this might have come from an old FS
359              * object. */
360              svn_fs_fs__dag_set_fs(node, root->fs);
361
362              /* Retain the DAG node in L1 cache. */
363              bucket->node = svn_fs_fs__dag_dup(node,
364                                                ffd->dag_node_cache->pool);
365            }
366        }
367      else
368        {
369          /* Copy the node from L1 cache into the passed-in POOL. */
370          node = svn_fs_fs__dag_dup(bucket->node, pool);
371        }
372    }
373  else
374    {
375      /* DAG is mutable / may become invalid. Use the TXN-local cache */
376
377      locate_cache(&cache, &key, root, path, pool);
378
379      SVN_ERR(svn_cache__get((void **) &node, &found, cache, key, pool));
380      if (found && node)
381        {
382          /* Patch up the FS, since this might have come from an old FS
383          * object. */
384          svn_fs_fs__dag_set_fs(node, root->fs);
385        }
386    }
387
388  *node_p = node;
389
390  return SVN_NO_ERROR;
391}
392
393
394/* Add the NODE for PATH to ROOT's node cache. */
395static svn_error_t *
396dag_node_cache_set(svn_fs_root_t *root,
397                   const char *path,
398                   dag_node_t *node,
399                   apr_pool_t *pool)
400{
401  svn_cache__t *cache;
402  const char *key;
403
404  SVN_ERR_ASSERT(*path == '/');
405
406  /* Do *not* attempt to dup and put the node into L1.
407   * dup() is twice as expensive as an L2 lookup (which will set also L1).
408   */
409  locate_cache(&cache, &key, root, path, pool);
410
411  return svn_cache__set(cache, key, node, pool);
412}
413
414
415/* Baton for find_descendents_in_cache. */
416struct fdic_baton {
417  const char *path;
418  apr_array_header_t *list;
419  apr_pool_t *pool;
420};
421
422/* If the given item is a descendent of BATON->PATH, push
423 * it onto BATON->LIST (copying into BATON->POOL).  Implements
424 * the svn_iter_apr_hash_cb_t prototype. */
425static svn_error_t *
426find_descendents_in_cache(void *baton,
427                          const void *key,
428                          apr_ssize_t klen,
429                          void *val,
430                          apr_pool_t *pool)
431{
432  struct fdic_baton *b = baton;
433  const char *item_path = key;
434
435  if (svn_fspath__skip_ancestor(b->path, item_path))
436    APR_ARRAY_PUSH(b->list, const char *) = apr_pstrdup(b->pool, item_path);
437
438  return SVN_NO_ERROR;
439}
440
441/* Invalidate cache entries for PATH and any of its children.  This
442   should *only* be called on a transaction root! */
443static svn_error_t *
444dag_node_cache_invalidate(svn_fs_root_t *root,
445                          const char *path,
446                          apr_pool_t *pool)
447{
448  struct fdic_baton b;
449  svn_cache__t *cache;
450  apr_pool_t *iterpool;
451  int i;
452
453  b.path = path;
454  b.pool = svn_pool_create(pool);
455  b.list = apr_array_make(b.pool, 1, sizeof(const char *));
456
457  SVN_ERR_ASSERT(root->is_txn_root);
458  locate_cache(&cache, NULL, root, NULL, b.pool);
459
460
461  SVN_ERR(svn_cache__iter(NULL, cache, find_descendents_in_cache,
462                          &b, b.pool));
463
464  iterpool = svn_pool_create(b.pool);
465
466  for (i = 0; i < b.list->nelts; i++)
467    {
468      const char *descendent = APR_ARRAY_IDX(b.list, i, const char *);
469      svn_pool_clear(iterpool);
470      SVN_ERR(svn_cache__set(cache, descendent, NULL, iterpool));
471    }
472
473  svn_pool_destroy(iterpool);
474  svn_pool_destroy(b.pool);
475  return SVN_NO_ERROR;
476}
477
478
479
480/* Creating transaction and revision root nodes.  */
481
482svn_error_t *
483svn_fs_fs__txn_root(svn_fs_root_t **root_p,
484                    svn_fs_txn_t *txn,
485                    apr_pool_t *pool)
486{
487  apr_uint32_t flags = 0;
488  apr_hash_t *txnprops;
489
490  /* Look for the temporary txn props representing 'flags'. */
491  SVN_ERR(svn_fs_fs__txn_proplist(&txnprops, txn, pool));
492  if (txnprops)
493    {
494      if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD))
495        flags |= SVN_FS_TXN_CHECK_OOD;
496
497      if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS))
498        flags |= SVN_FS_TXN_CHECK_LOCKS;
499    }
500
501  return make_txn_root(root_p, txn->fs, txn->id, txn->base_rev, flags, pool);
502}
503
504
505svn_error_t *
506svn_fs_fs__revision_root(svn_fs_root_t **root_p,
507                         svn_fs_t *fs,
508                         svn_revnum_t rev,
509                         apr_pool_t *pool)
510{
511  dag_node_t *root_dir;
512
513  SVN_ERR(svn_fs__check_fs(fs, TRUE));
514
515  SVN_ERR(svn_fs_fs__dag_revision_root(&root_dir, fs, rev, pool));
516
517  *root_p = make_revision_root(fs, rev, root_dir, pool);
518
519  return SVN_NO_ERROR;
520}
521
522
523
524/* Getting dag nodes for roots.  */
525
526
527/* Set *NODE_P to a freshly opened dag node referring to the root
528   directory of ROOT, allocating from POOL.  */
529static svn_error_t *
530root_node(dag_node_t **node_p,
531          svn_fs_root_t *root,
532          apr_pool_t *pool)
533{
534  if (root->is_txn_root)
535    {
536      /* It's a transaction root.  Open a fresh copy.  */
537      return svn_fs_fs__dag_txn_root(node_p, root->fs, root->txn, pool);
538    }
539  else
540    {
541      /* It's a revision root, so we already have its root directory
542         opened.  */
543      fs_rev_root_data_t *frd = root->fsap_data;
544      *node_p = svn_fs_fs__dag_dup(frd->root_dir, pool);
545      return SVN_NO_ERROR;
546    }
547}
548
549
550/* Set *NODE_P to a mutable root directory for ROOT, cloning if
551   necessary, allocating in POOL.  ROOT must be a transaction root.
552   Use ERROR_PATH in error messages.  */
553static svn_error_t *
554mutable_root_node(dag_node_t **node_p,
555                  svn_fs_root_t *root,
556                  const char *error_path,
557                  apr_pool_t *pool)
558{
559  if (root->is_txn_root)
560    return svn_fs_fs__dag_clone_root(node_p, root->fs, root->txn, pool);
561  else
562    /* If it's not a transaction root, we can't change its contents.  */
563    return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path);
564}
565
566
567
568/* Traversing directory paths.  */
569
570typedef enum copy_id_inherit_t
571{
572  copy_id_inherit_unknown = 0,
573  copy_id_inherit_self,
574  copy_id_inherit_parent,
575  copy_id_inherit_new
576
577} copy_id_inherit_t;
578
579/* A linked list representing the path from a node up to a root
580   directory.  We use this for cloning, and for operations that need
581   to deal with both a node and its parent directory.  For example, a
582   `delete' operation needs to know that the node actually exists, but
583   also needs to change the parent directory.  */
584typedef struct parent_path_t
585{
586
587  /* A node along the path.  This could be the final node, one of its
588     parents, or the root.  Every parent path ends with an element for
589     the root directory.  */
590  dag_node_t *node;
591
592  /* The name NODE has in its parent directory.  This is zero for the
593     root directory, which (obviously) has no name in its parent.  */
594  char *entry;
595
596  /* The parent of NODE, or zero if NODE is the root directory.  */
597  struct parent_path_t *parent;
598
599  /* The copy ID inheritance style. */
600  copy_id_inherit_t copy_inherit;
601
602  /* If copy ID inheritance style is copy_id_inherit_new, this is the
603     path which should be implicitly copied; otherwise, this is NULL. */
604  const char *copy_src_path;
605
606} parent_path_t;
607
608/* Return a text string describing the absolute path of parent_path
609   PARENT_PATH.  It will be allocated in POOL. */
610static const char *
611parent_path_path(parent_path_t *parent_path,
612                 apr_pool_t *pool)
613{
614  const char *path_so_far = "/";
615  if (parent_path->parent)
616    path_so_far = parent_path_path(parent_path->parent, pool);
617  return parent_path->entry
618    ? svn_fspath__join(path_so_far, parent_path->entry, pool)
619    : path_so_far;
620}
621
622
623/* Return the FS path for the parent path chain object CHILD relative
624   to its ANCESTOR in the same chain, allocated in POOL.  */
625static const char *
626parent_path_relpath(parent_path_t *child,
627                    parent_path_t *ancestor,
628                    apr_pool_t *pool)
629{
630  const char *path_so_far = "";
631  parent_path_t *this_node = child;
632  while (this_node != ancestor)
633    {
634      assert(this_node != NULL);
635      path_so_far = svn_relpath_join(this_node->entry, path_so_far, pool);
636      this_node = this_node->parent;
637    }
638  return path_so_far;
639}
640
641
642
643/* Choose a copy ID inheritance method *INHERIT_P to be used in the
644   event that immutable node CHILD in FS needs to be made mutable.  If
645   the inheritance method is copy_id_inherit_new, also return a
646   *COPY_SRC_PATH on which to base the new copy ID (else return NULL
647   for that path).  CHILD must have a parent (it cannot be the root
648   node).  TXN_ID is the transaction in which these items might be
649   mutable.  Allocations are taken from POOL. */
650static svn_error_t *
651get_copy_inheritance(copy_id_inherit_t *inherit_p,
652                     const char **copy_src_path,
653                     svn_fs_t *fs,
654                     parent_path_t *child,
655                     const char *txn_id,
656                     apr_pool_t *pool)
657{
658  const svn_fs_id_t *child_id, *parent_id, *copyroot_id;
659  const char *child_copy_id, *parent_copy_id;
660  const char *id_path = NULL;
661  svn_fs_root_t *copyroot_root;
662  dag_node_t *copyroot_node;
663  svn_revnum_t copyroot_rev;
664  const char *copyroot_path;
665
666  SVN_ERR_ASSERT(child && child->parent && txn_id);
667
668  /* Initialize some convenience variables. */
669  child_id = svn_fs_fs__dag_get_id(child->node);
670  parent_id = svn_fs_fs__dag_get_id(child->parent->node);
671  child_copy_id = svn_fs_fs__id_copy_id(child_id);
672  parent_copy_id = svn_fs_fs__id_copy_id(parent_id);
673
674  /* If this child is already mutable, we have nothing to do. */
675  if (svn_fs_fs__id_txn_id(child_id))
676    {
677      *inherit_p = copy_id_inherit_self;
678      *copy_src_path = NULL;
679      return SVN_NO_ERROR;
680    }
681
682  /* From this point on, we'll assume that the child will just take
683     its copy ID from its parent. */
684  *inherit_p = copy_id_inherit_parent;
685  *copy_src_path = NULL;
686
687  /* Special case: if the child's copy ID is '0', use the parent's
688     copy ID. */
689  if (strcmp(child_copy_id, "0") == 0)
690    return SVN_NO_ERROR;
691
692  /* Compare the copy IDs of the child and its parent.  If they are
693     the same, then the child is already on the same branch as the
694     parent, and should use the same mutability copy ID that the
695     parent will use. */
696  if (svn_fs_fs__key_compare(child_copy_id, parent_copy_id) == 0)
697    return SVN_NO_ERROR;
698
699  /* If the child is on the same branch that the parent is on, the
700     child should just use the same copy ID that the parent would use.
701     Else, the child needs to generate a new copy ID to use should it
702     need to be made mutable.  We will claim that child is on the same
703     branch as its parent if the child itself is not a branch point,
704     or if it is a branch point that we are accessing via its original
705     copy destination path. */
706  SVN_ERR(svn_fs_fs__dag_get_copyroot(&copyroot_rev, &copyroot_path,
707                                      child->node));
708  SVN_ERR(svn_fs_fs__revision_root(&copyroot_root, fs, copyroot_rev, pool));
709  SVN_ERR(get_dag(&copyroot_node, copyroot_root, copyroot_path, pool));
710  copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
711
712  if (svn_fs_fs__id_compare(copyroot_id, child_id) == -1)
713    return SVN_NO_ERROR;
714
715  /* Determine if we are looking at the child via its original path or
716     as a subtree item of a copied tree. */
717  id_path = svn_fs_fs__dag_get_created_path(child->node);
718  if (strcmp(id_path, parent_path_path(child, pool)) == 0)
719    {
720      *inherit_p = copy_id_inherit_self;
721      return SVN_NO_ERROR;
722    }
723
724  /* We are pretty sure that the child node is an unedited nested
725     branched node.  When it needs to be made mutable, it should claim
726     a new copy ID. */
727  *inherit_p = copy_id_inherit_new;
728  *copy_src_path = id_path;
729  return SVN_NO_ERROR;
730}
731
732/* Allocate a new parent_path_t node from POOL, referring to NODE,
733   ENTRY, PARENT, and COPY_ID.  */
734static parent_path_t *
735make_parent_path(dag_node_t *node,
736                 char *entry,
737                 parent_path_t *parent,
738                 apr_pool_t *pool)
739{
740  parent_path_t *parent_path = apr_pcalloc(pool, sizeof(*parent_path));
741  parent_path->node = node;
742  parent_path->entry = entry;
743  parent_path->parent = parent;
744  parent_path->copy_inherit = copy_id_inherit_unknown;
745  parent_path->copy_src_path = NULL;
746  return parent_path;
747}
748
749
750/* Flags for open_path.  */
751typedef enum open_path_flags_t {
752
753  /* The last component of the PATH need not exist.  (All parent
754     directories must exist, as usual.)  If the last component doesn't
755     exist, simply leave the `node' member of the bottom parent_path
756     component zero.  */
757  open_path_last_optional = 1,
758
759  /* When this flag is set, don't bother to lookup the DAG node in
760     our caches because we already tried this.  Ignoring this flag
761     has no functional impact.  */
762  open_path_uncached = 2,
763
764  /* The caller does not care about the parent node chain but only
765     the final DAG node. */
766  open_path_node_only = 4
767} open_path_flags_t;
768
769
770/* Open the node identified by PATH in ROOT, allocating in POOL.  Set
771   *PARENT_PATH_P to a path from the node up to ROOT.  The resulting
772   **PARENT_PATH_P value is guaranteed to contain at least one
773   *element, for the root directory.  PATH must be in canonical form.
774
775   If resulting *PARENT_PATH_P will eventually be made mutable and
776   modified, or if copy ID inheritance information is otherwise
777   needed, TXN_ID should be the ID of the mutability transaction.  If
778   TXN_ID is NULL, no copy ID inheritance information will be
779   calculated for the *PARENT_PATH_P chain.
780
781   If FLAGS & open_path_last_optional is zero, return the error
782   SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist.  If
783   non-zero, require all the parent directories to exist as normal,
784   but if the final path component doesn't exist, simply return a path
785   whose bottom `node' member is zero.  This option is useful for
786   callers that create new nodes --- we find the parent directory for
787   them, and tell them whether the entry exists already.
788
789   The remaining bits in FLAGS are hints that allow this function
790   to take shortcuts based on knowledge that the caller provides,
791   such as the caller is not actually being interested in PARENT_PATH_P,
792   but only in (*PARENT_PATH_P)->NODE.
793
794   NOTE: Public interfaces which only *read* from the filesystem
795   should not call this function directly, but should instead use
796   get_dag().
797*/
798static svn_error_t *
799open_path(parent_path_t **parent_path_p,
800          svn_fs_root_t *root,
801          const char *path,
802          int flags,
803          const char *txn_id,
804          apr_pool_t *pool)
805{
806  svn_fs_t *fs = root->fs;
807  dag_node_t *here = NULL; /* The directory we're currently looking at.  */
808  parent_path_t *parent_path; /* The path from HERE up to the root. */
809  const char *rest; /* The portion of PATH we haven't traversed yet.  */
810
811  /* ensure a canonical path representation */
812  const char *path_so_far = "/";
813  apr_pool_t *iterpool = svn_pool_create(pool);
814
815  /* callers often traverse the tree in some path-based order.  That means
816     a sibling of PATH has been presently accessed.  Try to start the lookup
817     directly at the parent node, if the caller did not requested the full
818     parent chain. */
819  const char *directory;
820  assert(svn_fs__is_canonical_abspath(path));
821  if (flags & open_path_node_only)
822    {
823      directory = svn_dirent_dirname(path, pool);
824      if (directory[1] != 0) /* root nodes are covered anyway */
825        SVN_ERR(dag_node_cache_get(&here, root, directory, pool));
826    }
827
828  /* did the shortcut work? */
829  if (here)
830    {
831      path_so_far = directory;
832      rest = path + strlen(directory) + 1;
833    }
834  else
835    {
836      /* Make a parent_path item for the root node, using its own current
837         copy id.  */
838      SVN_ERR(root_node(&here, root, pool));
839      rest = path + 1; /* skip the leading '/', it saves in iteration */
840    }
841
842  parent_path = make_parent_path(here, 0, 0, pool);
843  parent_path->copy_inherit = copy_id_inherit_self;
844
845  /* Whenever we are at the top of this loop:
846     - HERE is our current directory,
847     - ID is the node revision ID of HERE,
848     - REST is the path we're going to find in HERE, and
849     - PARENT_PATH includes HERE and all its parents.  */
850  for (;;)
851    {
852      const char *next;
853      char *entry;
854      dag_node_t *child;
855
856      svn_pool_clear(iterpool);
857
858      /* Parse out the next entry from the path.  */
859      entry = svn_fs__next_entry_name(&next, rest, pool);
860
861      /* Calculate the path traversed thus far. */
862      path_so_far = svn_fspath__join(path_so_far, entry, pool);
863
864      if (*entry == '\0')
865        {
866          /* Given the behavior of svn_fs__next_entry_name(), this
867             happens when the path either starts or ends with a slash.
868             In either case, we stay put: the current directory stays
869             the same, and we add nothing to the parent path. */
870          child = here;
871        }
872      else
873        {
874          copy_id_inherit_t inherit;
875          const char *copy_path = NULL;
876          svn_error_t *err = SVN_NO_ERROR;
877          dag_node_t *cached_node = NULL;
878
879          /* If we found a directory entry, follow it.  First, we
880             check our node cache, and, failing that, we hit the DAG
881             layer.  Don't bother to contact the cache for the last
882             element if we already know the lookup to fail for the
883             complete path. */
884          if (next || !(flags & open_path_uncached))
885            SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far, pool));
886
887          if (cached_node)
888            child = cached_node;
889          else
890            err = svn_fs_fs__dag_open(&child, here, entry, pool, iterpool);
891
892          /* "file not found" requires special handling.  */
893          if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
894            {
895              /* If this was the last path component, and the caller
896                 said it was optional, then don't return an error;
897                 just put a NULL node pointer in the path.  */
898
899              svn_error_clear(err);
900
901              if ((flags & open_path_last_optional)
902                  && (! next || *next == '\0'))
903                {
904                  parent_path = make_parent_path(NULL, entry, parent_path,
905                                                 pool);
906                  break;
907                }
908              else
909                {
910                  /* Build a better error message than svn_fs_fs__dag_open
911                     can provide, giving the root and full path name.  */
912                  return SVN_FS__NOT_FOUND(root, path);
913                }
914            }
915
916          /* Other errors we return normally.  */
917          SVN_ERR(err);
918
919          if (flags & open_path_node_only)
920            {
921              /* Shortcut: the caller only wan'ts the final DAG node. */
922              parent_path->node = child;
923            }
924          else
925            {
926              /* Now, make a parent_path item for CHILD. */
927              parent_path = make_parent_path(child, entry, parent_path, pool);
928              if (txn_id)
929                {
930                  SVN_ERR(get_copy_inheritance(&inherit, &copy_path, fs,
931                                               parent_path, txn_id, iterpool));
932                  parent_path->copy_inherit = inherit;
933                  parent_path->copy_src_path = apr_pstrdup(pool, copy_path);
934                }
935            }
936
937          /* Cache the node we found (if it wasn't already cached). */
938          if (! cached_node)
939            SVN_ERR(dag_node_cache_set(root, path_so_far, child, iterpool));
940        }
941
942      /* Are we finished traversing the path?  */
943      if (! next)
944        break;
945
946      /* The path isn't finished yet; we'd better be in a directory.  */
947      if (svn_fs_fs__dag_node_kind(child) != svn_node_dir)
948        SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far),
949                  apr_psprintf(iterpool, _("Failure opening '%s'"), path));
950
951      rest = next;
952      here = child;
953    }
954
955  svn_pool_destroy(iterpool);
956  *parent_path_p = parent_path;
957  return SVN_NO_ERROR;
958}
959
960
961/* Make the node referred to by PARENT_PATH mutable, if it isn't
962   already, allocating from POOL.  ROOT must be the root from which
963   PARENT_PATH descends.  Clone any parent directories as needed.
964   Adjust the dag nodes in PARENT_PATH to refer to the clones.  Use
965   ERROR_PATH in error messages.  */
966static svn_error_t *
967make_path_mutable(svn_fs_root_t *root,
968                  parent_path_t *parent_path,
969                  const char *error_path,
970                  apr_pool_t *pool)
971{
972  dag_node_t *clone;
973  const char *txn_id = root->txn;
974
975  /* Is the node mutable already?  */
976  if (svn_fs_fs__dag_check_mutable(parent_path->node))
977    return SVN_NO_ERROR;
978
979  /* Are we trying to clone the root, or somebody's child node?  */
980  if (parent_path->parent)
981    {
982      const svn_fs_id_t *parent_id, *child_id, *copyroot_id;
983      const char *copy_id = NULL;
984      copy_id_inherit_t inherit = parent_path->copy_inherit;
985      const char *clone_path, *copyroot_path;
986      svn_revnum_t copyroot_rev;
987      svn_boolean_t is_parent_copyroot = FALSE;
988      svn_fs_root_t *copyroot_root;
989      dag_node_t *copyroot_node;
990
991      /* We're trying to clone somebody's child.  Make sure our parent
992         is mutable.  */
993      SVN_ERR(make_path_mutable(root, parent_path->parent,
994                                error_path, pool));
995
996      switch (inherit)
997        {
998        case copy_id_inherit_parent:
999          parent_id = svn_fs_fs__dag_get_id(parent_path->parent->node);
1000          copy_id = svn_fs_fs__id_copy_id(parent_id);
1001          break;
1002
1003        case copy_id_inherit_new:
1004          SVN_ERR(svn_fs_fs__reserve_copy_id(&copy_id, root->fs, txn_id,
1005                                             pool));
1006          break;
1007
1008        case copy_id_inherit_self:
1009          copy_id = NULL;
1010          break;
1011
1012        case copy_id_inherit_unknown:
1013        default:
1014          SVN_ERR_MALFUNCTION(); /* uh-oh -- somebody didn't calculate copy-ID
1015                      inheritance data. */
1016        }
1017
1018      /* Determine what copyroot our new child node should use. */
1019      SVN_ERR(svn_fs_fs__dag_get_copyroot(&copyroot_rev, &copyroot_path,
1020                                          parent_path->node));
1021      SVN_ERR(svn_fs_fs__revision_root(&copyroot_root, root->fs,
1022                                       copyroot_rev, pool));
1023      SVN_ERR(get_dag(&copyroot_node, copyroot_root, copyroot_path, pool));
1024
1025      child_id = svn_fs_fs__dag_get_id(parent_path->node);
1026      copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
1027      if (strcmp(svn_fs_fs__id_node_id(child_id),
1028                 svn_fs_fs__id_node_id(copyroot_id)) != 0)
1029        is_parent_copyroot = TRUE;
1030
1031      /* Now make this node mutable.  */
1032      clone_path = parent_path_path(parent_path->parent, pool);
1033      SVN_ERR(svn_fs_fs__dag_clone_child(&clone,
1034                                         parent_path->parent->node,
1035                                         clone_path,
1036                                         parent_path->entry,
1037                                         copy_id, txn_id,
1038                                         is_parent_copyroot,
1039                                         pool));
1040
1041      /* Update the path cache. */
1042      SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool),
1043                                 clone, pool));
1044    }
1045  else
1046    {
1047      /* We're trying to clone the root directory.  */
1048      SVN_ERR(mutable_root_node(&clone, root, error_path, pool));
1049    }
1050
1051  /* Update the PARENT_PATH link to refer to the clone.  */
1052  parent_path->node = clone;
1053
1054  return SVN_NO_ERROR;
1055}
1056
1057
1058/* Open the node identified by PATH in ROOT.  Set DAG_NODE_P to the
1059   node we find, allocated in POOL.  Return the error
1060   SVN_ERR_FS_NOT_FOUND if this node doesn't exist. */
1061static svn_error_t *
1062get_dag(dag_node_t **dag_node_p,
1063        svn_fs_root_t *root,
1064        const char *path,
1065        apr_pool_t *pool)
1066{
1067  parent_path_t *parent_path;
1068  dag_node_t *node = NULL;
1069
1070  /* First we look for the DAG in our cache
1071     (if the path may be canonical). */
1072  if (*path == '/')
1073    SVN_ERR(dag_node_cache_get(&node, root, path, pool));
1074
1075  if (! node)
1076    {
1077      /* Canonicalize the input PATH. */
1078      if (! svn_fs__is_canonical_abspath(path))
1079        {
1080          path = svn_fs__canonicalize_abspath(path, pool);
1081
1082          /* Try again with the corrected path. */
1083          SVN_ERR(dag_node_cache_get(&node, root, path, pool));
1084        }
1085
1086      if (! node)
1087        {
1088          /* Call open_path with no flags, as we want this to return an
1089           * error if the node for which we are searching doesn't exist. */
1090          SVN_ERR(open_path(&parent_path, root, path,
1091                            open_path_uncached | open_path_node_only,
1092                            NULL, pool));
1093          node = parent_path->node;
1094
1095          /* No need to cache our find -- open_path() will do that for us. */
1096        }
1097    }
1098
1099  *dag_node_p = node;
1100  return SVN_NO_ERROR;
1101}
1102
1103
1104
1105/* Populating the `changes' table. */
1106
1107/* Add a change to the changes table in FS, keyed on transaction id
1108   TXN_ID, and indicated that a change of kind CHANGE_KIND occurred on
1109   PATH (whose node revision id is--or was, in the case of a
1110   deletion--NODEREV_ID), and optionally that TEXT_MODs or PROP_MODs
1111   occurred.  If the change resulted from a copy, COPYFROM_REV and
1112   COPYFROM_PATH specify under which revision and path the node was
1113   copied from.  If this was not part of a copy, COPYFROM_REV should
1114   be SVN_INVALID_REVNUM.  Do all this as part of POOL.  */
1115static svn_error_t *
1116add_change(svn_fs_t *fs,
1117           const char *txn_id,
1118           const char *path,
1119           const svn_fs_id_t *noderev_id,
1120           svn_fs_path_change_kind_t change_kind,
1121           svn_boolean_t text_mod,
1122           svn_boolean_t prop_mod,
1123           svn_node_kind_t node_kind,
1124           svn_revnum_t copyfrom_rev,
1125           const char *copyfrom_path,
1126           apr_pool_t *pool)
1127{
1128  return svn_fs_fs__add_change(fs, txn_id,
1129                               svn_fs__canonicalize_abspath(path, pool),
1130                               noderev_id, change_kind, text_mod, prop_mod,
1131                               node_kind, copyfrom_rev, copyfrom_path,
1132                               pool);
1133}
1134
1135
1136
1137/* Generic node operations.  */
1138
1139/* Get the id of a node referenced by path PATH in ROOT.  Return the
1140   id in *ID_P allocated in POOL. */
1141svn_error_t *
1142svn_fs_fs__node_id(const svn_fs_id_t **id_p,
1143                   svn_fs_root_t *root,
1144                   const char *path,
1145                   apr_pool_t *pool)
1146{
1147  if ((! root->is_txn_root)
1148      && (path[0] == '\0' || ((path[0] == '/') && (path[1] == '\0'))))
1149    {
1150      /* Optimize the case where we don't need any db access at all.
1151         The root directory ("" or "/") node is stored in the
1152         svn_fs_root_t object, and never changes when it's a revision
1153         root, so we can just reach in and grab it directly. */
1154      fs_rev_root_data_t *frd = root->fsap_data;
1155      *id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(frd->root_dir), pool);
1156    }
1157  else
1158    {
1159      dag_node_t *node;
1160
1161      SVN_ERR(get_dag(&node, root, path, pool));
1162      *id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(node), pool);
1163    }
1164  return SVN_NO_ERROR;
1165}
1166
1167
1168svn_error_t *
1169svn_fs_fs__node_created_rev(svn_revnum_t *revision,
1170                            svn_fs_root_t *root,
1171                            const char *path,
1172                            apr_pool_t *pool)
1173{
1174  dag_node_t *node;
1175
1176  SVN_ERR(get_dag(&node, root, path, pool));
1177  return svn_fs_fs__dag_get_revision(revision, node, pool);
1178}
1179
1180
1181/* Set *CREATED_PATH to the path at which PATH under ROOT was created.
1182   Return a string allocated in POOL. */
1183static svn_error_t *
1184fs_node_created_path(const char **created_path,
1185                     svn_fs_root_t *root,
1186                     const char *path,
1187                     apr_pool_t *pool)
1188{
1189  dag_node_t *node;
1190
1191  SVN_ERR(get_dag(&node, root, path, pool));
1192  *created_path = svn_fs_fs__dag_get_created_path(node);
1193
1194  return SVN_NO_ERROR;
1195}
1196
1197
1198/* Set *KIND_P to the type of node located at PATH under ROOT.
1199   Perform temporary allocations in POOL. */
1200static svn_error_t *
1201node_kind(svn_node_kind_t *kind_p,
1202          svn_fs_root_t *root,
1203          const char *path,
1204          apr_pool_t *pool)
1205{
1206  const svn_fs_id_t *node_id;
1207  dag_node_t *node;
1208
1209  /* Get the node id. */
1210  SVN_ERR(svn_fs_fs__node_id(&node_id, root, path, pool));
1211
1212  /* Use the node id to get the real kind. */
1213  SVN_ERR(svn_fs_fs__dag_get_node(&node, root->fs, node_id, pool));
1214  *kind_p = svn_fs_fs__dag_node_kind(node);
1215
1216  return SVN_NO_ERROR;
1217}
1218
1219
1220/* Set *KIND_P to the type of node present at PATH under ROOT.  If
1221   PATH does not exist under ROOT, set *KIND_P to svn_node_none.  Use
1222   POOL for temporary allocation. */
1223svn_error_t *
1224svn_fs_fs__check_path(svn_node_kind_t *kind_p,
1225                      svn_fs_root_t *root,
1226                      const char *path,
1227                      apr_pool_t *pool)
1228{
1229  svn_error_t *err = node_kind(kind_p, root, path, pool);
1230  if (err &&
1231      ((err->apr_err == SVN_ERR_FS_NOT_FOUND)
1232       || (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY)))
1233    {
1234      svn_error_clear(err);
1235      err = SVN_NO_ERROR;
1236      *kind_p = svn_node_none;
1237    }
1238
1239  return svn_error_trace(err);
1240}
1241
1242/* Set *VALUE_P to the value of the property named PROPNAME of PATH in
1243   ROOT.  If the node has no property by that name, set *VALUE_P to
1244   zero.  Allocate the result in POOL. */
1245static svn_error_t *
1246fs_node_prop(svn_string_t **value_p,
1247             svn_fs_root_t *root,
1248             const char *path,
1249             const char *propname,
1250             apr_pool_t *pool)
1251{
1252  dag_node_t *node;
1253  apr_hash_t *proplist;
1254
1255  SVN_ERR(get_dag(&node, root, path, pool));
1256  SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, node, pool));
1257  *value_p = NULL;
1258  if (proplist)
1259    *value_p = svn_hash_gets(proplist, propname);
1260
1261  return SVN_NO_ERROR;
1262}
1263
1264
1265/* Set *TABLE_P to the entire property list of PATH under ROOT, as an
1266   APR hash table allocated in POOL.  The resulting property table
1267   maps property names to pointers to svn_string_t objects containing
1268   the property value. */
1269static svn_error_t *
1270fs_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  dag_node_t *node;
1277
1278  SVN_ERR(get_dag(&node, root, path, pool));
1279  SVN_ERR(svn_fs_fs__dag_get_proplist(&table, node, pool));
1280  *table_p = table ? table : apr_hash_make(pool);
1281
1282  return SVN_NO_ERROR;
1283}
1284
1285
1286static svn_error_t *
1287increment_mergeinfo_up_tree(parent_path_t *pp,
1288                            apr_int64_t increment,
1289                            apr_pool_t *pool)
1290{
1291  for (; pp; pp = pp->parent)
1292    SVN_ERR(svn_fs_fs__dag_increment_mergeinfo_count(pp->node,
1293                                                     increment,
1294                                                     pool));
1295
1296  return SVN_NO_ERROR;
1297}
1298
1299/* Change, add, or delete a node's property value.  The affected node
1300   is PATH under ROOT, the property value to modify is NAME, and VALUE
1301   points to either a string value to set the new contents to, or NULL
1302   if the property should be deleted.  Perform temporary allocations
1303   in POOL. */
1304static svn_error_t *
1305fs_change_node_prop(svn_fs_root_t *root,
1306                    const char *path,
1307                    const char *name,
1308                    const svn_string_t *value,
1309                    apr_pool_t *pool)
1310{
1311  parent_path_t *parent_path;
1312  apr_hash_t *proplist;
1313  const char *txn_id;
1314
1315  if (! root->is_txn_root)
1316    return SVN_FS__NOT_TXN(root);
1317  txn_id = root->txn;
1318
1319  path = svn_fs__canonicalize_abspath(path, pool);
1320  SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, pool));
1321
1322  /* Check (non-recursively) to see if path is locked; if so, check
1323     that we can use it. */
1324  if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
1325    SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, FALSE, FALSE,
1326                                              pool));
1327
1328  SVN_ERR(make_path_mutable(root, parent_path, path, pool));
1329  SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, parent_path->node, pool));
1330
1331  /* If there's no proplist, but we're just deleting a property, exit now. */
1332  if ((! proplist) && (! value))
1333    return SVN_NO_ERROR;
1334
1335  /* Now, if there's no proplist, we know we need to make one. */
1336  if (! proplist)
1337    proplist = apr_hash_make(pool);
1338
1339  if (svn_fs_fs__fs_supports_mergeinfo(root->fs)
1340      && strcmp (name, SVN_PROP_MERGEINFO) == 0)
1341    {
1342      apr_int64_t increment = 0;
1343      svn_boolean_t had_mergeinfo;
1344      SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&had_mergeinfo, parent_path->node));
1345
1346      if (value && !had_mergeinfo)
1347        increment = 1;
1348      else if (!value && had_mergeinfo)
1349        increment = -1;
1350
1351      if (increment != 0)
1352        {
1353          SVN_ERR(increment_mergeinfo_up_tree(parent_path, increment, pool));
1354          SVN_ERR(svn_fs_fs__dag_set_has_mergeinfo(parent_path->node,
1355                                                   (value != NULL), pool));
1356        }
1357    }
1358
1359  /* Set the property. */
1360  svn_hash_sets(proplist, name, value);
1361
1362  /* Overwrite the node's proplist. */
1363  SVN_ERR(svn_fs_fs__dag_set_proplist(parent_path->node, proplist,
1364                                      pool));
1365
1366  /* Make a record of this modification in the changes table. */
1367  return add_change(root->fs, txn_id, path,
1368                    svn_fs_fs__dag_get_id(parent_path->node),
1369                    svn_fs_path_change_modify, FALSE, TRUE,
1370                    svn_fs_fs__dag_node_kind(parent_path->node),
1371                    SVN_INVALID_REVNUM, NULL, pool);
1372}
1373
1374
1375/* Determine if the properties of two path/root combinations are
1376   different.  Set *CHANGED_P to TRUE if the properties at PATH1 under
1377   ROOT1 differ from those at PATH2 under ROOT2, or FALSE otherwise.
1378   Both roots must be in the same filesystem. */
1379static svn_error_t *
1380fs_props_changed(svn_boolean_t *changed_p,
1381                 svn_fs_root_t *root1,
1382                 const char *path1,
1383                 svn_fs_root_t *root2,
1384                 const char *path2,
1385                 apr_pool_t *pool)
1386{
1387  dag_node_t *node1, *node2;
1388
1389  /* Check that roots are in the same fs. */
1390  if (root1->fs != root2->fs)
1391    return svn_error_create
1392      (SVN_ERR_FS_GENERAL, NULL,
1393       _("Cannot compare property value between two different filesystems"));
1394
1395  SVN_ERR(get_dag(&node1, root1, path1, pool));
1396  SVN_ERR(get_dag(&node2, root2, path2, pool));
1397  return svn_fs_fs__dag_things_different(changed_p, NULL,
1398                                         node1, node2);
1399}
1400
1401
1402
1403/* Merges and commits. */
1404
1405/* Set *NODE to the root node of ROOT.  */
1406static svn_error_t *
1407get_root(dag_node_t **node, svn_fs_root_t *root, apr_pool_t *pool)
1408{
1409  return get_dag(node, root, "/", pool);
1410}
1411
1412
1413/* Set the contents of CONFLICT_PATH to PATH, and return an
1414   SVN_ERR_FS_CONFLICT error that indicates that there was a conflict
1415   at PATH.  Perform all allocations in POOL (except the allocation of
1416   CONFLICT_PATH, which should be handled outside this function).  */
1417static svn_error_t *
1418conflict_err(svn_stringbuf_t *conflict_path,
1419             const char *path)
1420{
1421  svn_stringbuf_set(conflict_path, path);
1422  return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL,
1423                           _("Conflict at '%s'"), path);
1424}
1425
1426
1427/* Merge changes between ANCESTOR and SOURCE into TARGET.  ANCESTOR
1428 * and TARGET must be distinct node revisions.  TARGET_PATH should
1429 * correspond to TARGET's full path in its filesystem, and is used for
1430 * reporting conflict location.
1431 *
1432 * SOURCE, TARGET, and ANCESTOR are generally directories; this
1433 * function recursively merges the directories' contents.  If any are
1434 * files, this function simply returns an error whenever SOURCE,
1435 * TARGET, and ANCESTOR are all distinct node revisions.
1436 *
1437 * If there are differences between ANCESTOR and SOURCE that conflict
1438 * with changes between ANCESTOR and TARGET, this function returns an
1439 * SVN_ERR_FS_CONFLICT error, and updates CONFLICT_P to the name of the
1440 * conflicting node in TARGET, with TARGET_PATH prepended as a path.
1441 *
1442 * If there are no conflicting differences, CONFLICT_P is updated to
1443 * the empty string.
1444 *
1445 * CONFLICT_P must point to a valid svn_stringbuf_t.
1446 *
1447 * Do any necessary temporary allocation in POOL.
1448 */
1449static svn_error_t *
1450merge(svn_stringbuf_t *conflict_p,
1451      const char *target_path,
1452      dag_node_t *target,
1453      dag_node_t *source,
1454      dag_node_t *ancestor,
1455      const char *txn_id,
1456      apr_int64_t *mergeinfo_increment_out,
1457      apr_pool_t *pool)
1458{
1459  const svn_fs_id_t *source_id, *target_id, *ancestor_id;
1460  apr_hash_t *s_entries, *t_entries, *a_entries;
1461  apr_hash_index_t *hi;
1462  svn_fs_t *fs;
1463  apr_pool_t *iterpool;
1464  apr_int64_t mergeinfo_increment = 0;
1465  svn_boolean_t fs_supports_mergeinfo;
1466
1467  /* Make sure everyone comes from the same filesystem. */
1468  fs = svn_fs_fs__dag_get_fs(ancestor);
1469  if ((fs != svn_fs_fs__dag_get_fs(source))
1470      || (fs != svn_fs_fs__dag_get_fs(target)))
1471    {
1472      return svn_error_create
1473        (SVN_ERR_FS_CORRUPT, NULL,
1474         _("Bad merge; ancestor, source, and target not all in same fs"));
1475    }
1476
1477  /* We have the same fs, now check it. */
1478  SVN_ERR(svn_fs__check_fs(fs, TRUE));
1479
1480  source_id   = svn_fs_fs__dag_get_id(source);
1481  target_id   = svn_fs_fs__dag_get_id(target);
1482  ancestor_id = svn_fs_fs__dag_get_id(ancestor);
1483
1484  /* It's improper to call this function with ancestor == target. */
1485  if (svn_fs_fs__id_eq(ancestor_id, target_id))
1486    {
1487      svn_string_t *id_str = svn_fs_fs__id_unparse(target_id, pool);
1488      return svn_error_createf
1489        (SVN_ERR_FS_GENERAL, NULL,
1490         _("Bad merge; target '%s' has id '%s', same as ancestor"),
1491         target_path, id_str->data);
1492    }
1493
1494  svn_stringbuf_setempty(conflict_p);
1495
1496  /* Base cases:
1497   * Either no change made in source, or same change as made in target.
1498   * Both mean nothing to merge here.
1499   */
1500  if (svn_fs_fs__id_eq(ancestor_id, source_id)
1501      || (svn_fs_fs__id_eq(source_id, target_id)))
1502    return SVN_NO_ERROR;
1503
1504  /* Else proceed, knowing all three are distinct node revisions.
1505   *
1506   * How to merge from this point:
1507   *
1508   * if (not all 3 are directories)
1509   *   {
1510   *     early exit with conflict;
1511   *   }
1512   *
1513   * // Property changes may only be made to up-to-date
1514   * // directories, because once the client commits the prop
1515   * // change, it bumps the directory's revision, and therefore
1516   * // must be able to depend on there being no other changes to
1517   * // that directory in the repository.
1518   * if (target's property list differs from ancestor's)
1519   *    conflict;
1520   *
1521   * For each entry NAME in the directory ANCESTOR:
1522   *
1523   *   Let ANCESTOR-ENTRY, SOURCE-ENTRY, and TARGET-ENTRY be the IDs of
1524   *   the name within ANCESTOR, SOURCE, and TARGET respectively.
1525   *   (Possibly null if NAME does not exist in SOURCE or TARGET.)
1526   *
1527   *   If ANCESTOR-ENTRY == SOURCE-ENTRY, then:
1528   *     No changes were made to this entry while the transaction was in
1529   *     progress, so do nothing to the target.
1530   *
1531   *   Else if ANCESTOR-ENTRY == TARGET-ENTRY, then:
1532   *     A change was made to this entry while the transaction was in
1533   *     process, but the transaction did not touch this entry.  Replace
1534   *     TARGET-ENTRY with SOURCE-ENTRY.
1535   *
1536   *   Else:
1537   *     Changes were made to this entry both within the transaction and
1538   *     to the repository while the transaction was in progress.  They
1539   *     must be merged or declared to be in conflict.
1540   *
1541   *     If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
1542   *     double delete; flag a conflict.
1543   *
1544   *     If any of the three entries is of type file, declare a conflict.
1545   *
1546   *     If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
1547   *     modification of ANCESTOR-ENTRY (determine by comparing the
1548   *     node-id fields), declare a conflict.  A replacement is
1549   *     incompatible with a modification or other replacement--even
1550   *     an identical replacement.
1551   *
1552   *     Direct modifications were made to the directory ANCESTOR-ENTRY
1553   *     in both SOURCE and TARGET.  Recursively merge these
1554   *     modifications.
1555   *
1556   * For each leftover entry NAME in the directory SOURCE:
1557   *
1558   *   If NAME exists in TARGET, declare a conflict.  Even if SOURCE and
1559   *   TARGET are adding exactly the same thing, two additions are not
1560   *   auto-mergeable with each other.
1561   *
1562   *   Add NAME to TARGET with the entry from SOURCE.
1563   *
1564   * Now that we are done merging the changes from SOURCE into the
1565   * directory TARGET, update TARGET's predecessor to be SOURCE.
1566   */
1567
1568  if ((svn_fs_fs__dag_node_kind(source) != svn_node_dir)
1569      || (svn_fs_fs__dag_node_kind(target) != svn_node_dir)
1570      || (svn_fs_fs__dag_node_kind(ancestor) != svn_node_dir))
1571    {
1572      return conflict_err(conflict_p, target_path);
1573    }
1574
1575
1576  /* Possible early merge failure: if target and ancestor have
1577     different property lists, then the merge should fail.
1578     Propchanges can *only* be committed on an up-to-date directory.
1579     ### TODO: see issue #418 about the inelegance of this.
1580
1581     Another possible, similar, early merge failure: if source and
1582     ancestor have different property lists (meaning someone else
1583     changed directory properties while our commit transaction was
1584     happening), the merge should fail.  See issue #2751.
1585  */
1586  {
1587    node_revision_t *tgt_nr, *anc_nr, *src_nr;
1588
1589    /* Get node revisions for our id's. */
1590    SVN_ERR(svn_fs_fs__get_node_revision(&tgt_nr, fs, target_id, pool));
1591    SVN_ERR(svn_fs_fs__get_node_revision(&anc_nr, fs, ancestor_id, pool));
1592    SVN_ERR(svn_fs_fs__get_node_revision(&src_nr, fs, source_id, pool));
1593
1594    /* Now compare the prop-keys of the skels.  Note that just because
1595       the keys are different -doesn't- mean the proplists have
1596       different contents.  But merge() isn't concerned with contents;
1597       it doesn't do a brute-force comparison on textual contents, so
1598       it won't do that here either.  Checking to see if the propkey
1599       atoms are `equal' is enough. */
1600    if (! svn_fs_fs__noderev_same_rep_key(tgt_nr->prop_rep, anc_nr->prop_rep))
1601      return conflict_err(conflict_p, target_path);
1602    if (! svn_fs_fs__noderev_same_rep_key(src_nr->prop_rep, anc_nr->prop_rep))
1603      return conflict_err(conflict_p, target_path);
1604  }
1605
1606  /* ### todo: it would be more efficient to simply check for a NULL
1607     entries hash where necessary below than to allocate an empty hash
1608     here, but another day, another day... */
1609  SVN_ERR(svn_fs_fs__dag_dir_entries(&s_entries, source, pool));
1610  SVN_ERR(svn_fs_fs__dag_dir_entries(&t_entries, target, pool));
1611  SVN_ERR(svn_fs_fs__dag_dir_entries(&a_entries, ancestor, pool));
1612
1613  fs_supports_mergeinfo = svn_fs_fs__fs_supports_mergeinfo(fs);
1614
1615  /* for each entry E in a_entries... */
1616  iterpool = svn_pool_create(pool);
1617  for (hi = apr_hash_first(pool, a_entries);
1618       hi;
1619       hi = apr_hash_next(hi))
1620    {
1621      svn_fs_dirent_t *s_entry, *t_entry, *a_entry;
1622      const char *name;
1623      apr_ssize_t klen;
1624
1625      svn_pool_clear(iterpool);
1626
1627      name = svn__apr_hash_index_key(hi);
1628      klen = svn__apr_hash_index_klen(hi);
1629      a_entry = svn__apr_hash_index_val(hi);
1630
1631      s_entry = apr_hash_get(s_entries, name, klen);
1632      t_entry = apr_hash_get(t_entries, name, klen);
1633
1634      /* No changes were made to this entry while the transaction was
1635         in progress, so do nothing to the target. */
1636      if (s_entry && svn_fs_fs__id_eq(a_entry->id, s_entry->id))
1637        goto end;
1638
1639      /* A change was made to this entry while the transaction was in
1640         process, but the transaction did not touch this entry. */
1641      else if (t_entry && svn_fs_fs__id_eq(a_entry->id, t_entry->id))
1642        {
1643          dag_node_t *t_ent_node;
1644          SVN_ERR(svn_fs_fs__dag_get_node(&t_ent_node, fs,
1645                                          t_entry->id, iterpool));
1646          if (fs_supports_mergeinfo)
1647            {
1648              apr_int64_t mergeinfo_start;
1649              SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start,
1650                                                         t_ent_node));
1651              mergeinfo_increment -= mergeinfo_start;
1652            }
1653
1654          if (s_entry)
1655            {
1656              dag_node_t *s_ent_node;
1657              SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
1658                                              s_entry->id, iterpool));
1659
1660              if (fs_supports_mergeinfo)
1661                {
1662                  apr_int64_t mergeinfo_end;
1663                  SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end,
1664                                                             s_ent_node));
1665                  mergeinfo_increment += mergeinfo_end;
1666                }
1667
1668              SVN_ERR(svn_fs_fs__dag_set_entry(target, name,
1669                                               s_entry->id,
1670                                               s_entry->kind,
1671                                               txn_id,
1672                                               iterpool));
1673            }
1674          else
1675            {
1676              SVN_ERR(svn_fs_fs__dag_delete(target, name, txn_id, iterpool));
1677            }
1678        }
1679
1680      /* Changes were made to this entry both within the transaction
1681         and to the repository while the transaction was in progress.
1682         They must be merged or declared to be in conflict. */
1683      else
1684        {
1685          dag_node_t *s_ent_node, *t_ent_node, *a_ent_node;
1686          const char *new_tpath;
1687          apr_int64_t sub_mergeinfo_increment;
1688
1689          /* If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
1690             double delete; if one of them is null, that's a delete versus
1691             a modification. In any of these cases, flag a conflict. */
1692          if (s_entry == NULL || t_entry == NULL)
1693            return conflict_err(conflict_p,
1694                                svn_fspath__join(target_path,
1695                                                 a_entry->name,
1696                                                 iterpool));
1697
1698          /* If any of the three entries is of type file, flag a conflict. */
1699          if (s_entry->kind == svn_node_file
1700              || t_entry->kind == svn_node_file
1701              || a_entry->kind == svn_node_file)
1702            return conflict_err(conflict_p,
1703                                svn_fspath__join(target_path,
1704                                                 a_entry->name,
1705                                                 iterpool));
1706
1707          /* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
1708             modification of ANCESTOR-ENTRY, declare a conflict. */
1709          if (strcmp(svn_fs_fs__id_node_id(s_entry->id),
1710                     svn_fs_fs__id_node_id(a_entry->id)) != 0
1711              || strcmp(svn_fs_fs__id_copy_id(s_entry->id),
1712                        svn_fs_fs__id_copy_id(a_entry->id)) != 0
1713              || strcmp(svn_fs_fs__id_node_id(t_entry->id),
1714                        svn_fs_fs__id_node_id(a_entry->id)) != 0
1715              || strcmp(svn_fs_fs__id_copy_id(t_entry->id),
1716                        svn_fs_fs__id_copy_id(a_entry->id)) != 0)
1717            return conflict_err(conflict_p,
1718                                svn_fspath__join(target_path,
1719                                                 a_entry->name,
1720                                                 iterpool));
1721
1722          /* Direct modifications were made to the directory
1723             ANCESTOR-ENTRY in both SOURCE and TARGET.  Recursively
1724             merge these modifications. */
1725          SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
1726                                          s_entry->id, iterpool));
1727          SVN_ERR(svn_fs_fs__dag_get_node(&t_ent_node, fs,
1728                                          t_entry->id, iterpool));
1729          SVN_ERR(svn_fs_fs__dag_get_node(&a_ent_node, fs,
1730                                          a_entry->id, iterpool));
1731          new_tpath = svn_fspath__join(target_path, t_entry->name, iterpool);
1732          SVN_ERR(merge(conflict_p, new_tpath,
1733                        t_ent_node, s_ent_node, a_ent_node,
1734                        txn_id,
1735                        &sub_mergeinfo_increment,
1736                        iterpool));
1737          if (fs_supports_mergeinfo)
1738            mergeinfo_increment += sub_mergeinfo_increment;
1739        }
1740
1741      /* We've taken care of any possible implications E could have.
1742         Remove it from source_entries, so it's easy later to loop
1743         over all the source entries that didn't exist in
1744         ancestor_entries. */
1745    end:
1746      apr_hash_set(s_entries, name, klen, NULL);
1747    }
1748
1749  /* For each entry E in source but not in ancestor */
1750  for (hi = apr_hash_first(pool, s_entries);
1751       hi;
1752       hi = apr_hash_next(hi))
1753    {
1754      svn_fs_dirent_t *s_entry, *t_entry;
1755      const char *name = svn__apr_hash_index_key(hi);
1756      apr_ssize_t klen = svn__apr_hash_index_klen(hi);
1757      dag_node_t *s_ent_node;
1758
1759      svn_pool_clear(iterpool);
1760
1761      s_entry = svn__apr_hash_index_val(hi);
1762      t_entry = apr_hash_get(t_entries, name, klen);
1763
1764      /* If NAME exists in TARGET, declare a conflict. */
1765      if (t_entry)
1766        return conflict_err(conflict_p,
1767                            svn_fspath__join(target_path,
1768                                             t_entry->name,
1769                                             iterpool));
1770
1771      SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
1772                                      s_entry->id, iterpool));
1773      if (fs_supports_mergeinfo)
1774        {
1775          apr_int64_t mergeinfo_s;
1776          SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_s,
1777                                                     s_ent_node));
1778          mergeinfo_increment += mergeinfo_s;
1779        }
1780
1781      SVN_ERR(svn_fs_fs__dag_set_entry
1782              (target, s_entry->name, s_entry->id, s_entry->kind,
1783               txn_id, iterpool));
1784    }
1785  svn_pool_destroy(iterpool);
1786
1787  SVN_ERR(svn_fs_fs__dag_update_ancestry(target, source, pool));
1788
1789  if (fs_supports_mergeinfo)
1790    SVN_ERR(svn_fs_fs__dag_increment_mergeinfo_count(target,
1791                                                     mergeinfo_increment,
1792                                                     pool));
1793
1794  if (mergeinfo_increment_out)
1795    *mergeinfo_increment_out = mergeinfo_increment;
1796
1797  return SVN_NO_ERROR;
1798}
1799
1800/* Merge changes between an ancestor and SOURCE_NODE into
1801   TXN.  The ancestor is either ANCESTOR_NODE, or if
1802   that is null, TXN's base node.
1803
1804   If the merge is successful, TXN's base will become
1805   SOURCE_NODE, and its root node will have a new ID, a
1806   successor of SOURCE_NODE.
1807
1808   If a conflict results, update *CONFLICT to the path in the txn that
1809   conflicted; see the CONFLICT_P parameter of merge() for details. */
1810static svn_error_t *
1811merge_changes(dag_node_t *ancestor_node,
1812              dag_node_t *source_node,
1813              svn_fs_txn_t *txn,
1814              svn_stringbuf_t *conflict,
1815              apr_pool_t *pool)
1816{
1817  dag_node_t *txn_root_node;
1818  svn_fs_t *fs = txn->fs;
1819  const char *txn_id = txn->id;
1820
1821  SVN_ERR(svn_fs_fs__dag_txn_root(&txn_root_node, fs, txn_id, pool));
1822
1823  if (ancestor_node == NULL)
1824    {
1825      SVN_ERR(svn_fs_fs__dag_txn_base_root(&ancestor_node, fs,
1826                                           txn_id, pool));
1827    }
1828
1829  if (svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(ancestor_node),
1830                       svn_fs_fs__dag_get_id(txn_root_node)))
1831    {
1832      /* If no changes have been made in TXN since its current base,
1833         then it can't conflict with any changes since that base.
1834         The caller isn't supposed to call us in that case. */
1835      SVN_ERR_MALFUNCTION();
1836    }
1837  else
1838    SVN_ERR(merge(conflict, "/", txn_root_node,
1839                  source_node, ancestor_node, txn_id, NULL, pool));
1840
1841  return SVN_NO_ERROR;
1842}
1843
1844
1845svn_error_t *
1846svn_fs_fs__commit_txn(const char **conflict_p,
1847                      svn_revnum_t *new_rev,
1848                      svn_fs_txn_t *txn,
1849                      apr_pool_t *pool)
1850{
1851  /* How do commits work in Subversion?
1852   *
1853   * When you're ready to commit, here's what you have:
1854   *
1855   *    1. A transaction, with a mutable tree hanging off it.
1856   *    2. A base revision, against which TXN_TREE was made.
1857   *    3. A latest revision, which may be newer than the base rev.
1858   *
1859   * The problem is that if latest != base, then one can't simply
1860   * attach the txn root as the root of the new revision, because that
1861   * would lose all the changes between base and latest.  It is also
1862   * not acceptable to insist that base == latest; in a busy
1863   * repository, commits happen too fast to insist that everyone keep
1864   * their entire tree up-to-date at all times.  Non-overlapping
1865   * changes should not interfere with each other.
1866   *
1867   * The solution is to merge the changes between base and latest into
1868   * the txn tree [see the function merge()].  The txn tree is the
1869   * only one of the three trees that is mutable, so it has to be the
1870   * one to adjust.
1871   *
1872   * You might have to adjust it more than once, if a new latest
1873   * revision gets committed while you were merging in the previous
1874   * one.  For example:
1875   *
1876   *    1. Jane starts txn T, based at revision 6.
1877   *    2. Someone commits (or already committed) revision 7.
1878   *    3. Jane's starts merging the changes between 6 and 7 into T.
1879   *    4. Meanwhile, someone commits revision 8.
1880   *    5. Jane finishes the 6-->7 merge.  T could now be committed
1881   *       against a latest revision of 7, if only that were still the
1882   *       latest.  Unfortunately, 8 is now the latest, so...
1883   *    6. Jane starts merging the changes between 7 and 8 into T.
1884   *    7. Meanwhile, no one commits any new revisions.  Whew.
1885   *    8. Jane commits T, creating revision 9, whose tree is exactly
1886   *       T's tree, except immutable now.
1887   *
1888   * Lather, rinse, repeat.
1889   */
1890
1891  svn_error_t *err = SVN_NO_ERROR;
1892  svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool);
1893  svn_fs_t *fs = txn->fs;
1894
1895  /* Limit memory usage when the repository has a high commit rate and
1896     needs to run the following while loop multiple times.  The memory
1897     growth without an iteration pool is very noticeable when the
1898     transaction modifies a node that has 20,000 sibling nodes. */
1899  apr_pool_t *iterpool = svn_pool_create(pool);
1900
1901  /* Initialize output params. */
1902  *new_rev = SVN_INVALID_REVNUM;
1903  if (conflict_p)
1904    *conflict_p = NULL;
1905
1906  while (1729)
1907    {
1908      svn_revnum_t youngish_rev;
1909      svn_fs_root_t *youngish_root;
1910      dag_node_t *youngish_root_node;
1911
1912      svn_pool_clear(iterpool);
1913
1914      /* Get the *current* youngest revision.  We call it "youngish"
1915         because new revisions might get committed after we've
1916         obtained it. */
1917
1918      SVN_ERR(svn_fs_fs__youngest_rev(&youngish_rev, fs, iterpool));
1919      SVN_ERR(svn_fs_fs__revision_root(&youngish_root, fs, youngish_rev,
1920                                       iterpool));
1921
1922      /* Get the dag node for the youngest revision.  Later we'll use
1923         it as the SOURCE argument to a merge, and if the merge
1924         succeeds, this youngest root node will become the new base
1925         root for the svn txn that was the target of the merge (but
1926         note that the youngest rev may have changed by then -- that's
1927         why we're careful to get this root in its own bdb txn
1928         here). */
1929      SVN_ERR(get_root(&youngish_root_node, youngish_root, iterpool));
1930
1931      /* Try to merge.  If the merge succeeds, the base root node of
1932         TARGET's txn will become the same as youngish_root_node, so
1933         any future merges will only be between that node and whatever
1934         the root node of the youngest rev is by then. */
1935      err = merge_changes(NULL, youngish_root_node, txn, conflict, iterpool);
1936      if (err)
1937        {
1938          if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
1939            *conflict_p = conflict->data;
1940          goto cleanup;
1941        }
1942      txn->base_rev = youngish_rev;
1943
1944      /* Try to commit. */
1945      err = svn_fs_fs__commit(new_rev, fs, txn, iterpool);
1946      if (err && (err->apr_err == SVN_ERR_FS_TXN_OUT_OF_DATE))
1947        {
1948          /* Did someone else finish committing a new revision while we
1949             were in mid-merge or mid-commit?  If so, we'll need to
1950             loop again to merge the new changes in, then try to
1951             commit again.  Or if that's not what happened, then just
1952             return the error. */
1953          svn_revnum_t youngest_rev;
1954          SVN_ERR(svn_fs_fs__youngest_rev(&youngest_rev, fs, iterpool));
1955          if (youngest_rev == youngish_rev)
1956            goto cleanup;
1957          else
1958            svn_error_clear(err);
1959        }
1960      else if (err)
1961        {
1962          goto cleanup;
1963        }
1964      else
1965        {
1966          err = SVN_NO_ERROR;
1967          goto cleanup;
1968        }
1969    }
1970
1971 cleanup:
1972
1973  svn_fs_fs__reset_txn_caches(fs);
1974
1975  svn_pool_destroy(iterpool);
1976  return svn_error_trace(err);
1977}
1978
1979
1980/* Merge changes between two nodes into a third node.  Given nodes
1981   SOURCE_PATH under SOURCE_ROOT, TARGET_PATH under TARGET_ROOT and
1982   ANCESTOR_PATH under ANCESTOR_ROOT, modify target to contain all the
1983   changes between the ancestor and source.  If there are conflicts,
1984   return SVN_ERR_FS_CONFLICT and set *CONFLICT_P to a textual
1985   description of the offending changes.  Perform any temporary
1986   allocations in POOL. */
1987static svn_error_t *
1988fs_merge(const char **conflict_p,
1989         svn_fs_root_t *source_root,
1990         const char *source_path,
1991         svn_fs_root_t *target_root,
1992         const char *target_path,
1993         svn_fs_root_t *ancestor_root,
1994         const char *ancestor_path,
1995         apr_pool_t *pool)
1996{
1997  dag_node_t *source, *ancestor;
1998  svn_fs_txn_t *txn;
1999  svn_error_t *err;
2000  svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool);
2001
2002  if (! target_root->is_txn_root)
2003    return SVN_FS__NOT_TXN(target_root);
2004
2005  /* Paranoia. */
2006  if ((source_root->fs != ancestor_root->fs)
2007      || (target_root->fs != ancestor_root->fs))
2008    {
2009      return svn_error_create
2010        (SVN_ERR_FS_CORRUPT, NULL,
2011         _("Bad merge; ancestor, source, and target not all in same fs"));
2012    }
2013
2014  /* ### kff todo: is there any compelling reason to get the nodes in
2015     one db transaction?  Right now we don't; txn_body_get_root() gets
2016     one node at a time.  This will probably need to change:
2017
2018     Jim Blandy <jimb@zwingli.cygnus.com> writes:
2019     > svn_fs_merge needs to be a single transaction, to protect it against
2020     > people deleting parents of nodes it's working on, etc.
2021  */
2022
2023  /* Get the ancestor node. */
2024  SVN_ERR(get_root(&ancestor, ancestor_root, pool));
2025
2026  /* Get the source node. */
2027  SVN_ERR(get_root(&source, source_root, pool));
2028
2029  /* Open a txn for the txn root into which we're merging. */
2030  SVN_ERR(svn_fs_fs__open_txn(&txn, ancestor_root->fs, target_root->txn,
2031                              pool));
2032
2033  /* Merge changes between ANCESTOR and SOURCE into TXN. */
2034  err = merge_changes(ancestor, source, txn, conflict, pool);
2035  if (err)
2036    {
2037      if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2038        *conflict_p = conflict->data;
2039      return svn_error_trace(err);
2040    }
2041
2042  return SVN_NO_ERROR;
2043}
2044
2045svn_error_t *
2046svn_fs_fs__deltify(svn_fs_t *fs,
2047                   svn_revnum_t revision,
2048                   apr_pool_t *pool)
2049{
2050  /* Deltify is a no-op for fs_fs. */
2051
2052  return SVN_NO_ERROR;
2053}
2054
2055
2056
2057/* Directories.  */
2058
2059/* Set *TABLE_P to a newly allocated APR hash table containing the
2060   entries of the directory at PATH in ROOT.  The keys of the table
2061   are entry names, as byte strings, excluding the final null
2062   character; the table's values are pointers to svn_fs_dirent_t
2063   structures.  Allocate the table and its contents in POOL. */
2064static svn_error_t *
2065fs_dir_entries(apr_hash_t **table_p,
2066               svn_fs_root_t *root,
2067               const char *path,
2068               apr_pool_t *pool)
2069{
2070  dag_node_t *node;
2071
2072  /* Get the entries for this path in the caller's pool. */
2073  SVN_ERR(get_dag(&node, root, path, pool));
2074  return svn_fs_fs__dag_dir_entries(table_p, node, pool);
2075}
2076
2077/* Raise an error if PATH contains a newline because FSFS cannot handle
2078 * such paths. See issue #4340. */
2079static svn_error_t *
2080check_newline(const char *path, apr_pool_t *pool)
2081{
2082  char *c = strchr(path, '\n');
2083
2084  if (c)
2085    return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
2086       _("Invalid control character '0x%02x' in path '%s'"),
2087       (unsigned char)*c, svn_path_illegal_path_escape(path, pool));
2088
2089  return SVN_NO_ERROR;
2090}
2091
2092/* Create a new directory named PATH in ROOT.  The new directory has
2093   no entries, and no properties.  ROOT must be the root of a
2094   transaction, not a revision.  Do any necessary temporary allocation
2095   in POOL.  */
2096static svn_error_t *
2097fs_make_dir(svn_fs_root_t *root,
2098            const char *path,
2099            apr_pool_t *pool)
2100{
2101  parent_path_t *parent_path;
2102  dag_node_t *sub_dir;
2103  const char *txn_id = root->txn;
2104
2105  SVN_ERR(check_newline(path, pool));
2106
2107  path = svn_fs__canonicalize_abspath(path, pool);
2108  SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
2109                    txn_id, pool));
2110
2111  /* Check (recursively) to see if some lock is 'reserving' a path at
2112     that location, or even some child-path; if so, check that we can
2113     use it. */
2114  if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2115    SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, TRUE, FALSE,
2116                                              pool));
2117
2118  /* If there's already a sub-directory by that name, complain.  This
2119     also catches the case of trying to make a subdirectory named `/'.  */
2120  if (parent_path->node)
2121    return SVN_FS__ALREADY_EXISTS(root, path);
2122
2123  /* Create the subdirectory.  */
2124  SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
2125  SVN_ERR(svn_fs_fs__dag_make_dir(&sub_dir,
2126                                  parent_path->parent->node,
2127                                  parent_path_path(parent_path->parent,
2128                                                   pool),
2129                                  parent_path->entry,
2130                                  txn_id,
2131                                  pool));
2132
2133  /* Add this directory to the path cache. */
2134  SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool),
2135                             sub_dir, pool));
2136
2137  /* Make a record of this modification in the changes table. */
2138  return add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(sub_dir),
2139                    svn_fs_path_change_add, FALSE, FALSE, svn_node_dir,
2140                    SVN_INVALID_REVNUM, NULL, pool);
2141}
2142
2143
2144/* Delete the node at PATH under ROOT.  ROOT must be a transaction
2145   root.  Perform temporary allocations in POOL. */
2146static svn_error_t *
2147fs_delete_node(svn_fs_root_t *root,
2148               const char *path,
2149               apr_pool_t *pool)
2150{
2151  parent_path_t *parent_path;
2152  const char *txn_id = root->txn;
2153  apr_int64_t mergeinfo_count = 0;
2154  svn_node_kind_t kind;
2155
2156  if (! root->is_txn_root)
2157    return SVN_FS__NOT_TXN(root);
2158
2159  path = svn_fs__canonicalize_abspath(path, pool);
2160  SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, pool));
2161  kind = svn_fs_fs__dag_node_kind(parent_path->node);
2162
2163  /* We can't remove the root of the filesystem.  */
2164  if (! parent_path->parent)
2165    return svn_error_create(SVN_ERR_FS_ROOT_DIR, NULL,
2166                            _("The root directory cannot be deleted"));
2167
2168  /* Check to see if path (or any child thereof) is locked; if so,
2169     check that we can use the existing lock(s). */
2170  if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2171    SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, TRUE, FALSE,
2172                                              pool));
2173
2174  /* Make the parent directory mutable, and do the deletion.  */
2175  SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
2176  if (svn_fs_fs__fs_supports_mergeinfo(root->fs))
2177    SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_count,
2178                                               parent_path->node));
2179  SVN_ERR(svn_fs_fs__dag_delete(parent_path->parent->node,
2180                                parent_path->entry,
2181                                txn_id, pool));
2182
2183  /* Remove this node and any children from the path cache. */
2184  SVN_ERR(dag_node_cache_invalidate(root, parent_path_path(parent_path, pool),
2185                                    pool));
2186
2187  /* Update mergeinfo counts for parents */
2188  if (mergeinfo_count > 0)
2189    SVN_ERR(increment_mergeinfo_up_tree(parent_path->parent,
2190                                        -mergeinfo_count,
2191                                        pool));
2192
2193  /* Make a record of this modification in the changes table. */
2194  return add_change(root->fs, txn_id, path,
2195                    svn_fs_fs__dag_get_id(parent_path->node),
2196                    svn_fs_path_change_delete, FALSE, FALSE, kind,
2197                    SVN_INVALID_REVNUM, NULL, pool);
2198}
2199
2200
2201/* Set *SAME_P to TRUE if FS1 and FS2 have the same UUID, else set to FALSE.
2202   Use POOL for temporary allocation only.
2203   Note: this code is duplicated between libsvn_fs_fs and libsvn_fs_base. */
2204static svn_error_t *
2205fs_same_p(svn_boolean_t *same_p,
2206          svn_fs_t *fs1,
2207          svn_fs_t *fs2,
2208          apr_pool_t *pool)
2209{
2210  *same_p = ! strcmp(fs1->uuid, fs2->uuid);
2211  return SVN_NO_ERROR;
2212}
2213
2214/* Copy the node at FROM_PATH under FROM_ROOT to TO_PATH under
2215   TO_ROOT.  If PRESERVE_HISTORY is set, then the copy is recorded in
2216   the copies table.  Perform temporary allocations in POOL. */
2217static svn_error_t *
2218copy_helper(svn_fs_root_t *from_root,
2219            const char *from_path,
2220            svn_fs_root_t *to_root,
2221            const char *to_path,
2222            svn_boolean_t preserve_history,
2223            apr_pool_t *pool)
2224{
2225  dag_node_t *from_node;
2226  parent_path_t *to_parent_path;
2227  const char *txn_id = to_root->txn;
2228  svn_boolean_t same_p;
2229
2230  /* Use an error check, not an assert, because even the caller cannot
2231     guarantee that a filesystem's UUID has not changed "on the fly". */
2232  SVN_ERR(fs_same_p(&same_p, from_root->fs, to_root->fs, pool));
2233  if (! same_p)
2234    return svn_error_createf
2235      (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2236       _("Cannot copy between two different filesystems ('%s' and '%s')"),
2237       from_root->fs->path, to_root->fs->path);
2238
2239  if (from_root->is_txn_root)
2240    return svn_error_create
2241      (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2242       _("Copy from mutable tree not currently supported"));
2243
2244  /* Get the NODE for FROM_PATH in FROM_ROOT.*/
2245  SVN_ERR(get_dag(&from_node, from_root, from_path, pool));
2246
2247  /* Build up the parent path from TO_PATH in TO_ROOT.  If the last
2248     component does not exist, it's not that big a deal.  We'll just
2249     make one there. */
2250  SVN_ERR(open_path(&to_parent_path, to_root, to_path,
2251                    open_path_last_optional, txn_id, pool));
2252
2253  /* Check to see if path (or any child thereof) is locked; if so,
2254     check that we can use the existing lock(s). */
2255  if (to_root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2256    SVN_ERR(svn_fs_fs__allow_locked_operation(to_path, to_root->fs,
2257                                              TRUE, FALSE, pool));
2258
2259  /* If the destination node already exists as the same node as the
2260     source (in other words, this operation would result in nothing
2261     happening at all), just do nothing an return successfully,
2262     proud that you saved yourself from a tiresome task. */
2263  if (to_parent_path->node &&
2264      svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(from_node),
2265                       svn_fs_fs__dag_get_id(to_parent_path->node)))
2266    return SVN_NO_ERROR;
2267
2268  if (! from_root->is_txn_root)
2269    {
2270      svn_fs_path_change_kind_t kind;
2271      dag_node_t *new_node;
2272      const char *from_canonpath;
2273      apr_int64_t mergeinfo_start;
2274      apr_int64_t mergeinfo_end;
2275
2276      /* If TO_PATH already existed prior to the copy, note that this
2277         operation is a replacement, not an addition. */
2278      if (to_parent_path->node)
2279        {
2280          kind = svn_fs_path_change_replace;
2281          if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs))
2282            SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start,
2283                                                       to_parent_path->node));
2284        }
2285      else
2286        {
2287          kind = svn_fs_path_change_add;
2288          mergeinfo_start = 0;
2289        }
2290
2291      if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs))
2292        SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end,
2293                                                   from_node));
2294
2295      /* Make sure the target node's parents are mutable.  */
2296      SVN_ERR(make_path_mutable(to_root, to_parent_path->parent,
2297                                to_path, pool));
2298
2299      /* Canonicalize the copyfrom path. */
2300      from_canonpath = svn_fs__canonicalize_abspath(from_path, pool);
2301
2302      SVN_ERR(svn_fs_fs__dag_copy(to_parent_path->parent->node,
2303                                  to_parent_path->entry,
2304                                  from_node,
2305                                  preserve_history,
2306                                  from_root->rev,
2307                                  from_canonpath,
2308                                  txn_id, pool));
2309
2310      if (kind == svn_fs_path_change_replace)
2311        SVN_ERR(dag_node_cache_invalidate(to_root,
2312                                          parent_path_path(to_parent_path,
2313                                                           pool), pool));
2314
2315      if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs)
2316          && mergeinfo_start != mergeinfo_end)
2317        SVN_ERR(increment_mergeinfo_up_tree(to_parent_path->parent,
2318                                            mergeinfo_end - mergeinfo_start,
2319                                            pool));
2320
2321      /* Make a record of this modification in the changes table. */
2322      SVN_ERR(get_dag(&new_node, to_root, to_path, pool));
2323      SVN_ERR(add_change(to_root->fs, txn_id, to_path,
2324                         svn_fs_fs__dag_get_id(new_node), kind, FALSE, FALSE,
2325                         svn_fs_fs__dag_node_kind(from_node),
2326                         from_root->rev, from_canonpath, pool));
2327    }
2328  else
2329    {
2330      /* See IZ Issue #436 */
2331      /* Copying from transaction roots not currently available.
2332
2333         ### cmpilato todo someday: make this not so. :-) Note that
2334         when copying from mutable trees, you have to make sure that
2335         you aren't creating a cyclic graph filesystem, and a simple
2336         referencing operation won't cut it.  Currently, we should not
2337         be able to reach this clause, and the interface reports that
2338         this only works from immutable trees anyway, but JimB has
2339         stated that this requirement need not be necessary in the
2340         future. */
2341
2342      SVN_ERR_MALFUNCTION();
2343    }
2344
2345  return SVN_NO_ERROR;
2346}
2347
2348
2349/* Create a copy of FROM_PATH in FROM_ROOT named TO_PATH in TO_ROOT.
2350   If FROM_PATH is a directory, copy it recursively.  Temporary
2351   allocations are from POOL.*/
2352static svn_error_t *
2353fs_copy(svn_fs_root_t *from_root,
2354        const char *from_path,
2355        svn_fs_root_t *to_root,
2356        const char *to_path,
2357        apr_pool_t *pool)
2358{
2359  SVN_ERR(check_newline(to_path, pool));
2360
2361  return svn_error_trace(copy_helper(from_root,
2362                                     svn_fs__canonicalize_abspath(from_path,
2363                                                                  pool),
2364                                     to_root,
2365                                     svn_fs__canonicalize_abspath(to_path,
2366                                                                  pool),
2367                                     TRUE, pool));
2368}
2369
2370
2371/* Create a copy of FROM_PATH in FROM_ROOT named TO_PATH in TO_ROOT.
2372   If FROM_PATH is a directory, copy it recursively.  No history is
2373   preserved.  Temporary allocations are from POOL. */
2374static svn_error_t *
2375fs_revision_link(svn_fs_root_t *from_root,
2376                 svn_fs_root_t *to_root,
2377                 const char *path,
2378                 apr_pool_t *pool)
2379{
2380  if (! to_root->is_txn_root)
2381    return SVN_FS__NOT_TXN(to_root);
2382
2383  path = svn_fs__canonicalize_abspath(path, pool);
2384  return svn_error_trace(copy_helper(from_root, path, to_root, path,
2385                                     FALSE, pool));
2386}
2387
2388
2389/* Discover the copy ancestry of PATH under ROOT.  Return a relevant
2390   ancestor/revision combination in *PATH_P and *REV_P.  Temporary
2391   allocations are in POOL. */
2392static svn_error_t *
2393fs_copied_from(svn_revnum_t *rev_p,
2394               const char **path_p,
2395               svn_fs_root_t *root,
2396               const char *path,
2397               apr_pool_t *pool)
2398{
2399  dag_node_t *node;
2400  const char *copyfrom_path, *copyfrom_str = NULL;
2401  svn_revnum_t copyfrom_rev;
2402  char *str, *buf;
2403
2404  /* Check to see if there is a cached version of this copyfrom
2405     entry. */
2406  if (! root->is_txn_root) {
2407    fs_rev_root_data_t *frd = root->fsap_data;
2408    copyfrom_str = svn_hash_gets(frd->copyfrom_cache, path);
2409  }
2410
2411  if (copyfrom_str)
2412    {
2413      if (*copyfrom_str == 0)
2414        {
2415          /* We have a cached entry that says there is no copyfrom
2416             here. */
2417          copyfrom_rev = SVN_INVALID_REVNUM;
2418          copyfrom_path = NULL;
2419        }
2420      else
2421        {
2422          /* Parse the copyfrom string for our cached entry. */
2423          buf = apr_pstrdup(pool, copyfrom_str);
2424          str = svn_cstring_tokenize(" ", &buf);
2425          copyfrom_rev = SVN_STR_TO_REV(str);
2426          copyfrom_path = buf;
2427        }
2428    }
2429  else
2430    {
2431      /* There is no cached entry, look it up the old-fashioned
2432         way. */
2433      SVN_ERR(get_dag(&node, root, path, pool));
2434      SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(&copyfrom_rev, node));
2435      SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(&copyfrom_path, node));
2436    }
2437
2438  *rev_p  = copyfrom_rev;
2439  *path_p = copyfrom_path;
2440
2441  return SVN_NO_ERROR;
2442}
2443
2444
2445
2446/* Files.  */
2447
2448/* Create the empty file PATH under ROOT.  Temporary allocations are
2449   in POOL. */
2450static svn_error_t *
2451fs_make_file(svn_fs_root_t *root,
2452             const char *path,
2453             apr_pool_t *pool)
2454{
2455  parent_path_t *parent_path;
2456  dag_node_t *child;
2457  const char *txn_id = root->txn;
2458
2459  SVN_ERR(check_newline(path, pool));
2460
2461  path = svn_fs__canonicalize_abspath(path, pool);
2462  SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
2463                   txn_id, pool));
2464
2465  /* If there's already a file by that name, complain.
2466     This also catches the case of trying to make a file named `/'.  */
2467  if (parent_path->node)
2468    return SVN_FS__ALREADY_EXISTS(root, path);
2469
2470  /* Check (non-recursively) to see if path is locked;  if so, check
2471     that we can use it. */
2472  if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2473    SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, FALSE, FALSE,
2474                                              pool));
2475
2476  /* Create the file.  */
2477  SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
2478  SVN_ERR(svn_fs_fs__dag_make_file(&child,
2479                                   parent_path->parent->node,
2480                                   parent_path_path(parent_path->parent,
2481                                                    pool),
2482                                   parent_path->entry,
2483                                   txn_id,
2484                                   pool));
2485
2486  /* Add this file to the path cache. */
2487  SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool), child,
2488                             pool));
2489
2490  /* Make a record of this modification in the changes table. */
2491  return add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(child),
2492                    svn_fs_path_change_add, TRUE, FALSE, svn_node_file,
2493                    SVN_INVALID_REVNUM, NULL, pool);
2494}
2495
2496
2497/* Set *LENGTH_P to the size of the file PATH under ROOT.  Temporary
2498   allocations are in POOL. */
2499static svn_error_t *
2500fs_file_length(svn_filesize_t *length_p,
2501               svn_fs_root_t *root,
2502               const char *path,
2503               apr_pool_t *pool)
2504{
2505  dag_node_t *file;
2506
2507  /* First create a dag_node_t from the root/path pair. */
2508  SVN_ERR(get_dag(&file, root, path, pool));
2509
2510  /* Now fetch its length */
2511  return svn_fs_fs__dag_file_length(length_p, file, pool);
2512}
2513
2514
2515/* Set *CHECKSUM to the checksum of type KIND for PATH under ROOT, or
2516   NULL if that information isn't available.  Temporary allocations
2517   are from POOL. */
2518static svn_error_t *
2519fs_file_checksum(svn_checksum_t **checksum,
2520                 svn_checksum_kind_t kind,
2521                 svn_fs_root_t *root,
2522                 const char *path,
2523                 apr_pool_t *pool)
2524{
2525  dag_node_t *file;
2526
2527  SVN_ERR(get_dag(&file, root, path, pool));
2528  return svn_fs_fs__dag_file_checksum(checksum, file, kind, pool);
2529}
2530
2531
2532/* --- Machinery for svn_fs_file_contents() ---  */
2533
2534/* Set *CONTENTS to a readable stream that will return the contents of
2535   PATH under ROOT.  The stream is allocated in POOL. */
2536static svn_error_t *
2537fs_file_contents(svn_stream_t **contents,
2538                 svn_fs_root_t *root,
2539                 const char *path,
2540                 apr_pool_t *pool)
2541{
2542  dag_node_t *node;
2543  svn_stream_t *file_stream;
2544
2545  /* First create a dag_node_t from the root/path pair. */
2546  SVN_ERR(get_dag(&node, root, path, pool));
2547
2548  /* Then create a readable stream from the dag_node_t. */
2549  SVN_ERR(svn_fs_fs__dag_get_contents(&file_stream, node, pool));
2550
2551  *contents = file_stream;
2552  return SVN_NO_ERROR;
2553}
2554
2555/* --- End machinery for svn_fs_file_contents() ---  */
2556
2557
2558/* --- Machinery for svn_fs_try_process_file_contents() ---  */
2559
2560static svn_error_t *
2561fs_try_process_file_contents(svn_boolean_t *success,
2562                             svn_fs_root_t *root,
2563                             const char *path,
2564                             svn_fs_process_contents_func_t processor,
2565                             void* baton,
2566                             apr_pool_t *pool)
2567{
2568  dag_node_t *node;
2569  SVN_ERR(get_dag(&node, root, path, pool));
2570
2571  return svn_fs_fs__dag_try_process_file_contents(success, node,
2572                                                  processor, baton, pool);
2573}
2574
2575/* --- End machinery for svn_fs_try_process_file_contents() ---  */
2576
2577
2578/* --- Machinery for svn_fs_apply_textdelta() ---  */
2579
2580
2581/* Local baton type for all the helper functions below. */
2582typedef struct txdelta_baton_t
2583{
2584  /* This is the custom-built window consumer given to us by the delta
2585     library;  it uniquely knows how to read data from our designated
2586     "source" stream, interpret the window, and write data to our
2587     designated "target" stream (in this case, our repos file.) */
2588  svn_txdelta_window_handler_t interpreter;
2589  void *interpreter_baton;
2590
2591  /* The original file info */
2592  svn_fs_root_t *root;
2593  const char *path;
2594
2595  /* Derived from the file info */
2596  dag_node_t *node;
2597
2598  svn_stream_t *source_stream;
2599  svn_stream_t *target_stream;
2600  svn_stream_t *string_stream;
2601  svn_stringbuf_t *target_string;
2602
2603  /* MD5 digest for the base text against which a delta is to be
2604     applied, and for the resultant fulltext, respectively.  Either or
2605     both may be null, in which case ignored. */
2606  svn_checksum_t *base_checksum;
2607  svn_checksum_t *result_checksum;
2608
2609  /* Pool used by db txns */
2610  apr_pool_t *pool;
2611
2612} txdelta_baton_t;
2613
2614
2615/* ### see comment in window_consumer() regarding this function. */
2616
2617/* Helper function of generic type `svn_write_fn_t'.  Implements a
2618   writable stream which appends to an svn_stringbuf_t. */
2619static svn_error_t *
2620write_to_string(void *baton, const char *data, apr_size_t *len)
2621{
2622  txdelta_baton_t *tb = (txdelta_baton_t *) baton;
2623  svn_stringbuf_appendbytes(tb->target_string, data, *len);
2624  return SVN_NO_ERROR;
2625}
2626
2627
2628
2629/* The main window handler returned by svn_fs_apply_textdelta. */
2630static svn_error_t *
2631window_consumer(svn_txdelta_window_t *window, void *baton)
2632{
2633  txdelta_baton_t *tb = (txdelta_baton_t *) baton;
2634
2635  /* Send the window right through to the custom window interpreter.
2636     In theory, the interpreter will then write more data to
2637     cb->target_string. */
2638  SVN_ERR(tb->interpreter(window, tb->interpreter_baton));
2639
2640  /* ### the write_to_string() callback for the txdelta's output stream
2641     ### should be doing all the flush determination logic, not here.
2642     ### in a drastic case, a window could generate a LOT more than the
2643     ### maximum buffer size. we want to flush to the underlying target
2644     ### stream much sooner (e.g. also in a streamy fashion). also, by
2645     ### moving this logic inside the stream, the stream becomes nice
2646     ### and encapsulated: it holds all the logic about buffering and
2647     ### flushing.
2648     ###
2649     ### further: I believe the buffering should be removed from tree.c
2650     ### the buffering should go into the target_stream itself, which
2651     ### is defined by reps-string.c. Specifically, I think the
2652     ### rep_write_contents() function will handle the buffering and
2653     ### the spill to the underlying DB. by locating it there, then
2654     ### anybody who gets a writable stream for FS content can take
2655     ### advantage of the buffering capability. this will be important
2656     ### when we export an FS API function for writing a fulltext into
2657     ### the FS, rather than forcing that fulltext thru apply_textdelta.
2658  */
2659
2660  /* Check to see if we need to purge the portion of the contents that
2661     have been written thus far. */
2662  if ((! window) || (tb->target_string->len > WRITE_BUFFER_SIZE))
2663    {
2664      apr_size_t len = tb->target_string->len;
2665      SVN_ERR(svn_stream_write(tb->target_stream,
2666                               tb->target_string->data,
2667                               &len));
2668      svn_stringbuf_setempty(tb->target_string);
2669    }
2670
2671  /* Is the window NULL?  If so, we're done. */
2672  if (! window)
2673    {
2674      /* Close the internal-use stream.  ### This used to be inside of
2675         txn_body_fulltext_finalize_edits(), but that invoked a nested
2676         Berkeley DB transaction -- scandalous! */
2677      SVN_ERR(svn_stream_close(tb->target_stream));
2678
2679      SVN_ERR(svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum,
2680                                            tb->pool));
2681    }
2682
2683  return SVN_NO_ERROR;
2684}
2685
2686/* Helper function for fs_apply_textdelta.  BATON is of type
2687   txdelta_baton_t. */
2688static svn_error_t *
2689apply_textdelta(void *baton, apr_pool_t *pool)
2690{
2691  txdelta_baton_t *tb = (txdelta_baton_t *) baton;
2692  parent_path_t *parent_path;
2693  const char *txn_id = tb->root->txn;
2694
2695  /* Call open_path with no flags, as we want this to return an error
2696     if the node for which we are searching doesn't exist. */
2697  SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id, pool));
2698
2699  /* Check (non-recursively) to see if path is locked; if so, check
2700     that we can use it. */
2701  if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2702    SVN_ERR(svn_fs_fs__allow_locked_operation(tb->path, tb->root->fs,
2703                                              FALSE, FALSE, pool));
2704
2705  /* Now, make sure this path is mutable. */
2706  SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, pool));
2707  tb->node = parent_path->node;
2708
2709  if (tb->base_checksum)
2710    {
2711      svn_checksum_t *checksum;
2712
2713      /* Until we finalize the node, its data_key points to the old
2714         contents, in other words, the base text. */
2715      SVN_ERR(svn_fs_fs__dag_file_checksum(&checksum, tb->node,
2716                                           tb->base_checksum->kind, pool));
2717      if (!svn_checksum_match(tb->base_checksum, checksum))
2718        return svn_checksum_mismatch_err(tb->base_checksum, checksum, pool,
2719                                         _("Base checksum mismatch on '%s'"),
2720                                         tb->path);
2721    }
2722
2723  /* Make a readable "source" stream out of the current contents of
2724     ROOT/PATH; obviously, this must done in the context of a db_txn.
2725     The stream is returned in tb->source_stream. */
2726  SVN_ERR(svn_fs_fs__dag_get_contents(&(tb->source_stream),
2727                                      tb->node, tb->pool));
2728
2729  /* Make a writable "target" stream */
2730  SVN_ERR(svn_fs_fs__dag_get_edit_stream(&(tb->target_stream), tb->node,
2731                                         tb->pool));
2732
2733  /* Make a writable "string" stream which writes data to
2734     tb->target_string. */
2735  tb->target_string = svn_stringbuf_create_empty(tb->pool);
2736  tb->string_stream = svn_stream_create(tb, tb->pool);
2737  svn_stream_set_write(tb->string_stream, write_to_string);
2738
2739  /* Now, create a custom window handler that uses our two streams. */
2740  svn_txdelta_apply(tb->source_stream,
2741                    tb->string_stream,
2742                    NULL,
2743                    tb->path,
2744                    tb->pool,
2745                    &(tb->interpreter),
2746                    &(tb->interpreter_baton));
2747
2748  /* Make a record of this modification in the changes table. */
2749  return add_change(tb->root->fs, txn_id, tb->path,
2750                    svn_fs_fs__dag_get_id(tb->node),
2751                    svn_fs_path_change_modify, TRUE, FALSE, svn_node_file,
2752                    SVN_INVALID_REVNUM, NULL, pool);
2753}
2754
2755
2756/* Set *CONTENTS_P and *CONTENTS_BATON_P to a window handler and baton
2757   that will accept text delta windows to modify the contents of PATH
2758   under ROOT.  Allocations are in POOL. */
2759static svn_error_t *
2760fs_apply_textdelta(svn_txdelta_window_handler_t *contents_p,
2761                   void **contents_baton_p,
2762                   svn_fs_root_t *root,
2763                   const char *path,
2764                   svn_checksum_t *base_checksum,
2765                   svn_checksum_t *result_checksum,
2766                   apr_pool_t *pool)
2767{
2768  txdelta_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
2769
2770  tb->root = root;
2771  tb->path = svn_fs__canonicalize_abspath(path, pool);
2772  tb->pool = pool;
2773  tb->base_checksum = svn_checksum_dup(base_checksum, pool);
2774  tb->result_checksum = svn_checksum_dup(result_checksum, pool);
2775
2776  SVN_ERR(apply_textdelta(tb, pool));
2777
2778  *contents_p = window_consumer;
2779  *contents_baton_p = tb;
2780  return SVN_NO_ERROR;
2781}
2782
2783/* --- End machinery for svn_fs_apply_textdelta() ---  */
2784
2785/* --- Machinery for svn_fs_apply_text() ---  */
2786
2787/* Baton for svn_fs_apply_text(). */
2788struct text_baton_t
2789{
2790  /* The original file info */
2791  svn_fs_root_t *root;
2792  const char *path;
2793
2794  /* Derived from the file info */
2795  dag_node_t *node;
2796
2797  /* The returned stream that will accept the file's new contents. */
2798  svn_stream_t *stream;
2799
2800  /* The actual fs stream that the returned stream will write to. */
2801  svn_stream_t *file_stream;
2802
2803  /* MD5 digest for the final fulltext written to the file.  May
2804     be null, in which case ignored. */
2805  svn_checksum_t *result_checksum;
2806
2807  /* Pool used by db txns */
2808  apr_pool_t *pool;
2809};
2810
2811
2812/* A wrapper around svn_fs_fs__dag_finalize_edits, but for
2813 * fulltext data, not text deltas.  Closes BATON->file_stream.
2814 *
2815 * Note: If you're confused about how this function relates to another
2816 * of similar name, think of it this way:
2817 *
2818 * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits()
2819 * svn_fs_apply_text()      ==> ... ==> txn_body_fulltext_finalize_edits()
2820 */
2821
2822/* Write function for the publically returned stream. */
2823static svn_error_t *
2824text_stream_writer(void *baton,
2825                   const char *data,
2826                   apr_size_t *len)
2827{
2828  struct text_baton_t *tb = baton;
2829
2830  /* Psst, here's some data.  Pass it on to the -real- file stream. */
2831  return svn_stream_write(tb->file_stream, data, len);
2832}
2833
2834/* Close function for the publically returned stream. */
2835static svn_error_t *
2836text_stream_closer(void *baton)
2837{
2838  struct text_baton_t *tb = baton;
2839
2840  /* Close the internal-use stream.  ### This used to be inside of
2841     txn_body_fulltext_finalize_edits(), but that invoked a nested
2842     Berkeley DB transaction -- scandalous! */
2843  SVN_ERR(svn_stream_close(tb->file_stream));
2844
2845  /* Need to tell fs that we're done sending text */
2846  return svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum,
2847                                       tb->pool);
2848}
2849
2850
2851/* Helper function for fs_apply_text.  BATON is of type
2852   text_baton_t. */
2853static svn_error_t *
2854apply_text(void *baton, apr_pool_t *pool)
2855{
2856  struct text_baton_t *tb = baton;
2857  parent_path_t *parent_path;
2858  const char *txn_id = tb->root->txn;
2859
2860  /* Call open_path with no flags, as we want this to return an error
2861     if the node for which we are searching doesn't exist. */
2862  SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id, pool));
2863
2864  /* Check (non-recursively) to see if path is locked; if so, check
2865     that we can use it. */
2866  if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2867    SVN_ERR(svn_fs_fs__allow_locked_operation(tb->path, tb->root->fs,
2868                                              FALSE, FALSE, pool));
2869
2870  /* Now, make sure this path is mutable. */
2871  SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, pool));
2872  tb->node = parent_path->node;
2873
2874  /* Make a writable stream for replacing the file's text. */
2875  SVN_ERR(svn_fs_fs__dag_get_edit_stream(&(tb->file_stream), tb->node,
2876                                         tb->pool));
2877
2878  /* Create a 'returnable' stream which writes to the file_stream. */
2879  tb->stream = svn_stream_create(tb, tb->pool);
2880  svn_stream_set_write(tb->stream, text_stream_writer);
2881  svn_stream_set_close(tb->stream, text_stream_closer);
2882
2883  /* Make a record of this modification in the changes table. */
2884  return add_change(tb->root->fs, txn_id, tb->path,
2885                    svn_fs_fs__dag_get_id(tb->node),
2886                    svn_fs_path_change_modify, TRUE, FALSE, svn_node_file,
2887                    SVN_INVALID_REVNUM, NULL, pool);
2888}
2889
2890
2891/* Return a writable stream that will set the contents of PATH under
2892   ROOT.  RESULT_CHECKSUM is the MD5 checksum of the final result.
2893   Temporary allocations are in POOL. */
2894static svn_error_t *
2895fs_apply_text(svn_stream_t **contents_p,
2896              svn_fs_root_t *root,
2897              const char *path,
2898              svn_checksum_t *result_checksum,
2899              apr_pool_t *pool)
2900{
2901  struct text_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
2902
2903  tb->root = root;
2904  tb->path = svn_fs__canonicalize_abspath(path, pool);
2905  tb->pool = pool;
2906  tb->result_checksum = svn_checksum_dup(result_checksum, pool);
2907
2908  SVN_ERR(apply_text(tb, pool));
2909
2910  *contents_p = tb->stream;
2911  return SVN_NO_ERROR;
2912}
2913
2914/* --- End machinery for svn_fs_apply_text() ---  */
2915
2916
2917/* Check if the contents of PATH1 under ROOT1 are different from the
2918   contents of PATH2 under ROOT2.  If they are different set
2919   *CHANGED_P to TRUE, otherwise set it to FALSE. */
2920static svn_error_t *
2921fs_contents_changed(svn_boolean_t *changed_p,
2922                    svn_fs_root_t *root1,
2923                    const char *path1,
2924                    svn_fs_root_t *root2,
2925                    const char *path2,
2926                    apr_pool_t *pool)
2927{
2928  dag_node_t *node1, *node2;
2929
2930  /* Check that roots are in the same fs. */
2931  if (root1->fs != root2->fs)
2932    return svn_error_create
2933      (SVN_ERR_FS_GENERAL, NULL,
2934       _("Cannot compare file contents between two different filesystems"));
2935
2936  /* Check that both paths are files. */
2937  {
2938    svn_node_kind_t kind;
2939
2940    SVN_ERR(svn_fs_fs__check_path(&kind, root1, path1, pool));
2941    if (kind != svn_node_file)
2942      return svn_error_createf
2943        (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path1);
2944
2945    SVN_ERR(svn_fs_fs__check_path(&kind, root2, path2, pool));
2946    if (kind != svn_node_file)
2947      return svn_error_createf
2948        (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path2);
2949  }
2950
2951  SVN_ERR(get_dag(&node1, root1, path1, pool));
2952  SVN_ERR(get_dag(&node2, root2, path2, pool));
2953  return svn_fs_fs__dag_things_different(NULL, changed_p,
2954                                         node1, node2);
2955}
2956
2957
2958
2959/* Public interface to computing file text deltas.  */
2960
2961static svn_error_t *
2962fs_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
2963                         svn_fs_root_t *source_root,
2964                         const char *source_path,
2965                         svn_fs_root_t *target_root,
2966                         const char *target_path,
2967                         apr_pool_t *pool)
2968{
2969  dag_node_t *source_node, *target_node;
2970
2971  if (source_root && source_path)
2972    SVN_ERR(get_dag(&source_node, source_root, source_path, pool));
2973  else
2974    source_node = NULL;
2975  SVN_ERR(get_dag(&target_node, target_root, target_path, pool));
2976
2977  /* Create a delta stream that turns the source into the target.  */
2978  return svn_fs_fs__dag_get_file_delta_stream(stream_p, source_node,
2979                                              target_node, pool);
2980}
2981
2982
2983
2984/* Finding Changes */
2985
2986/* Set *CHANGED_PATHS_P to a newly allocated hash containing
2987   descriptions of the paths changed under ROOT.  The hash is keyed
2988   with const char * paths and has svn_fs_path_change2_t * values.  Use
2989   POOL for all allocations. */
2990static svn_error_t *
2991fs_paths_changed(apr_hash_t **changed_paths_p,
2992                 svn_fs_root_t *root,
2993                 apr_pool_t *pool)
2994{
2995  if (root->is_txn_root)
2996    return svn_fs_fs__txn_changes_fetch(changed_paths_p, root->fs, root->txn,
2997                                        pool);
2998  else
2999    {
3000      fs_rev_root_data_t *frd = root->fsap_data;
3001      return svn_fs_fs__paths_changed(changed_paths_p, root->fs, root->rev,
3002                                      frd->copyfrom_cache, pool);
3003    }
3004}
3005
3006
3007
3008/* Our coolio opaque history object. */
3009typedef struct fs_history_data_t
3010{
3011  /* filesystem object */
3012  svn_fs_t *fs;
3013
3014  /* path and revision of historical location */
3015  const char *path;
3016  svn_revnum_t revision;
3017
3018  /* internal-use hints about where to resume the history search. */
3019  const char *path_hint;
3020  svn_revnum_t rev_hint;
3021
3022  /* FALSE until the first call to svn_fs_history_prev(). */
3023  svn_boolean_t is_interesting;
3024} fs_history_data_t;
3025
3026static svn_fs_history_t *
3027assemble_history(svn_fs_t *fs,
3028                 const char *path,
3029                 svn_revnum_t revision,
3030                 svn_boolean_t is_interesting,
3031                 const char *path_hint,
3032                 svn_revnum_t rev_hint,
3033                 apr_pool_t *pool);
3034
3035
3036/* Set *HISTORY_P to an opaque node history object which represents
3037   PATH under ROOT.  ROOT must be a revision root.  Use POOL for all
3038   allocations. */
3039static svn_error_t *
3040fs_node_history(svn_fs_history_t **history_p,
3041                svn_fs_root_t *root,
3042                const char *path,
3043                apr_pool_t *pool)
3044{
3045  svn_node_kind_t kind;
3046
3047  /* We require a revision root. */
3048  if (root->is_txn_root)
3049    return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
3050
3051  /* And we require that the path exist in the root. */
3052  SVN_ERR(svn_fs_fs__check_path(&kind, root, path, pool));
3053  if (kind == svn_node_none)
3054    return SVN_FS__NOT_FOUND(root, path);
3055
3056  /* Okay, all seems well.  Build our history object and return it. */
3057  *history_p = assemble_history(root->fs,
3058                                svn_fs__canonicalize_abspath(path, pool),
3059                                root->rev, FALSE, NULL,
3060                                SVN_INVALID_REVNUM, pool);
3061  return SVN_NO_ERROR;
3062}
3063
3064/* Find the youngest copyroot for path PARENT_PATH or its parents in
3065   filesystem FS, and store the copyroot in *REV_P and *PATH_P.
3066   Perform all allocations in POOL. */
3067static svn_error_t *
3068find_youngest_copyroot(svn_revnum_t *rev_p,
3069                       const char **path_p,
3070                       svn_fs_t *fs,
3071                       parent_path_t *parent_path,
3072                       apr_pool_t *pool)
3073{
3074  svn_revnum_t rev_mine;
3075  svn_revnum_t rev_parent = SVN_INVALID_REVNUM;
3076  const char *path_mine;
3077  const char *path_parent = NULL;
3078
3079  /* First find our parent's youngest copyroot. */
3080  if (parent_path->parent)
3081    SVN_ERR(find_youngest_copyroot(&rev_parent, &path_parent, fs,
3082                                   parent_path->parent, pool));
3083
3084  /* Find our copyroot. */
3085  SVN_ERR(svn_fs_fs__dag_get_copyroot(&rev_mine, &path_mine,
3086                                      parent_path->node));
3087
3088  /* If a parent and child were copied to in the same revision, prefer
3089     the child copy target, since it is the copy relevant to the
3090     history of the child. */
3091  if (rev_mine >= rev_parent)
3092    {
3093      *rev_p = rev_mine;
3094      *path_p = path_mine;
3095    }
3096  else
3097    {
3098      *rev_p = rev_parent;
3099      *path_p = path_parent;
3100    }
3101
3102  return SVN_NO_ERROR;
3103}
3104
3105
3106static svn_error_t *fs_closest_copy(svn_fs_root_t **root_p,
3107                                    const char **path_p,
3108                                    svn_fs_root_t *root,
3109                                    const char *path,
3110                                    apr_pool_t *pool)
3111{
3112  svn_fs_t *fs = root->fs;
3113  parent_path_t *parent_path, *copy_dst_parent_path;
3114  svn_revnum_t copy_dst_rev, created_rev;
3115  const char *copy_dst_path;
3116  svn_fs_root_t *copy_dst_root;
3117  dag_node_t *copy_dst_node;
3118  svn_node_kind_t kind;
3119
3120  /* Initialize return values. */
3121  *root_p = NULL;
3122  *path_p = NULL;
3123
3124  path = svn_fs__canonicalize_abspath(path, pool);
3125  SVN_ERR(open_path(&parent_path, root, path, 0, NULL, pool));
3126
3127  /* Find the youngest copyroot in the path of this node-rev, which
3128     will indicate the target of the innermost copy affecting the
3129     node-rev. */
3130  SVN_ERR(find_youngest_copyroot(&copy_dst_rev, &copy_dst_path,
3131                                 fs, parent_path, pool));
3132  if (copy_dst_rev == 0)  /* There are no copies affecting this node-rev. */
3133    return SVN_NO_ERROR;
3134
3135  /* It is possible that this node was created from scratch at some
3136     revision between COPY_DST_REV and REV.  Make sure that PATH
3137     exists as of COPY_DST_REV and is related to this node-rev. */
3138  SVN_ERR(svn_fs_fs__revision_root(&copy_dst_root, fs, copy_dst_rev, pool));
3139  SVN_ERR(svn_fs_fs__check_path(&kind, copy_dst_root, path, pool));
3140  if (kind == svn_node_none)
3141    return SVN_NO_ERROR;
3142  SVN_ERR(open_path(&copy_dst_parent_path, copy_dst_root, path,
3143                    open_path_node_only, NULL, pool));
3144  copy_dst_node = copy_dst_parent_path->node;
3145  if (! svn_fs_fs__id_check_related(svn_fs_fs__dag_get_id(copy_dst_node),
3146                                    svn_fs_fs__dag_get_id(parent_path->node)))
3147    return SVN_NO_ERROR;
3148
3149  /* One final check must be done here.  If you copy a directory and
3150     create a new entity somewhere beneath that directory in the same
3151     txn, then we can't claim that the copy affected the new entity.
3152     For example, if you do:
3153
3154        copy dir1 dir2
3155        create dir2/new-thing
3156        commit
3157
3158     then dir2/new-thing was not affected by the copy of dir1 to dir2.
3159     We detect this situation by asking if PATH@COPY_DST_REV's
3160     created-rev is COPY_DST_REV, and that node-revision has no
3161     predecessors, then there is no relevant closest copy.
3162  */
3163  SVN_ERR(svn_fs_fs__dag_get_revision(&created_rev, copy_dst_node, pool));
3164  if (created_rev == copy_dst_rev)
3165    {
3166      const svn_fs_id_t *pred;
3167      SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred, copy_dst_node));
3168      if (! pred)
3169        return SVN_NO_ERROR;
3170    }
3171
3172  /* The copy destination checks out.  Return it. */
3173  *root_p = copy_dst_root;
3174  *path_p = copy_dst_path;
3175  return SVN_NO_ERROR;
3176}
3177
3178
3179/* Set *PREV_PATH and *PREV_REV to the path and revision which
3180   represent the location at which PATH in FS was located immediately
3181   prior to REVISION iff there was a copy operation (to PATH or one of
3182   its parent directories) between that previous location and
3183   PATH@REVISION.
3184
3185   If there was no such copy operation in that portion of PATH's
3186   history, set *PREV_PATH to NULL and *PREV_REV to SVN_INVALID_REVNUM.  */
3187static svn_error_t *
3188prev_location(const char **prev_path,
3189              svn_revnum_t *prev_rev,
3190              svn_fs_t *fs,
3191              svn_fs_root_t *root,
3192              const char *path,
3193              apr_pool_t *pool)
3194{
3195  const char *copy_path, *copy_src_path, *remainder_path;
3196  svn_fs_root_t *copy_root;
3197  svn_revnum_t copy_src_rev;
3198
3199  /* Ask about the most recent copy which affected PATH@REVISION.  If
3200     there was no such copy, we're done.  */
3201  SVN_ERR(fs_closest_copy(&copy_root, &copy_path, root, path, pool));
3202  if (! copy_root)
3203    {
3204      *prev_rev = SVN_INVALID_REVNUM;
3205      *prev_path = NULL;
3206      return SVN_NO_ERROR;
3207    }
3208
3209  /* Ultimately, it's not the path of the closest copy's source that
3210     we care about -- it's our own path's location in the copy source
3211     revision.  So we'll tack the relative path that expresses the
3212     difference between the copy destination and our path in the copy
3213     revision onto the copy source path to determine this information.
3214
3215     In other words, if our path is "/branches/my-branch/foo/bar", and
3216     we know that the closest relevant copy was a copy of "/trunk" to
3217     "/branches/my-branch", then that relative path under the copy
3218     destination is "/foo/bar".  Tacking that onto the copy source
3219     path tells us that our path was located at "/trunk/foo/bar"
3220     before the copy.
3221  */
3222  SVN_ERR(fs_copied_from(&copy_src_rev, &copy_src_path,
3223                         copy_root, copy_path, pool));
3224  remainder_path = svn_fspath__skip_ancestor(copy_path, path);
3225  *prev_path = svn_fspath__join(copy_src_path, remainder_path, pool);
3226  *prev_rev = copy_src_rev;
3227  return SVN_NO_ERROR;
3228}
3229
3230
3231static svn_error_t *
3232fs_node_origin_rev(svn_revnum_t *revision,
3233                   svn_fs_root_t *root,
3234                   const char *path,
3235                   apr_pool_t *pool)
3236{
3237  svn_fs_t *fs = root->fs;
3238  const svn_fs_id_t *given_noderev_id, *cached_origin_id;
3239  const char *node_id, *dash;
3240
3241  path = svn_fs__canonicalize_abspath(path, pool);
3242
3243  /* Check the cache first. */
3244  SVN_ERR(svn_fs_fs__node_id(&given_noderev_id, root, path, pool));
3245  node_id = svn_fs_fs__id_node_id(given_noderev_id);
3246
3247  /* Is it a brand new uncommitted node? */
3248  if (node_id[0] == '_')
3249    {
3250      *revision = SVN_INVALID_REVNUM;
3251      return SVN_NO_ERROR;
3252    }
3253
3254  /* Maybe this is a new-style node ID that just has the revision
3255     sitting right in it. */
3256  dash = strchr(node_id, '-');
3257  if (dash && *(dash+1))
3258    {
3259      *revision = SVN_STR_TO_REV(dash + 1);
3260      return SVN_NO_ERROR;
3261    }
3262
3263  /* The root node always has ID 0, created in revision 0 and will never
3264     use the new-style ID format. */
3265  if (strcmp(node_id, "0") == 0)
3266    {
3267      *revision = 0;
3268      return SVN_NO_ERROR;
3269    }
3270
3271  /* OK, it's an old-style ID?  Maybe it's cached. */
3272  SVN_ERR(svn_fs_fs__get_node_origin(&cached_origin_id,
3273                                     fs,
3274                                     node_id,
3275                                     pool));
3276  if (cached_origin_id != NULL)
3277    {
3278      *revision = svn_fs_fs__id_rev(cached_origin_id);
3279      return SVN_NO_ERROR;
3280    }
3281
3282  {
3283    /* Ah well, the answer isn't in the ID itself or in the cache.
3284       Let's actually calculate it, then. */
3285    svn_fs_root_t *curroot = root;
3286    apr_pool_t *subpool = svn_pool_create(pool);
3287    apr_pool_t *predidpool = svn_pool_create(pool);
3288    svn_stringbuf_t *lastpath = svn_stringbuf_create(path, pool);
3289    svn_revnum_t lastrev = SVN_INVALID_REVNUM;
3290    dag_node_t *node;
3291    const svn_fs_id_t *pred_id;
3292
3293    /* Walk the closest-copy chain back to the first copy in our history.
3294
3295       NOTE: We merely *assume* that this is faster than walking the
3296       predecessor chain, because we *assume* that copies of parent
3297       directories happen less often than modifications to a given item. */
3298    while (1)
3299      {
3300        svn_revnum_t currev;
3301        const char *curpath = lastpath->data;
3302
3303        svn_pool_clear(subpool);
3304
3305        /* Get a root pointing to LASTREV.  (The first time around,
3306           LASTREV is invalid, but that's cool because CURROOT is
3307           already initialized.)  */
3308        if (SVN_IS_VALID_REVNUM(lastrev))
3309          SVN_ERR(svn_fs_fs__revision_root(&curroot, fs, lastrev, subpool));
3310
3311        /* Find the previous location using the closest-copy shortcut. */
3312        SVN_ERR(prev_location(&curpath, &currev, fs, curroot, curpath,
3313                              subpool));
3314        if (! curpath)
3315          break;
3316
3317        /* Update our LASTPATH and LASTREV variables (which survive
3318           SUBPOOL). */
3319        svn_stringbuf_set(lastpath, curpath);
3320        lastrev = currev;
3321      }
3322
3323    /* Walk the predecessor links back to origin. */
3324    SVN_ERR(svn_fs_fs__node_id(&pred_id, curroot, lastpath->data, predidpool));
3325    do
3326      {
3327        svn_pool_clear(subpool);
3328        SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, subpool));
3329
3330        /* Why not just fetch the predecessor ID in PREDIDPOOL?
3331           Because svn_fs_fs__dag_get_predecessor_id() doesn't
3332           necessarily honor the passed-in pool, and might return a
3333           value cached in the node (which is allocated in
3334           SUBPOOL... maybe). */
3335        svn_pool_clear(predidpool);
3336        SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
3337        pred_id = pred_id ? svn_fs_fs__id_copy(pred_id, predidpool) : NULL;
3338      }
3339    while (pred_id);
3340
3341    /* When we get here, NODE should be the first node-revision in our
3342       chain. */
3343    SVN_ERR(svn_fs_fs__dag_get_revision(revision, node, pool));
3344
3345    /* Wow, I don't want to have to do all that again.  Let's cache
3346       the result. */
3347    if (node_id[0] != '_')
3348      SVN_ERR(svn_fs_fs__set_node_origin(fs, node_id,
3349                                         svn_fs_fs__dag_get_id(node), pool));
3350
3351    svn_pool_destroy(subpool);
3352    svn_pool_destroy(predidpool);
3353    return SVN_NO_ERROR;
3354  }
3355}
3356
3357
3358struct history_prev_args
3359{
3360  svn_fs_history_t **prev_history_p;
3361  svn_fs_history_t *history;
3362  svn_boolean_t cross_copies;
3363  apr_pool_t *pool;
3364};
3365
3366
3367static svn_error_t *
3368history_prev(void *baton, apr_pool_t *pool)
3369{
3370  struct history_prev_args *args = baton;
3371  svn_fs_history_t **prev_history = args->prev_history_p;
3372  svn_fs_history_t *history = args->history;
3373  fs_history_data_t *fhd = history->fsap_data;
3374  const char *commit_path, *src_path, *path = fhd->path;
3375  svn_revnum_t commit_rev, src_rev, dst_rev;
3376  svn_revnum_t revision = fhd->revision;
3377  apr_pool_t *retpool = args->pool;
3378  svn_fs_t *fs = fhd->fs;
3379  parent_path_t *parent_path;
3380  dag_node_t *node;
3381  svn_fs_root_t *root;
3382  svn_boolean_t reported = fhd->is_interesting;
3383  svn_revnum_t copyroot_rev;
3384  const char *copyroot_path;
3385
3386  /* Initialize our return value. */
3387  *prev_history = NULL;
3388
3389  /* If our last history report left us hints about where to pickup
3390     the chase, then our last report was on the destination of a
3391     copy.  If we are crossing copies, start from those locations,
3392     otherwise, we're all done here.  */
3393  if (fhd->path_hint && SVN_IS_VALID_REVNUM(fhd->rev_hint))
3394    {
3395      reported = FALSE;
3396      if (! args->cross_copies)
3397        return SVN_NO_ERROR;
3398      path = fhd->path_hint;
3399      revision = fhd->rev_hint;
3400    }
3401
3402  /* Construct a ROOT for the current revision. */
3403  SVN_ERR(svn_fs_fs__revision_root(&root, fs, revision, pool));
3404
3405  /* Open PATH/REVISION, and get its node and a bunch of other
3406     goodies.  */
3407  SVN_ERR(open_path(&parent_path, root, path, 0, NULL, pool));
3408  node = parent_path->node;
3409  commit_path = svn_fs_fs__dag_get_created_path(node);
3410  SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, pool));
3411
3412  /* The Subversion filesystem is written in such a way that a given
3413     line of history may have at most one interesting history point
3414     per filesystem revision.  Either that node was edited (and
3415     possibly copied), or it was copied but not edited.  And a copy
3416     source cannot be from the same revision as its destination.  So,
3417     if our history revision matches its node's commit revision, we
3418     know that ... */
3419  if (revision == commit_rev)
3420    {
3421      if (! reported)
3422        {
3423          /* ... we either have not yet reported on this revision (and
3424             need now to do so) ... */
3425          *prev_history = assemble_history(fs,
3426                                           apr_pstrdup(retpool, commit_path),
3427                                           commit_rev, TRUE, NULL,
3428                                           SVN_INVALID_REVNUM, retpool);
3429          return SVN_NO_ERROR;
3430        }
3431      else
3432        {
3433          /* ... or we *have* reported on this revision, and must now
3434             progress toward this node's predecessor (unless there is
3435             no predecessor, in which case we're all done!). */
3436          const svn_fs_id_t *pred_id;
3437
3438          SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
3439          if (! pred_id)
3440            return SVN_NO_ERROR;
3441
3442          /* Replace NODE and friends with the information from its
3443             predecessor. */
3444          SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, pool));
3445          commit_path = svn_fs_fs__dag_get_created_path(node);
3446          SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, pool));
3447        }
3448    }
3449
3450  /* Find the youngest copyroot in the path of this node, including
3451     itself. */
3452  SVN_ERR(find_youngest_copyroot(&copyroot_rev, &copyroot_path, fs,
3453                                 parent_path, pool));
3454
3455  /* Initialize some state variables. */
3456  src_path = NULL;
3457  src_rev = SVN_INVALID_REVNUM;
3458  dst_rev = SVN_INVALID_REVNUM;
3459
3460  if (copyroot_rev > commit_rev)
3461    {
3462      const char *remainder_path;
3463      const char *copy_dst, *copy_src;
3464      svn_fs_root_t *copyroot_root;
3465
3466      SVN_ERR(svn_fs_fs__revision_root(&copyroot_root, fs, copyroot_rev,
3467                                       pool));
3468      SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, pool));
3469      copy_dst = svn_fs_fs__dag_get_created_path(node);
3470
3471      /* If our current path was the very destination of the copy,
3472         then our new current path will be the copy source.  If our
3473         current path was instead the *child* of the destination of
3474         the copy, then figure out its previous location by taking its
3475         path relative to the copy destination and appending that to
3476         the copy source.  Finally, if our current path doesn't meet
3477         one of these other criteria ... ### for now just fallback to
3478         the old copy hunt algorithm. */
3479      remainder_path = svn_fspath__skip_ancestor(copy_dst, path);
3480
3481      if (remainder_path)
3482        {
3483          /* If we get here, then our current path is the destination
3484             of, or the child of the destination of, a copy.  Fill
3485             in the return values and get outta here.  */
3486          SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(&src_rev, node));
3487          SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(&copy_src, node));
3488
3489          dst_rev = copyroot_rev;
3490          src_path = svn_fspath__join(copy_src, remainder_path, pool);
3491        }
3492    }
3493
3494  /* If we calculated a copy source path and revision, we'll make a
3495     'copy-style' history object. */
3496  if (src_path && SVN_IS_VALID_REVNUM(src_rev))
3497    {
3498      svn_boolean_t retry = FALSE;
3499
3500      /* It's possible for us to find a copy location that is the same
3501         as the history point we've just reported.  If that happens,
3502         we simply need to take another trip through this history
3503         search. */
3504      if ((dst_rev == revision) && reported)
3505        retry = TRUE;
3506
3507      *prev_history = assemble_history(fs, apr_pstrdup(retpool, path),
3508                                       dst_rev, ! retry,
3509                                       src_path, src_rev, retpool);
3510    }
3511  else
3512    {
3513      *prev_history = assemble_history(fs, apr_pstrdup(retpool, commit_path),
3514                                       commit_rev, TRUE, NULL,
3515                                       SVN_INVALID_REVNUM, retpool);
3516    }
3517
3518  return SVN_NO_ERROR;
3519}
3520
3521
3522/* Implement svn_fs_history_prev, set *PREV_HISTORY_P to a new
3523   svn_fs_history_t object that represents the predecessory of
3524   HISTORY.  If CROSS_COPIES is true, *PREV_HISTORY_P may be related
3525   only through a copy operation.  Perform all allocations in POOL. */
3526static svn_error_t *
3527fs_history_prev(svn_fs_history_t **prev_history_p,
3528                svn_fs_history_t *history,
3529                svn_boolean_t cross_copies,
3530                apr_pool_t *pool)
3531{
3532  svn_fs_history_t *prev_history = NULL;
3533  fs_history_data_t *fhd = history->fsap_data;
3534  svn_fs_t *fs = fhd->fs;
3535
3536  /* Special case: the root directory changes in every single
3537     revision, no exceptions.  And, the root can't be the target (or
3538     child of a target -- duh) of a copy.  So, if that's our path,
3539     then we need only decrement our revision by 1, and there you go. */
3540  if (strcmp(fhd->path, "/") == 0)
3541    {
3542      if (! fhd->is_interesting)
3543        prev_history = assemble_history(fs, "/", fhd->revision,
3544                                        1, NULL, SVN_INVALID_REVNUM, pool);
3545      else if (fhd->revision > 0)
3546        prev_history = assemble_history(fs, "/", fhd->revision - 1,
3547                                        1, NULL, SVN_INVALID_REVNUM, pool);
3548    }
3549  else
3550    {
3551      struct history_prev_args args;
3552      prev_history = history;
3553
3554      while (1)
3555        {
3556          args.prev_history_p = &prev_history;
3557          args.history = prev_history;
3558          args.cross_copies = cross_copies;
3559          args.pool = pool;
3560          SVN_ERR(history_prev(&args, pool));
3561
3562          if (! prev_history)
3563            break;
3564          fhd = prev_history->fsap_data;
3565          if (fhd->is_interesting)
3566            break;
3567        }
3568    }
3569
3570  *prev_history_p = prev_history;
3571  return SVN_NO_ERROR;
3572}
3573
3574
3575/* Set *PATH and *REVISION to the path and revision for the HISTORY
3576   object.  Use POOL for all allocations. */
3577static svn_error_t *
3578fs_history_location(const char **path,
3579                    svn_revnum_t *revision,
3580                    svn_fs_history_t *history,
3581                    apr_pool_t *pool)
3582{
3583  fs_history_data_t *fhd = history->fsap_data;
3584
3585  *path = apr_pstrdup(pool, fhd->path);
3586  *revision = fhd->revision;
3587  return SVN_NO_ERROR;
3588}
3589
3590static history_vtable_t history_vtable = {
3591  fs_history_prev,
3592  fs_history_location
3593};
3594
3595/* Return a new history object (marked as "interesting") for PATH and
3596   REVISION, allocated in POOL, and with its members set to the values
3597   of the parameters provided.  Note that PATH and PATH_HINT are not
3598   duped into POOL -- it is the responsibility of the caller to ensure
3599   that this happens. */
3600static svn_fs_history_t *
3601assemble_history(svn_fs_t *fs,
3602                 const char *path,
3603                 svn_revnum_t revision,
3604                 svn_boolean_t is_interesting,
3605                 const char *path_hint,
3606                 svn_revnum_t rev_hint,
3607                 apr_pool_t *pool)
3608{
3609  svn_fs_history_t *history = apr_pcalloc(pool, sizeof(*history));
3610  fs_history_data_t *fhd = apr_pcalloc(pool, sizeof(*fhd));
3611  fhd->path = svn_fs__canonicalize_abspath(path, pool);
3612  fhd->revision = revision;
3613  fhd->is_interesting = is_interesting;
3614  fhd->path_hint = path_hint;
3615  fhd->rev_hint = rev_hint;
3616  fhd->fs = fs;
3617
3618  history->vtable = &history_vtable;
3619  history->fsap_data = fhd;
3620  return history;
3621}
3622
3623
3624/* mergeinfo queries */
3625
3626
3627/* DIR_DAG is a directory DAG node which has mergeinfo in its
3628   descendants.  This function iterates over its children.  For each
3629   child with immediate mergeinfo, it adds its mergeinfo to
3630   RESULT_CATALOG.  appropriate arguments.  For each child with
3631   descendants with mergeinfo, it recurses.  Note that it does *not*
3632   call the action on the path for DIR_DAG itself.
3633
3634   POOL is used for temporary allocations, including the mergeinfo
3635   hashes passed to actions; RESULT_POOL is used for the mergeinfo added
3636   to RESULT_CATALOG.
3637 */
3638static svn_error_t *
3639crawl_directory_dag_for_mergeinfo(svn_fs_root_t *root,
3640                                  const char *this_path,
3641                                  dag_node_t *dir_dag,
3642                                  svn_mergeinfo_catalog_t result_catalog,
3643                                  apr_pool_t *result_pool,
3644                                  apr_pool_t *scratch_pool)
3645{
3646  apr_hash_t *entries;
3647  apr_hash_index_t *hi;
3648  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3649
3650  SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, dir_dag,
3651                                     scratch_pool));
3652
3653  for (hi = apr_hash_first(scratch_pool, entries);
3654       hi;
3655       hi = apr_hash_next(hi))
3656    {
3657      svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
3658      const char *kid_path;
3659      dag_node_t *kid_dag;
3660      svn_boolean_t has_mergeinfo, go_down;
3661
3662      svn_pool_clear(iterpool);
3663
3664      kid_path = svn_fspath__join(this_path, dirent->name, iterpool);
3665      SVN_ERR(get_dag(&kid_dag, root, kid_path, iterpool));
3666
3667      SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, kid_dag));
3668      SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down, kid_dag));
3669
3670      if (has_mergeinfo)
3671        {
3672          /* Save this particular node's mergeinfo. */
3673          apr_hash_t *proplist;
3674          svn_mergeinfo_t kid_mergeinfo;
3675          svn_string_t *mergeinfo_string;
3676          svn_error_t *err;
3677
3678          SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, kid_dag, iterpool));
3679          mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
3680          if (!mergeinfo_string)
3681            {
3682              svn_string_t *idstr = svn_fs_fs__id_unparse(dirent->id, iterpool);
3683              return svn_error_createf
3684                (SVN_ERR_FS_CORRUPT, NULL,
3685                 _("Node-revision #'%s' claims to have mergeinfo but doesn't"),
3686                 idstr->data);
3687            }
3688
3689          /* Issue #3896: If a node has syntactically invalid mergeinfo, then
3690             treat it as if no mergeinfo is present rather than raising a parse
3691             error. */
3692          err = svn_mergeinfo_parse(&kid_mergeinfo,
3693                                    mergeinfo_string->data,
3694                                    result_pool);
3695          if (err)
3696            {
3697              if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
3698                svn_error_clear(err);
3699              else
3700                return svn_error_trace(err);
3701              }
3702          else
3703            {
3704              svn_hash_sets(result_catalog, apr_pstrdup(result_pool, kid_path),
3705                            kid_mergeinfo);
3706            }
3707        }
3708
3709      if (go_down)
3710        SVN_ERR(crawl_directory_dag_for_mergeinfo(root,
3711                                                  kid_path,
3712                                                  kid_dag,
3713                                                  result_catalog,
3714                                                  result_pool,
3715                                                  iterpool));
3716    }
3717
3718  svn_pool_destroy(iterpool);
3719  return SVN_NO_ERROR;
3720}
3721
3722/* Return the cache key as a combination of REV_ROOT->REV, the inheritance
3723   flags INHERIT and ADJUST_INHERITED_MERGEINFO, and the PATH.  The result
3724   will be allocated in POOL..
3725 */
3726static const char *
3727mergeinfo_cache_key(const char *path,
3728                    svn_fs_root_t *rev_root,
3729                    svn_mergeinfo_inheritance_t inherit,
3730                    svn_boolean_t adjust_inherited_mergeinfo,
3731                    apr_pool_t *pool)
3732{
3733  apr_int64_t number = rev_root->rev;
3734  number = number * 4
3735         + (inherit == svn_mergeinfo_nearest_ancestor ? 2 : 0)
3736         + (adjust_inherited_mergeinfo ? 1 : 0);
3737
3738  return svn_fs_fs__combine_number_and_string(number, path, pool);
3739}
3740
3741/* Calculates the mergeinfo for PATH under REV_ROOT using inheritance
3742   type INHERIT.  Returns it in *MERGEINFO, or NULL if there is none.
3743   The result is allocated in RESULT_POOL; SCRATCH_POOL is
3744   used for temporary allocations.
3745 */
3746static svn_error_t *
3747get_mergeinfo_for_path_internal(svn_mergeinfo_t *mergeinfo,
3748                                svn_fs_root_t *rev_root,
3749                                const char *path,
3750                                svn_mergeinfo_inheritance_t inherit,
3751                                svn_boolean_t adjust_inherited_mergeinfo,
3752                                apr_pool_t *result_pool,
3753                                apr_pool_t *scratch_pool)
3754{
3755  parent_path_t *parent_path, *nearest_ancestor;
3756  apr_hash_t *proplist;
3757  svn_string_t *mergeinfo_string;
3758
3759  path = svn_fs__canonicalize_abspath(path, scratch_pool);
3760
3761  SVN_ERR(open_path(&parent_path, rev_root, path, 0, NULL, scratch_pool));
3762
3763  if (inherit == svn_mergeinfo_nearest_ancestor && ! parent_path->parent)
3764    return SVN_NO_ERROR;
3765
3766  if (inherit == svn_mergeinfo_nearest_ancestor)
3767    nearest_ancestor = parent_path->parent;
3768  else
3769    nearest_ancestor = parent_path;
3770
3771  while (TRUE)
3772    {
3773      svn_boolean_t has_mergeinfo;
3774
3775      SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo,
3776                                           nearest_ancestor->node));
3777      if (has_mergeinfo)
3778        break;
3779
3780      /* No need to loop if we're looking for explicit mergeinfo. */
3781      if (inherit == svn_mergeinfo_explicit)
3782        {
3783          return SVN_NO_ERROR;
3784        }
3785
3786      nearest_ancestor = nearest_ancestor->parent;
3787
3788      /* Run out?  There's no mergeinfo. */
3789      if (!nearest_ancestor)
3790        {
3791          return SVN_NO_ERROR;
3792        }
3793    }
3794
3795  SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, nearest_ancestor->node,
3796                                      scratch_pool));
3797  mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
3798  if (!mergeinfo_string)
3799    return svn_error_createf
3800      (SVN_ERR_FS_CORRUPT, NULL,
3801       _("Node-revision '%s@%ld' claims to have mergeinfo but doesn't"),
3802       parent_path_path(nearest_ancestor, scratch_pool), rev_root->rev);
3803
3804  /* Parse the mergeinfo; store the result in *MERGEINFO. */
3805  {
3806    /* Issue #3896: If a node has syntactically invalid mergeinfo, then
3807       treat it as if no mergeinfo is present rather than raising a parse
3808       error. */
3809    svn_error_t *err = svn_mergeinfo_parse(mergeinfo,
3810                                           mergeinfo_string->data,
3811                                           result_pool);
3812    if (err)
3813      {
3814        if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
3815          {
3816            svn_error_clear(err);
3817            err = NULL;
3818            *mergeinfo = NULL;
3819          }
3820        return svn_error_trace(err);
3821      }
3822  }
3823
3824  /* If our nearest ancestor is the very path we inquired about, we
3825     can return the mergeinfo results directly.  Otherwise, we're
3826     inheriting the mergeinfo, so we need to a) remove non-inheritable
3827     ranges and b) telescope the merged-from paths. */
3828  if (adjust_inherited_mergeinfo && (nearest_ancestor != parent_path))
3829    {
3830      svn_mergeinfo_t tmp_mergeinfo;
3831
3832      SVN_ERR(svn_mergeinfo_inheritable2(&tmp_mergeinfo, *mergeinfo,
3833                                         NULL, SVN_INVALID_REVNUM,
3834                                         SVN_INVALID_REVNUM, TRUE,
3835                                         scratch_pool, scratch_pool));
3836      SVN_ERR(svn_fs__append_to_merged_froms(mergeinfo, tmp_mergeinfo,
3837                                             parent_path_relpath(
3838                                               parent_path, nearest_ancestor,
3839                                               scratch_pool),
3840                                             result_pool));
3841    }
3842
3843  return SVN_NO_ERROR;
3844}
3845
3846/* Caching wrapper around get_mergeinfo_for_path_internal().
3847 */
3848static svn_error_t *
3849get_mergeinfo_for_path(svn_mergeinfo_t *mergeinfo,
3850                       svn_fs_root_t *rev_root,
3851                       const char *path,
3852                       svn_mergeinfo_inheritance_t inherit,
3853                       svn_boolean_t adjust_inherited_mergeinfo,
3854                       apr_pool_t *result_pool,
3855                       apr_pool_t *scratch_pool)
3856{
3857  fs_fs_data_t *ffd = rev_root->fs->fsap_data;
3858  const char *cache_key;
3859  svn_boolean_t found = FALSE;
3860  svn_stringbuf_t *mergeinfo_exists;
3861
3862  *mergeinfo = NULL;
3863
3864  cache_key = mergeinfo_cache_key(path, rev_root, inherit,
3865                                  adjust_inherited_mergeinfo, scratch_pool);
3866  if (ffd->mergeinfo_existence_cache)
3867    {
3868      SVN_ERR(svn_cache__get((void **)&mergeinfo_exists, &found,
3869                             ffd->mergeinfo_existence_cache,
3870                             cache_key, result_pool));
3871      if (found && mergeinfo_exists->data[0] == '1')
3872        SVN_ERR(svn_cache__get((void **)mergeinfo, &found,
3873                              ffd->mergeinfo_cache,
3874                              cache_key, result_pool));
3875    }
3876
3877  if (! found)
3878    {
3879      SVN_ERR(get_mergeinfo_for_path_internal(mergeinfo, rev_root, path,
3880                                              inherit,
3881                                              adjust_inherited_mergeinfo,
3882                                              result_pool, scratch_pool));
3883      if (ffd->mergeinfo_existence_cache)
3884        {
3885          mergeinfo_exists = svn_stringbuf_create(*mergeinfo ? "1" : "0",
3886                                                  scratch_pool);
3887          SVN_ERR(svn_cache__set(ffd->mergeinfo_existence_cache,
3888                                 cache_key, mergeinfo_exists, scratch_pool));
3889          if (*mergeinfo)
3890            SVN_ERR(svn_cache__set(ffd->mergeinfo_cache,
3891                                  cache_key, *mergeinfo, scratch_pool));
3892        }
3893    }
3894
3895  return SVN_NO_ERROR;
3896}
3897
3898/* Adds mergeinfo for each descendant of PATH (but not PATH itself)
3899   under ROOT to RESULT_CATALOG.  Returned values are allocated in
3900   RESULT_POOL; temporary values in POOL. */
3901static svn_error_t *
3902add_descendant_mergeinfo(svn_mergeinfo_catalog_t result_catalog,
3903                         svn_fs_root_t *root,
3904                         const char *path,
3905                         apr_pool_t *result_pool,
3906                         apr_pool_t *scratch_pool)
3907{
3908  dag_node_t *this_dag;
3909  svn_boolean_t go_down;
3910
3911  SVN_ERR(get_dag(&this_dag, root, path, scratch_pool));
3912  SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down,
3913                                                        this_dag));
3914  if (go_down)
3915    SVN_ERR(crawl_directory_dag_for_mergeinfo(root,
3916                                              path,
3917                                              this_dag,
3918                                              result_catalog,
3919                                              result_pool,
3920                                              scratch_pool));
3921  return SVN_NO_ERROR;
3922}
3923
3924
3925/* Get the mergeinfo for a set of paths, returned in
3926   *MERGEINFO_CATALOG.  Returned values are allocated in
3927   POOL, while temporary values are allocated in a sub-pool. */
3928static svn_error_t *
3929get_mergeinfos_for_paths(svn_fs_root_t *root,
3930                         svn_mergeinfo_catalog_t *mergeinfo_catalog,
3931                         const apr_array_header_t *paths,
3932                         svn_mergeinfo_inheritance_t inherit,
3933                         svn_boolean_t include_descendants,
3934                         svn_boolean_t adjust_inherited_mergeinfo,
3935                         apr_pool_t *result_pool,
3936                         apr_pool_t *scratch_pool)
3937{
3938  svn_mergeinfo_catalog_t result_catalog = svn_hash__make(result_pool);
3939  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3940  int i;
3941
3942  for (i = 0; i < paths->nelts; i++)
3943    {
3944      svn_error_t *err;
3945      svn_mergeinfo_t path_mergeinfo;
3946      const char *path = APR_ARRAY_IDX(paths, i, const char *);
3947
3948      svn_pool_clear(iterpool);
3949
3950      err = get_mergeinfo_for_path(&path_mergeinfo, root, path,
3951                                   inherit, adjust_inherited_mergeinfo,
3952                                   result_pool, iterpool);
3953      if (err)
3954        {
3955          if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
3956            {
3957              svn_error_clear(err);
3958              err = NULL;
3959              path_mergeinfo = NULL;
3960            }
3961          else
3962            {
3963              return svn_error_trace(err);
3964            }
3965        }
3966
3967      if (path_mergeinfo)
3968        svn_hash_sets(result_catalog, path, path_mergeinfo);
3969      if (include_descendants)
3970        SVN_ERR(add_descendant_mergeinfo(result_catalog, root, path,
3971                                         result_pool, scratch_pool));
3972    }
3973  svn_pool_destroy(iterpool);
3974
3975  *mergeinfo_catalog = result_catalog;
3976  return SVN_NO_ERROR;
3977}
3978
3979
3980/* Implements svn_fs_get_mergeinfo. */
3981static svn_error_t *
3982fs_get_mergeinfo(svn_mergeinfo_catalog_t *catalog,
3983                 svn_fs_root_t *root,
3984                 const apr_array_header_t *paths,
3985                 svn_mergeinfo_inheritance_t inherit,
3986                 svn_boolean_t include_descendants,
3987                 svn_boolean_t adjust_inherited_mergeinfo,
3988                 apr_pool_t *result_pool,
3989                 apr_pool_t *scratch_pool)
3990{
3991  fs_fs_data_t *ffd = root->fs->fsap_data;
3992
3993  /* We require a revision root. */
3994  if (root->is_txn_root)
3995    return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
3996
3997  /* We have to actually be able to find the mergeinfo metadata! */
3998  if (! svn_fs_fs__fs_supports_mergeinfo(root->fs))
3999    return svn_error_createf
4000      (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
4001       _("Querying mergeinfo requires version %d of the FSFS filesystem "
4002         "schema; filesystem '%s' uses only version %d"),
4003       SVN_FS_FS__MIN_MERGEINFO_FORMAT, root->fs->path, ffd->format);
4004
4005  /* Retrieve a path -> mergeinfo hash mapping. */
4006  return get_mergeinfos_for_paths(root, catalog, paths,
4007                                  inherit,
4008                                  include_descendants,
4009                                  adjust_inherited_mergeinfo,
4010                                  result_pool, scratch_pool);
4011}
4012
4013
4014/* The vtable associated with root objects. */
4015static root_vtable_t root_vtable = {
4016  fs_paths_changed,
4017  svn_fs_fs__check_path,
4018  fs_node_history,
4019  svn_fs_fs__node_id,
4020  svn_fs_fs__node_created_rev,
4021  fs_node_origin_rev,
4022  fs_node_created_path,
4023  fs_delete_node,
4024  fs_copied_from,
4025  fs_closest_copy,
4026  fs_node_prop,
4027  fs_node_proplist,
4028  fs_change_node_prop,
4029  fs_props_changed,
4030  fs_dir_entries,
4031  fs_make_dir,
4032  fs_copy,
4033  fs_revision_link,
4034  fs_file_length,
4035  fs_file_checksum,
4036  fs_file_contents,
4037  fs_try_process_file_contents,
4038  fs_make_file,
4039  fs_apply_textdelta,
4040  fs_apply_text,
4041  fs_contents_changed,
4042  fs_get_file_delta_stream,
4043  fs_merge,
4044  fs_get_mergeinfo,
4045};
4046
4047/* Construct a new root object in FS, allocated from POOL.  */
4048static svn_fs_root_t *
4049make_root(svn_fs_t *fs,
4050          apr_pool_t *pool)
4051{
4052  svn_fs_root_t *root = apr_pcalloc(pool, sizeof(*root));
4053
4054  root->fs = fs;
4055  root->pool = pool;
4056  root->vtable = &root_vtable;
4057
4058  return root;
4059}
4060
4061
4062/* Construct a root object referring to the root of REVISION in FS,
4063   whose root directory is ROOT_DIR.  Create the new root in POOL.  */
4064static svn_fs_root_t *
4065make_revision_root(svn_fs_t *fs,
4066                   svn_revnum_t rev,
4067                   dag_node_t *root_dir,
4068                   apr_pool_t *pool)
4069{
4070  svn_fs_root_t *root = make_root(fs, pool);
4071  fs_rev_root_data_t *frd = apr_pcalloc(root->pool, sizeof(*frd));
4072
4073  root->is_txn_root = FALSE;
4074  root->rev = rev;
4075
4076  frd->root_dir = root_dir;
4077  frd->copyfrom_cache = svn_hash__make(root->pool);
4078
4079  root->fsap_data = frd;
4080
4081  return root;
4082}
4083
4084
4085/* Construct a root object referring to the root of the transaction
4086   named TXN and based on revision BASE_REV in FS, with FLAGS to
4087   describe transaction's behavior.  Create the new root in POOL.  */
4088static svn_error_t *
4089make_txn_root(svn_fs_root_t **root_p,
4090              svn_fs_t *fs,
4091              const char *txn,
4092              svn_revnum_t base_rev,
4093              apr_uint32_t flags,
4094              apr_pool_t *pool)
4095{
4096  svn_fs_root_t *root = make_root(fs, pool);
4097  fs_txn_root_data_t *frd = apr_pcalloc(root->pool, sizeof(*frd));
4098
4099  root->is_txn_root = TRUE;
4100  root->txn = apr_pstrdup(root->pool, txn);
4101  root->txn_flags = flags;
4102  root->rev = base_rev;
4103
4104  frd->txn_id = txn;
4105
4106  /* Because this cache actually tries to invalidate elements, keep
4107     the number of elements per page down.
4108
4109     Note that since dag_node_cache_invalidate uses svn_cache__iter,
4110     this *cannot* be a memcache-based cache.  */
4111  SVN_ERR(svn_cache__create_inprocess(&(frd->txn_node_cache),
4112                                      svn_fs_fs__dag_serialize,
4113                                      svn_fs_fs__dag_deserialize,
4114                                      APR_HASH_KEY_STRING,
4115                                      32, 20, FALSE,
4116                                      apr_pstrcat(pool, txn, ":TXN",
4117                                                  (char *)NULL),
4118                                      root->pool));
4119
4120  /* Initialize transaction-local caches in FS.
4121
4122     Note that we cannot put those caches in frd because that content
4123     fs root object is not available where we would need it. */
4124  SVN_ERR(svn_fs_fs__initialize_txn_caches(fs, txn, pool));
4125
4126  root->fsap_data = frd;
4127
4128  *root_p = root;
4129  return SVN_NO_ERROR;
4130}
4131
4132
4133
4134/* Verify. */
4135static APR_INLINE const char *
4136stringify_node(dag_node_t *node,
4137               apr_pool_t *pool)
4138{
4139  /* ### TODO: print some PATH@REV to it, too. */
4140  return svn_fs_fs__id_unparse(svn_fs_fs__dag_get_id(node), pool)->data;
4141}
4142
4143/* Check metadata sanity on NODE, and on its children.  Manually verify
4144   information for DAG nodes in revision REV, and trust the metadata
4145   accuracy for nodes belonging to older revisions. */
4146static svn_error_t *
4147verify_node(dag_node_t *node,
4148            svn_revnum_t rev,
4149            apr_pool_t *pool)
4150{
4151  svn_boolean_t has_mergeinfo;
4152  apr_int64_t mergeinfo_count;
4153  const svn_fs_id_t *pred_id;
4154  svn_fs_t *fs = svn_fs_fs__dag_get_fs(node);
4155  int pred_count;
4156  svn_node_kind_t kind;
4157  apr_pool_t *iterpool = svn_pool_create(pool);
4158
4159  /* Fetch some data. */
4160  SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, node));
4161  SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_count, node));
4162  SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
4163  SVN_ERR(svn_fs_fs__dag_get_predecessor_count(&pred_count, node));
4164  kind = svn_fs_fs__dag_node_kind(node);
4165
4166  /* Sanity check. */
4167  if (mergeinfo_count < 0)
4168    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4169                             "Negative mergeinfo-count %" APR_INT64_T_FMT
4170                             " on node '%s'",
4171                             mergeinfo_count, stringify_node(node, iterpool));
4172
4173  /* Issue #4129. (This check will explicitly catch non-root instances too.) */
4174  if (pred_id)
4175    {
4176      dag_node_t *pred;
4177      int pred_pred_count;
4178      SVN_ERR(svn_fs_fs__dag_get_node(&pred, fs, pred_id, iterpool));
4179      SVN_ERR(svn_fs_fs__dag_get_predecessor_count(&pred_pred_count, pred));
4180      if (pred_pred_count+1 != pred_count)
4181        return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4182                                 "Predecessor count mismatch: "
4183                                 "%s has %d, but %s has %d",
4184                                 stringify_node(node, iterpool), pred_count,
4185                                 stringify_node(pred, iterpool),
4186                                 pred_pred_count);
4187    }
4188
4189  /* Kind-dependent verifications. */
4190  if (kind == svn_node_none)
4191    {
4192      return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4193                               "Node '%s' has kind 'none'",
4194                               stringify_node(node, iterpool));
4195    }
4196  if (kind == svn_node_file)
4197    {
4198      if (has_mergeinfo != mergeinfo_count) /* comparing int to bool */
4199        return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4200                                 "File node '%s' has inconsistent mergeinfo: "
4201                                 "has_mergeinfo=%d, "
4202                                 "mergeinfo_count=%" APR_INT64_T_FMT,
4203                                 stringify_node(node, iterpool),
4204                                 has_mergeinfo, mergeinfo_count);
4205    }
4206  if (kind == svn_node_dir)
4207    {
4208      apr_hash_t *entries;
4209      apr_hash_index_t *hi;
4210      apr_int64_t children_mergeinfo = 0;
4211
4212      SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool));
4213
4214      /* Compute CHILDREN_MERGEINFO. */
4215      for (hi = apr_hash_first(pool, entries);
4216           hi;
4217           hi = apr_hash_next(hi))
4218        {
4219          svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
4220          dag_node_t *child;
4221          svn_revnum_t child_rev;
4222          apr_int64_t child_mergeinfo;
4223
4224          svn_pool_clear(iterpool);
4225
4226          /* Compute CHILD_REV. */
4227          SVN_ERR(svn_fs_fs__dag_get_node(&child, fs, dirent->id, iterpool));
4228          SVN_ERR(svn_fs_fs__dag_get_revision(&child_rev, child, iterpool));
4229
4230          if (child_rev == rev)
4231            SVN_ERR(verify_node(child, rev, iterpool));
4232
4233          SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&child_mergeinfo, child));
4234          children_mergeinfo += child_mergeinfo;
4235        }
4236
4237      /* Side-effect of issue #4129. */
4238      if (children_mergeinfo+has_mergeinfo != mergeinfo_count)
4239        return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4240                                 "Mergeinfo-count discrepancy on '%s': "
4241                                 "expected %" APR_INT64_T_FMT "+%d, "
4242                                 "counted %" APR_INT64_T_FMT,
4243                                 stringify_node(node, iterpool),
4244                                 mergeinfo_count, has_mergeinfo,
4245                                 children_mergeinfo);
4246    }
4247
4248  svn_pool_destroy(iterpool);
4249  return SVN_NO_ERROR;
4250}
4251
4252svn_error_t *
4253svn_fs_fs__verify_root(svn_fs_root_t *root,
4254                       apr_pool_t *pool)
4255{
4256  svn_fs_t *fs = root->fs;
4257  dag_node_t *root_dir;
4258
4259  /* Issue #4129: bogus pred-counts and minfo-cnt's on the root node-rev
4260     (and elsewhere).  This code makes more thorough checks than the
4261     commit-time checks in validate_root_noderev(). */
4262
4263  /* Callers should disable caches by setting SVN_FS_CONFIG_FSFS_CACHE_NS;
4264     see r1462436.
4265
4266     When this code is called in the library, we want to ensure we
4267     use the on-disk data --- rather than some data that was read
4268     in the possibly-distance past and cached since. */
4269
4270  if (root->is_txn_root)
4271    {
4272      fs_txn_root_data_t *frd = root->fsap_data;
4273      SVN_ERR(svn_fs_fs__dag_txn_root(&root_dir, fs, frd->txn_id, pool));
4274    }
4275  else
4276    {
4277      fs_rev_root_data_t *frd = root->fsap_data;
4278      root_dir = frd->root_dir;
4279    }
4280
4281  /* Recursively verify ROOT_DIR. */
4282  SVN_ERR(verify_node(root_dir, root->rev, pool));
4283
4284  /* Verify explicitly the predecessor of the root. */
4285  {
4286    const svn_fs_id_t *pred_id;
4287
4288    /* Only r0 should have no predecessor. */
4289    SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, root_dir));
4290    if (! root->is_txn_root && !!pred_id != !!root->rev)
4291      return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4292                               "r%ld's root node's predecessor is "
4293                               "unexpectedly '%s'",
4294                               root->rev,
4295                               (pred_id
4296                                ? svn_fs_fs__id_unparse(pred_id, pool)->data
4297                                : "(null)"));
4298    if (root->is_txn_root && !pred_id)
4299      return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4300                               "Transaction '%s''s root node's predecessor is "
4301                               "unexpectedly NULL",
4302                               root->txn);
4303
4304    /* Check the predecessor's revision. */
4305    if (pred_id)
4306      {
4307        svn_revnum_t pred_rev = svn_fs_fs__id_rev(pred_id);
4308        if (! root->is_txn_root && pred_rev+1 != root->rev)
4309          /* Issue #4129. */
4310          return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4311                                   "r%ld's root node's predecessor is r%ld"
4312                                   " but should be r%ld",
4313                                   root->rev, pred_rev, root->rev - 1);
4314        if (root->is_txn_root && pred_rev != root->rev)
4315          return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4316                                   "Transaction '%s''s root node's predecessor"
4317                                   " is r%ld"
4318                                   " but should be r%ld",
4319                                   root->txn, pred_rev, root->rev);
4320      }
4321  }
4322
4323  return SVN_NO_ERROR;
4324}
4325