1251881Speter/* tree.c : tree-like filesystem, built on DAG filesystem
2251881Speter *
3251881Speter * ====================================================================
4251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
5251881Speter *    or more contributor license agreements.  See the NOTICE file
6251881Speter *    distributed with this work for additional information
7251881Speter *    regarding copyright ownership.  The ASF licenses this file
8251881Speter *    to you under the Apache License, Version 2.0 (the
9251881Speter *    "License"); you may not use this file except in compliance
10251881Speter *    with the License.  You may obtain a copy of the License at
11251881Speter *
12251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
13251881Speter *
14251881Speter *    Unless required by applicable law or agreed to in writing,
15251881Speter *    software distributed under the License is distributed on an
16251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17251881Speter *    KIND, either express or implied.  See the License for the
18251881Speter *    specific language governing permissions and limitations
19251881Speter *    under the License.
20251881Speter * ====================================================================
21251881Speter */
22251881Speter
23251881Speter
24251881Speter/* The job of this layer is to take a filesystem with lots of node
25251881Speter   sharing going on --- the real DAG filesystem as it appears in the
26251881Speter   database --- and make it look and act like an ordinary tree
27251881Speter   filesystem, with no sharing.
28251881Speter
29251881Speter   We do just-in-time cloning: you can walk from some unfinished
30251881Speter   transaction's root down into directories and files shared with
31251881Speter   committed revisions; as soon as you try to change something, the
32251881Speter   appropriate nodes get cloned (and parent directory entries updated)
33251881Speter   invisibly, behind your back.  Any other references you have to
34251881Speter   nodes that have been cloned by other changes, even made by other
35251881Speter   processes, are automatically updated to point to the right clones.  */
36251881Speter
37251881Speter
38251881Speter#include <stdlib.h>
39251881Speter#include <string.h>
40251881Speter#include <assert.h>
41251881Speter#include "svn_private_config.h"
42251881Speter#include "svn_hash.h"
43251881Speter#include "svn_pools.h"
44251881Speter#include "svn_error.h"
45251881Speter#include "svn_path.h"
46251881Speter#include "svn_mergeinfo.h"
47251881Speter#include "svn_fs.h"
48251881Speter#include "svn_sorts.h"
49251881Speter#include "svn_checksum.h"
50251881Speter#include "fs.h"
51251881Speter#include "err.h"
52251881Speter#include "trail.h"
53251881Speter#include "node-rev.h"
54251881Speter#include "key-gen.h"
55251881Speter#include "dag.h"
56251881Speter#include "tree.h"
57251881Speter#include "lock.h"
58251881Speter#include "revs-txns.h"
59251881Speter#include "id.h"
60251881Speter#include "bdb/txn-table.h"
61251881Speter#include "bdb/rev-table.h"
62251881Speter#include "bdb/nodes-table.h"
63251881Speter#include "bdb/changes-table.h"
64251881Speter#include "bdb/copies-table.h"
65251881Speter#include "bdb/node-origins-table.h"
66251881Speter#include "bdb/miscellaneous-table.h"
67251881Speter#include "../libsvn_fs/fs-loader.h"
68251881Speter#include "private/svn_fspath.h"
69251881Speter#include "private/svn_fs_util.h"
70251881Speter#include "private/svn_mergeinfo_private.h"
71251881Speter
72251881Speter
73251881Speter/* ### I believe this constant will become internal to reps-strings.c.
74251881Speter   ### see the comment in window_consumer() for more information. */
75251881Speter
76251881Speter/* ### the comment also seems to need tweaking: the log file stuff
77251881Speter   ### is no longer an issue... */
78251881Speter/* Data written to the filesystem through the svn_fs_apply_textdelta()
79251881Speter   interface is cached in memory until the end of the data stream, or
80251881Speter   until a size trigger is hit.  Define that trigger here (in bytes).
81251881Speter   Setting the value to 0 will result in no filesystem buffering at
82251881Speter   all.  The value only really matters when dealing with file contents
83251881Speter   bigger than the value itself.  Above that point, large values here
84251881Speter   allow the filesystem to buffer more data in memory before flushing
85251881Speter   to the database, which increases memory usage but greatly decreases
86251881Speter   the amount of disk access (and log-file generation) in database.
87251881Speter   Smaller values will limit your overall memory consumption, but can
88251881Speter   drastically hurt throughput by necessitating more write operations
89251881Speter   to the database (which also generates more log-files).  */
90251881Speter#define WRITE_BUFFER_SIZE          512000
91251881Speter
92251881Speter/* The maximum number of cache items to maintain in the node cache. */
93251881Speter#define NODE_CACHE_MAX_KEYS        32
94251881Speter
95251881Speter
96251881Speter
97251881Speter/* The root structure.  */
98251881Speter
99251881Speter/* Structure for svn_fs_root_t's node_cache hash values. */
100251881Speterstruct dag_node_cache_t
101251881Speter{
102251881Speter  dag_node_t *node; /* NODE to be cached. */
103251881Speter  int idx;          /* Index into the keys array for this cache item's key. */
104251881Speter  apr_pool_t *pool; /* Pool in which NODE is allocated. */
105251881Speter};
106251881Speter
107251881Speter
108251881Spetertypedef struct base_root_data_t
109251881Speter{
110251881Speter
111251881Speter  /* For revision roots, this is a dag node for the revision's root
112251881Speter     directory.  For transaction roots, we open the root directory
113251881Speter     afresh every time, since the root may have been cloned, or
114251881Speter     the transaction may have disappeared altogether.  */
115251881Speter  dag_node_t *root_dir;
116251881Speter
117251881Speter  /* Cache structures, for mapping const char * PATH to const
118251881Speter     struct dag_node_cache_t * structures.
119251881Speter
120251881Speter     ### Currently this is only used for revision roots.  To be safe
121251881Speter     for transaction roots, you must have the guarantee that there is
122251881Speter     never more than a single transaction root per Subversion
123251881Speter     transaction ever open at a given time -- having two roots open to
124251881Speter     the same Subversion transaction would be a request for pain.
125251881Speter     Also, you have to ensure that if a 'make_path_mutable()' fails for
126251881Speter     any reason, you don't leave cached nodes for the portion of that
127251881Speter     function that succeeded.  In other words, this cache must never,
128251881Speter     ever, lie. */
129251881Speter  apr_hash_t *node_cache;
130251881Speter  const char *node_cache_keys[NODE_CACHE_MAX_KEYS];
131251881Speter  int node_cache_idx;
132251881Speter} base_root_data_t;
133251881Speter
134251881Speter
135251881Speterstatic svn_fs_root_t *make_revision_root(svn_fs_t *fs, svn_revnum_t rev,
136251881Speter                                         dag_node_t *root_dir,
137251881Speter                                         apr_pool_t *pool);
138251881Speter
139251881Speterstatic svn_fs_root_t *make_txn_root(svn_fs_t *fs, const char *txn,
140251881Speter                                    svn_revnum_t base_rev, apr_uint32_t flags,
141251881Speter                                    apr_pool_t *pool);
142251881Speter
143251881Speter
144251881Speter/*** Node Caching in the Roots. ***/
145251881Speter
146251881Speter/* Return NODE for PATH from ROOT's node cache, or NULL if the node
147251881Speter   isn't cached. */
148251881Speterstatic dag_node_t *
149251881Speterdag_node_cache_get(svn_fs_root_t *root,
150251881Speter                   const char *path,
151251881Speter                   apr_pool_t *pool)
152251881Speter{
153251881Speter  base_root_data_t *brd = root->fsap_data;
154251881Speter  struct dag_node_cache_t *cache_item;
155251881Speter
156251881Speter  /* Assert valid input. */
157251881Speter  assert(*path == '/');
158251881Speter
159251881Speter  /* Only allow revision roots. */
160251881Speter  if (root->is_txn_root)
161251881Speter    return NULL;
162251881Speter
163251881Speter  /* Look in the cache for our desired item. */
164251881Speter  cache_item = svn_hash_gets(brd->node_cache, path);
165251881Speter  if (cache_item)
166251881Speter    return svn_fs_base__dag_dup(cache_item->node, pool);
167251881Speter
168251881Speter  return NULL;
169251881Speter}
170251881Speter
171251881Speter
172251881Speter/* Add the NODE for PATH to ROOT's node cache.  Callers should *NOT*
173251881Speter   call this unless they are adding a currently un-cached item to the
174251881Speter   cache, or are replacing the NODE for PATH with a new (different)
175251881Speter   one. */
176251881Speterstatic void
177251881Speterdag_node_cache_set(svn_fs_root_t *root,
178251881Speter                   const char *path,
179251881Speter                   dag_node_t *node)
180251881Speter{
181251881Speter  base_root_data_t *brd = root->fsap_data;
182251881Speter  const char *cache_path;
183251881Speter  apr_pool_t *cache_pool;
184251881Speter  struct dag_node_cache_t *cache_item;
185251881Speter  int num_keys = apr_hash_count(brd->node_cache);
186251881Speter
187251881Speter  /* What?  No POOL passed to this function?
188251881Speter
189251881Speter     To ensure that our cache values live as long as the svn_fs_root_t
190251881Speter     in which they are ultimately stored, and to allow us to free()
191251881Speter     them individually without harming the rest, they are each
192251881Speter     allocated from a subpool of ROOT's pool.  We'll keep one subpool
193251881Speter     around for each cache slot -- as we start expiring stuff
194251881Speter     to make room for more entries, we'll re-use the expired thing's
195251881Speter     pool. */
196251881Speter
197251881Speter  /* Assert valid input and state. */
198251881Speter  assert(*path == '/');
199251881Speter  assert((brd->node_cache_idx <= num_keys)
200251881Speter         && (num_keys <= NODE_CACHE_MAX_KEYS));
201251881Speter
202251881Speter  /* Only allow revision roots. */
203251881Speter  if (root->is_txn_root)
204251881Speter    return;
205251881Speter
206251881Speter  /* Special case: the caller wants us to replace an existing cached
207251881Speter     node with a new one.  If the callers aren't mindless, this should
208251881Speter     only happen when a node is made mutable under a transaction
209251881Speter     root, and that only happens once under that root.  So, we'll be a
210251881Speter     little bit sloppy here, and count on callers doing the right
211251881Speter     thing. */
212251881Speter  cache_item = svn_hash_gets(brd->node_cache, path);
213251881Speter  if (cache_item)
214251881Speter    {
215251881Speter      /* ### This section is somehow broken.  I don't know how, but it
216251881Speter         ### is.  And I don't want to spend any more time on it.  So,
217251881Speter         ### callers, use only revision root and don't try to update
218251881Speter         ### an already-cached thing.  -- cmpilato */
219251881Speter      SVN_ERR_MALFUNCTION_NO_RETURN();
220251881Speter
221251881Speter#if 0
222251881Speter      int cache_index = cache_item->idx;
223251881Speter      cache_path = brd->node_cache_keys[cache_index];
224251881Speter      cache_pool = cache_item->pool;
225251881Speter      cache_item->node = svn_fs_base__dag_dup(node, cache_pool);
226251881Speter
227251881Speter      /* Now, move the cache key reference to the end of the keys in
228251881Speter         the keys array (unless it's already at the end).  ### Yes,
229251881Speter         it's a memmove(), but we're not talking about pages of memory
230251881Speter         here. */
231251881Speter      if (cache_index != (num_keys - 1))
232251881Speter        {
233251881Speter          int move_num = NODE_CACHE_MAX_KEYS - cache_index - 1;
234251881Speter          memmove(brd->node_cache_keys + cache_index,
235251881Speter                  brd->node_cache_keys + cache_index + 1,
236251881Speter                  move_num * sizeof(const char *));
237251881Speter          cache_index = num_keys - 1;
238251881Speter          brd->node_cache_keys[cache_index] = cache_path;
239251881Speter        }
240251881Speter
241251881Speter      /* Advance the cache pointers. */
242251881Speter      cache_item->idx = cache_index;
243251881Speter      brd->node_cache_idx = (cache_index + 1) % NODE_CACHE_MAX_KEYS;
244251881Speter      return;
245251881Speter#endif
246251881Speter    }
247251881Speter
248251881Speter  /* We're adding a new cache item.  First, see if we have room for it
249251881Speter     (otherwise, make some room). */
250251881Speter  if (apr_hash_count(brd->node_cache) == NODE_CACHE_MAX_KEYS)
251251881Speter    {
252251881Speter      /* No room.  Expire the oldest thing. */
253251881Speter      cache_path = brd->node_cache_keys[brd->node_cache_idx];
254251881Speter      cache_item = svn_hash_gets(brd->node_cache, cache_path);
255251881Speter      svn_hash_sets(brd->node_cache, cache_path, NULL);
256251881Speter      cache_pool = cache_item->pool;
257251881Speter      svn_pool_clear(cache_pool);
258251881Speter    }
259251881Speter  else
260251881Speter    {
261251881Speter      cache_pool = svn_pool_create(root->pool);
262251881Speter    }
263251881Speter
264251881Speter  /* Make the cache item, allocated in its own pool. */
265251881Speter  cache_item = apr_palloc(cache_pool, sizeof(*cache_item));
266251881Speter  cache_item->node = svn_fs_base__dag_dup(node, cache_pool);
267251881Speter  cache_item->idx = brd->node_cache_idx;
268251881Speter  cache_item->pool = cache_pool;
269251881Speter
270251881Speter  /* Now add it to the cache. */
271251881Speter  cache_path = apr_pstrdup(cache_pool, path);
272251881Speter  svn_hash_sets(brd->node_cache, cache_path, cache_item);
273251881Speter  brd->node_cache_keys[brd->node_cache_idx] = cache_path;
274251881Speter
275251881Speter  /* Advance the cache pointer. */
276251881Speter  brd->node_cache_idx = (brd->node_cache_idx + 1) % NODE_CACHE_MAX_KEYS;
277251881Speter}
278251881Speter
279251881Speter
280251881Speter
281251881Speter
282251881Speter/* Creating transaction and revision root nodes.  */
283251881Speter
284251881Speterstruct txn_root_args
285251881Speter{
286251881Speter  svn_fs_root_t **root_p;
287251881Speter  svn_fs_txn_t *txn;
288251881Speter};
289251881Speter
290251881Speter
291251881Speterstatic svn_error_t *
292251881Spetertxn_body_txn_root(void *baton,
293251881Speter                  trail_t *trail)
294251881Speter{
295251881Speter  struct txn_root_args *args = baton;
296251881Speter  svn_fs_root_t **root_p = args->root_p;
297251881Speter  svn_fs_txn_t *txn = args->txn;
298251881Speter  svn_fs_t *fs = txn->fs;
299251881Speter  const char *svn_txn_id = txn->id;
300251881Speter  const svn_fs_id_t *root_id, *base_root_id;
301251881Speter  svn_fs_root_t *root;
302251881Speter  apr_hash_t *txnprops;
303251881Speter  apr_uint32_t flags = 0;
304251881Speter
305251881Speter  /* Verify that the transaction actually exists.  */
306251881Speter  SVN_ERR(svn_fs_base__get_txn_ids(&root_id, &base_root_id, fs,
307251881Speter                                   svn_txn_id, trail, trail->pool));
308251881Speter
309251881Speter  /* Look for special txn props that represent the 'flags' behavior of
310251881Speter     the transaction. */
311251881Speter  SVN_ERR(svn_fs_base__txn_proplist_in_trail(&txnprops, svn_txn_id, trail));
312251881Speter  if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD))
313251881Speter    flags |= SVN_FS_TXN_CHECK_OOD;
314251881Speter
315251881Speter  if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS))
316251881Speter    flags |= SVN_FS_TXN_CHECK_LOCKS;
317251881Speter
318251881Speter  root = make_txn_root(fs, svn_txn_id, txn->base_rev, flags, trail->pool);
319251881Speter
320251881Speter  *root_p = root;
321251881Speter  return SVN_NO_ERROR;
322251881Speter}
323251881Speter
324251881Speter
325251881Spetersvn_error_t *
326251881Spetersvn_fs_base__txn_root(svn_fs_root_t **root_p,
327251881Speter                      svn_fs_txn_t *txn,
328251881Speter                      apr_pool_t *pool)
329251881Speter{
330251881Speter  svn_fs_root_t *root;
331251881Speter  struct txn_root_args args;
332251881Speter
333251881Speter  args.root_p = &root;
334251881Speter  args.txn = txn;
335251881Speter  SVN_ERR(svn_fs_base__retry_txn(txn->fs, txn_body_txn_root, &args,
336251881Speter                                 FALSE, pool));
337251881Speter
338251881Speter  *root_p = root;
339251881Speter  return SVN_NO_ERROR;
340251881Speter}
341251881Speter
342251881Speter
343251881Speterstruct revision_root_args
344251881Speter{
345251881Speter  svn_fs_root_t **root_p;
346251881Speter  svn_revnum_t rev;
347251881Speter};
348251881Speter
349251881Speter
350251881Speterstatic svn_error_t *
351251881Spetertxn_body_revision_root(void *baton,
352251881Speter                       trail_t *trail)
353251881Speter{
354251881Speter  struct revision_root_args *args = baton;
355251881Speter  dag_node_t *root_dir;
356251881Speter  svn_fs_root_t *root;
357251881Speter
358251881Speter  SVN_ERR(svn_fs_base__dag_revision_root(&root_dir, trail->fs, args->rev,
359251881Speter                                         trail, trail->pool));
360251881Speter  root = make_revision_root(trail->fs, args->rev, root_dir, trail->pool);
361251881Speter
362251881Speter  *args->root_p = root;
363251881Speter  return SVN_NO_ERROR;
364251881Speter}
365251881Speter
366251881Speter
367251881Spetersvn_error_t *
368251881Spetersvn_fs_base__revision_root(svn_fs_root_t **root_p,
369251881Speter                           svn_fs_t *fs,
370251881Speter                           svn_revnum_t rev,
371251881Speter                           apr_pool_t *pool)
372251881Speter{
373251881Speter  struct revision_root_args args;
374251881Speter  svn_fs_root_t *root;
375251881Speter
376251881Speter  SVN_ERR(svn_fs__check_fs(fs, TRUE));
377251881Speter
378251881Speter  args.root_p = &root;
379251881Speter  args.rev = rev;
380251881Speter  SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_revision_root, &args,
381251881Speter                                 FALSE, pool));
382251881Speter
383251881Speter  *root_p = root;
384251881Speter  return SVN_NO_ERROR;
385251881Speter}
386251881Speter
387251881Speter
388251881Speter
389251881Speter/* Getting dag nodes for roots.  */
390251881Speter
391251881Speter
392251881Speter/* Set *NODE_P to a freshly opened dag node referring to the root
393251881Speter   directory of ROOT, as part of TRAIL.  */
394251881Speterstatic svn_error_t *
395251881Speterroot_node(dag_node_t **node_p,
396251881Speter          svn_fs_root_t *root,
397251881Speter          trail_t *trail,
398251881Speter          apr_pool_t *pool)
399251881Speter{
400251881Speter  base_root_data_t *brd = root->fsap_data;
401251881Speter
402251881Speter  if (! root->is_txn_root)
403251881Speter    {
404251881Speter      /* It's a revision root, so we already have its root directory
405251881Speter         opened.  */
406251881Speter      *node_p = svn_fs_base__dag_dup(brd->root_dir, pool);
407251881Speter      return SVN_NO_ERROR;
408251881Speter    }
409251881Speter  else
410251881Speter    {
411251881Speter      /* It's a transaction root.  Open a fresh copy.  */
412251881Speter      return svn_fs_base__dag_txn_root(node_p, root->fs, root->txn,
413251881Speter                                       trail, pool);
414251881Speter    }
415251881Speter}
416251881Speter
417251881Speter
418251881Speter/* Set *NODE_P to a mutable root directory for ROOT, cloning if
419251881Speter   necessary, as part of TRAIL.  ROOT must be a transaction root.  Use
420251881Speter   ERROR_PATH in error messages.  */
421251881Speterstatic svn_error_t *
422251881Spetermutable_root_node(dag_node_t **node_p,
423251881Speter                  svn_fs_root_t *root,
424251881Speter                  const char *error_path,
425251881Speter                  trail_t *trail,
426251881Speter                  apr_pool_t *pool)
427251881Speter{
428251881Speter  if (root->is_txn_root)
429251881Speter    return svn_fs_base__dag_clone_root(node_p, root->fs, root->txn,
430251881Speter                                       trail, pool);
431251881Speter  else
432251881Speter    /* If it's not a transaction root, we can't change its contents.  */
433251881Speter    return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path);
434251881Speter}
435251881Speter
436251881Speter
437251881Speter
438251881Speter/* Traversing directory paths.  */
439251881Speter
440251881Spetertypedef enum copy_id_inherit_t
441251881Speter{
442251881Speter  copy_id_inherit_unknown = 0,
443251881Speter  copy_id_inherit_self,
444251881Speter  copy_id_inherit_parent,
445251881Speter  copy_id_inherit_new
446251881Speter
447251881Speter} copy_id_inherit_t;
448251881Speter
449251881Speter/* A linked list representing the path from a node up to a root
450251881Speter   directory.  We use this for cloning, and for operations that need
451251881Speter   to deal with both a node and its parent directory.  For example, a
452251881Speter   `delete' operation needs to know that the node actually exists, but
453251881Speter   also needs to change the parent directory.  */
454251881Spetertypedef struct parent_path_t
455251881Speter{
456251881Speter
457251881Speter  /* A node along the path.  This could be the final node, one of its
458251881Speter     parents, or the root.  Every parent path ends with an element for
459251881Speter     the root directory.  */
460251881Speter  dag_node_t *node;
461251881Speter
462251881Speter  /* The name NODE has in its parent directory.  This is zero for the
463251881Speter     root directory, which (obviously) has no name in its parent.  */
464251881Speter  char *entry;
465251881Speter
466251881Speter  /* The parent of NODE, or zero if NODE is the root directory.  */
467251881Speter  struct parent_path_t *parent;
468251881Speter
469251881Speter  /* The copy ID inheritance style. */
470251881Speter  copy_id_inherit_t copy_inherit;
471251881Speter
472251881Speter  /* If copy ID inheritance style is copy_id_inherit_new, this is the
473251881Speter     path which should be implicitly copied; otherwise, this is NULL. */
474251881Speter  const char *copy_src_path;
475251881Speter
476251881Speter} parent_path_t;
477251881Speter
478251881Speter
479251881Speter/* Return the FS path for the parent path chain object PARENT_PATH,
480251881Speter   allocated in POOL. */
481251881Speterstatic const char *
482251881Speterparent_path_path(parent_path_t *parent_path,
483251881Speter                 apr_pool_t *pool)
484251881Speter{
485251881Speter  const char *path_so_far = "/";
486251881Speter  if (parent_path->parent)
487251881Speter    path_so_far = parent_path_path(parent_path->parent, pool);
488251881Speter  return parent_path->entry
489251881Speter    ? svn_fspath__join(path_so_far, parent_path->entry, pool)
490251881Speter         : path_so_far;
491251881Speter}
492251881Speter
493251881Speter
494251881Speter/* Return the FS path for the parent path chain object CHILD relative
495251881Speter   to its ANCESTOR in the same chain, allocated in POOL.  */
496251881Speterstatic const char *
497251881Speterparent_path_relpath(parent_path_t *child,
498251881Speter                    parent_path_t *ancestor,
499251881Speter                    apr_pool_t *pool)
500251881Speter{
501251881Speter  const char *path_so_far = "";
502251881Speter  parent_path_t *this_node = child;
503251881Speter  while (this_node != ancestor)
504251881Speter    {
505251881Speter      assert(this_node != NULL);
506251881Speter      path_so_far = svn_relpath_join(this_node->entry, path_so_far, pool);
507251881Speter      this_node = this_node->parent;
508251881Speter    }
509251881Speter  return path_so_far;
510251881Speter}
511251881Speter
512251881Speter
513251881Speter/* Choose a copy ID inheritance method *INHERIT_P to be used in the
514251881Speter   event that immutable node CHILD in FS needs to be made mutable.  If
515251881Speter   the inheritance method is copy_id_inherit_new, also return a
516251881Speter   *COPY_SRC_PATH on which to base the new copy ID (else return NULL
517251881Speter   for that path).  CHILD must have a parent (it cannot be the root
518251881Speter   node).  TXN_ID is the transaction in which these items might be
519251881Speter   mutable.  */
520251881Speterstatic svn_error_t *
521251881Speterget_copy_inheritance(copy_id_inherit_t *inherit_p,
522251881Speter                     const char **copy_src_path,
523251881Speter                     svn_fs_t *fs,
524251881Speter                     parent_path_t *child,
525251881Speter                     const char *txn_id,
526251881Speter                     trail_t *trail,
527251881Speter                     apr_pool_t *pool)
528251881Speter{
529251881Speter  const svn_fs_id_t *child_id, *parent_id;
530251881Speter  const char *child_copy_id, *parent_copy_id;
531251881Speter  const char *id_path = NULL;
532251881Speter
533251881Speter  SVN_ERR_ASSERT(child && child->parent && txn_id);
534251881Speter
535251881Speter  /* Initialize our return variables (default: self-inheritance). */
536251881Speter  *inherit_p = copy_id_inherit_self;
537251881Speter  *copy_src_path = NULL;
538251881Speter
539251881Speter  /* Initialize some convenience variables. */
540251881Speter  child_id = svn_fs_base__dag_get_id(child->node);
541251881Speter  parent_id = svn_fs_base__dag_get_id(child->parent->node);
542251881Speter  child_copy_id = svn_fs_base__id_copy_id(child_id);
543251881Speter  parent_copy_id = svn_fs_base__id_copy_id(parent_id);
544251881Speter
545251881Speter  /* Easy out: if this child is already mutable, we have nothing to do. */
546251881Speter  if (svn_fs_base__key_compare(svn_fs_base__id_txn_id(child_id), txn_id) == 0)
547251881Speter    return SVN_NO_ERROR;
548251881Speter
549251881Speter  /* If the child and its parent are on the same branch, then the
550251881Speter     child will inherit the copy ID of its parent when made mutable.
551251881Speter     This is trivially detectable when the child and its parent have
552251881Speter     the same copy ID.  But that's not the sole indicator of
553251881Speter     same-branchness.  It might be the case that the parent was the
554251881Speter     result of a copy, but the child has not yet been cloned for
555251881Speter     mutability since that copy.  Detection of this latter case
556251881Speter     basically means making sure the copy IDs don't differ for some
557251881Speter     other reason, such as that the child was the direct target of the
558251881Speter     copy whose ID it has.  There is a special case here, too -- if
559251881Speter     the child's copy ID is the special ID "0", it can't have been the
560251881Speter     target of any copy, and therefore must be on the same branch as
561251881Speter     its parent.  */
562251881Speter  if ((strcmp(child_copy_id, "0") == 0)
563251881Speter      || (svn_fs_base__key_compare(child_copy_id, parent_copy_id) == 0))
564251881Speter    {
565251881Speter      *inherit_p = copy_id_inherit_parent;
566251881Speter      return SVN_NO_ERROR;
567251881Speter    }
568251881Speter  else
569251881Speter    {
570251881Speter      copy_t *copy;
571251881Speter      SVN_ERR(svn_fs_bdb__get_copy(&copy, fs, child_copy_id, trail, pool));
572251881Speter      if (svn_fs_base__id_compare(copy->dst_noderev_id, child_id) == -1)
573251881Speter        {
574251881Speter          *inherit_p = copy_id_inherit_parent;
575251881Speter          return SVN_NO_ERROR;
576251881Speter        }
577251881Speter    }
578251881Speter
579251881Speter  /* If we get here, the child and its parent are not on speaking
580251881Speter     terms -- there will be no parental inheritance handed down in
581251881Speter     *this* generation. */
582251881Speter
583251881Speter  /* If the child was created at a different path than the one we are
584251881Speter     expecting its clone to live, one of its parents must have been
585251881Speter     created via a copy since the child was created.  The child isn't
586251881Speter     on the same branch as its parent (we caught those cases early);
587251881Speter     it can't keep its current copy ID because there's been an
588251881Speter     affecting copy (its clone won't be on the same branch as the
589251881Speter     child is).  That leaves only one course of action -- to assign
590251881Speter     the child a brand new "soft" copy ID. */
591251881Speter  id_path = svn_fs_base__dag_get_created_path(child->node);
592251881Speter  if (strcmp(id_path, parent_path_path(child, pool)) != 0)
593251881Speter    {
594251881Speter      *inherit_p = copy_id_inherit_new;
595251881Speter      *copy_src_path = id_path;
596251881Speter      return SVN_NO_ERROR;
597251881Speter    }
598251881Speter
599251881Speter  /* The node gets to keep its own ID. */
600251881Speter  return SVN_NO_ERROR;
601251881Speter}
602251881Speter
603251881Speter
604251881Speter/* Allocate a new parent_path_t node from POOL, referring to NODE,
605251881Speter   ENTRY, PARENT, and COPY_ID.  */
606251881Speterstatic parent_path_t *
607251881Spetermake_parent_path(dag_node_t *node,
608251881Speter                 char *entry,
609251881Speter                 parent_path_t *parent,
610251881Speter                 apr_pool_t *pool)
611251881Speter{
612251881Speter  parent_path_t *parent_path = apr_pcalloc(pool, sizeof(*parent_path));
613251881Speter  parent_path->node = node;
614251881Speter  parent_path->entry = entry;
615251881Speter  parent_path->parent = parent;
616251881Speter  parent_path->copy_inherit = copy_id_inherit_unknown;
617251881Speter  parent_path->copy_src_path = NULL;
618251881Speter  return parent_path;
619251881Speter}
620251881Speter
621251881Speter
622251881Speter/* Flags for open_path.  */
623251881Spetertypedef enum open_path_flags_t {
624251881Speter
625251881Speter  /* The last component of the PATH need not exist.  (All parent
626251881Speter     directories must exist, as usual.)  If the last component doesn't
627251881Speter     exist, simply leave the `node' member of the bottom parent_path
628251881Speter     component zero.  */
629251881Speter  open_path_last_optional = 1
630251881Speter
631251881Speter} open_path_flags_t;
632251881Speter
633251881Speter
634251881Speter/* Open the node identified by PATH in ROOT, as part of TRAIL.  Set
635251881Speter   *PARENT_PATH_P to a path from the node up to ROOT, allocated in
636251881Speter   TRAIL->pool.  The resulting *PARENT_PATH_P value is guaranteed to
637251881Speter   contain at least one element, for the root directory.
638251881Speter
639251881Speter   If resulting *PARENT_PATH_P will eventually be made mutable and
640251881Speter   modified, or if copy ID inheritance information is otherwise
641251881Speter   needed, TXN_ID should be the ID of the mutability transaction.  If
642251881Speter   TXN_ID is NULL, no copy ID in heritance information will be
643251881Speter   calculated for the *PARENT_PATH_P chain.
644251881Speter
645251881Speter   If FLAGS & open_path_last_optional is zero, return the error
646251881Speter   SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist.  If
647251881Speter   non-zero, require all the parent directories to exist as normal,
648251881Speter   but if the final path component doesn't exist, simply return a path
649251881Speter   whose bottom `node' member is zero.  This option is useful for
650251881Speter   callers that create new nodes --- we find the parent directory for
651251881Speter   them, and tell them whether the entry exists already.
652251881Speter
653251881Speter   NOTE: Public interfaces which only *read* from the filesystem
654251881Speter   should not call this function directly, but should instead use
655251881Speter   get_dag().
656251881Speter*/
657251881Speterstatic svn_error_t *
658251881Speteropen_path(parent_path_t **parent_path_p,
659251881Speter          svn_fs_root_t *root,
660251881Speter          const char *path,
661251881Speter          int flags,
662251881Speter          const char *txn_id,
663251881Speter          trail_t *trail,
664251881Speter          apr_pool_t *pool)
665251881Speter{
666251881Speter  svn_fs_t *fs = root->fs;
667251881Speter  dag_node_t *here; /* The directory we're currently looking at.  */
668251881Speter  parent_path_t *parent_path; /* The path from HERE up to the root.  */
669251881Speter  const char *rest; /* The portion of PATH we haven't traversed yet.  */
670251881Speter  const char *canon_path = svn_fs__canonicalize_abspath(path, pool);
671251881Speter  const char *path_so_far = "/";
672251881Speter
673251881Speter  /* Make a parent_path item for the root node, using its own current
674251881Speter     copy id.  */
675251881Speter  SVN_ERR(root_node(&here, root, trail, pool));
676251881Speter  parent_path = make_parent_path(here, 0, 0, pool);
677251881Speter  parent_path->copy_inherit = copy_id_inherit_self;
678251881Speter
679251881Speter  rest = canon_path + 1; /* skip the leading '/', it saves in iteration */
680251881Speter
681251881Speter  /* Whenever we are at the top of this loop:
682251881Speter     - HERE is our current directory,
683251881Speter     - ID is the node revision ID of HERE,
684251881Speter     - REST is the path we're going to find in HERE, and
685251881Speter     - PARENT_PATH includes HERE and all its parents.  */
686251881Speter  for (;;)
687251881Speter    {
688251881Speter      const char *next;
689251881Speter      char *entry;
690251881Speter      dag_node_t *child;
691251881Speter
692251881Speter      /* Parse out the next entry from the path.  */
693251881Speter      entry = svn_fs__next_entry_name(&next, rest, pool);
694251881Speter
695251881Speter      /* Calculate the path traversed thus far. */
696251881Speter      path_so_far = svn_fspath__join(path_so_far, entry, pool);
697251881Speter
698251881Speter      if (*entry == '\0')
699251881Speter        {
700251881Speter          /* Given the behavior of svn_fs__next_entry_name(), this
701251881Speter             happens when the path either starts or ends with a slash.
702251881Speter             In either case, we stay put: the current directory stays
703251881Speter             the same, and we add nothing to the parent path. */
704251881Speter          child = here;
705251881Speter        }
706251881Speter      else
707251881Speter        {
708251881Speter          copy_id_inherit_t inherit;
709251881Speter          const char *copy_path = NULL;
710251881Speter          svn_error_t *err = SVN_NO_ERROR;
711251881Speter          dag_node_t *cached_node;
712251881Speter
713251881Speter          /* If we found a directory entry, follow it.  First, we
714251881Speter             check our node cache, and, failing that, we hit the DAG
715251881Speter             layer. */
716251881Speter          cached_node = dag_node_cache_get(root, path_so_far, pool);
717251881Speter          if (cached_node)
718251881Speter            child = cached_node;
719251881Speter          else
720251881Speter            err = svn_fs_base__dag_open(&child, here, entry, trail, pool);
721251881Speter
722251881Speter          /* "file not found" requires special handling.  */
723251881Speter          if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
724251881Speter            {
725251881Speter              /* If this was the last path component, and the caller
726251881Speter                 said it was optional, then don't return an error;
727251881Speter                 just put a NULL node pointer in the path.  */
728251881Speter
729251881Speter              svn_error_clear(err);
730251881Speter
731251881Speter              if ((flags & open_path_last_optional)
732251881Speter                  && (! next || *next == '\0'))
733251881Speter                {
734251881Speter                  parent_path = make_parent_path(NULL, entry, parent_path,
735251881Speter                                                 pool);
736251881Speter                  break;
737251881Speter                }
738251881Speter              else
739251881Speter                {
740251881Speter                  /* Build a better error message than svn_fs_base__dag_open
741251881Speter                     can provide, giving the root and full path name.  */
742251881Speter                  return SVN_FS__NOT_FOUND(root, path);
743251881Speter                }
744251881Speter            }
745251881Speter
746251881Speter          /* Other errors we return normally.  */
747251881Speter          SVN_ERR(err);
748251881Speter
749251881Speter          /* Now, make a parent_path item for CHILD. */
750251881Speter          parent_path = make_parent_path(child, entry, parent_path, pool);
751251881Speter          if (txn_id)
752251881Speter            {
753251881Speter              SVN_ERR(get_copy_inheritance(&inherit, &copy_path,
754251881Speter                                           fs, parent_path, txn_id,
755251881Speter                                           trail, pool));
756251881Speter              parent_path->copy_inherit = inherit;
757251881Speter              parent_path->copy_src_path = apr_pstrdup(pool, copy_path);
758251881Speter            }
759251881Speter
760251881Speter          /* Cache the node we found (if it wasn't already cached). */
761251881Speter          if (! cached_node)
762251881Speter            dag_node_cache_set(root, path_so_far, child);
763251881Speter        }
764251881Speter
765251881Speter      /* Are we finished traversing the path?  */
766251881Speter      if (! next)
767251881Speter        break;
768251881Speter
769251881Speter      /* The path isn't finished yet; we'd better be in a directory.  */
770251881Speter      if (svn_fs_base__dag_node_kind(child) != svn_node_dir)
771251881Speter        SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far),
772251881Speter                  apr_psprintf(pool, _("Failure opening '%s'"), path));
773251881Speter
774251881Speter      rest = next;
775251881Speter      here = child;
776251881Speter    }
777251881Speter
778251881Speter  *parent_path_p = parent_path;
779251881Speter  return SVN_NO_ERROR;
780251881Speter}
781251881Speter
782251881Speter
783251881Speter/* Make the node referred to by PARENT_PATH mutable, if it isn't
784251881Speter   already, as part of TRAIL.  ROOT must be the root from which
785251881Speter   PARENT_PATH descends.  Clone any parent directories as needed.
786251881Speter   Adjust the dag nodes in PARENT_PATH to refer to the clones.  Use
787251881Speter   ERROR_PATH in error messages.  */
788251881Speterstatic svn_error_t *
789251881Spetermake_path_mutable(svn_fs_root_t *root,
790251881Speter                  parent_path_t *parent_path,
791251881Speter                  const char *error_path,
792251881Speter                  trail_t *trail,
793251881Speter                  apr_pool_t *pool)
794251881Speter{
795251881Speter  dag_node_t *cloned_node;
796251881Speter  const char *txn_id = root->txn;
797251881Speter  svn_fs_t *fs = root->fs;
798251881Speter
799251881Speter  /* Is the node mutable already?  */
800251881Speter  if (svn_fs_base__dag_check_mutable(parent_path->node, txn_id))
801251881Speter    return SVN_NO_ERROR;
802251881Speter
803251881Speter  /* Are we trying to clone the root, or somebody's child node?  */
804251881Speter  if (parent_path->parent)
805251881Speter    {
806251881Speter      const svn_fs_id_t *parent_id;
807251881Speter      const svn_fs_id_t *node_id = svn_fs_base__dag_get_id(parent_path->node);
808251881Speter      const char *copy_id = NULL;
809251881Speter      const char *copy_src_path = parent_path->copy_src_path;
810251881Speter      copy_id_inherit_t inherit = parent_path->copy_inherit;
811251881Speter      const char *clone_path;
812251881Speter
813251881Speter      /* We're trying to clone somebody's child.  Make sure our parent
814251881Speter         is mutable.  */
815251881Speter      SVN_ERR(make_path_mutable(root, parent_path->parent,
816251881Speter                                error_path, trail, pool));
817251881Speter
818251881Speter      switch (inherit)
819251881Speter        {
820251881Speter        case copy_id_inherit_parent:
821251881Speter          parent_id = svn_fs_base__dag_get_id(parent_path->parent->node);
822251881Speter          copy_id = svn_fs_base__id_copy_id(parent_id);
823251881Speter          break;
824251881Speter
825251881Speter        case copy_id_inherit_new:
826251881Speter          SVN_ERR(svn_fs_bdb__reserve_copy_id(&copy_id, fs, trail, pool));
827251881Speter          break;
828251881Speter
829251881Speter        case copy_id_inherit_self:
830251881Speter          copy_id = NULL;
831251881Speter          break;
832251881Speter
833251881Speter        case copy_id_inherit_unknown:
834251881Speter        default:
835251881Speter          SVN_ERR_MALFUNCTION(); /* uh-oh -- somebody didn't calculate copy-ID
836251881Speter                      inheritance data. */
837251881Speter        }
838251881Speter
839251881Speter      /* Now make this node mutable.  */
840251881Speter      clone_path = parent_path_path(parent_path->parent, pool);
841251881Speter      SVN_ERR(svn_fs_base__dag_clone_child(&cloned_node,
842251881Speter                                           parent_path->parent->node,
843251881Speter                                           clone_path,
844251881Speter                                           parent_path->entry,
845251881Speter                                           copy_id, txn_id,
846251881Speter                                           trail, pool));
847251881Speter
848251881Speter      /* If we just created a brand new copy ID, we need to store a
849251881Speter         `copies' table entry for it, as well as a notation in the
850251881Speter         transaction that should this transaction be terminated, our
851251881Speter         new copy needs to be removed. */
852251881Speter      if (inherit == copy_id_inherit_new)
853251881Speter        {
854251881Speter          const svn_fs_id_t *new_node_id =
855251881Speter            svn_fs_base__dag_get_id(cloned_node);
856251881Speter          SVN_ERR(svn_fs_bdb__create_copy(fs, copy_id, copy_src_path,
857251881Speter                                          svn_fs_base__id_txn_id(node_id),
858251881Speter                                          new_node_id,
859251881Speter                                          copy_kind_soft, trail, pool));
860251881Speter          SVN_ERR(svn_fs_base__add_txn_copy(fs, txn_id, copy_id,
861251881Speter                                            trail, pool));
862251881Speter        }
863251881Speter    }
864251881Speter  else
865251881Speter    {
866251881Speter      /* We're trying to clone the root directory.  */
867251881Speter      SVN_ERR(mutable_root_node(&cloned_node, root, error_path, trail, pool));
868251881Speter    }
869251881Speter
870251881Speter  /* Update the PARENT_PATH link to refer to the clone.  */
871251881Speter  parent_path->node = cloned_node;
872251881Speter
873251881Speter  return SVN_NO_ERROR;
874251881Speter}
875251881Speter
876251881Speter
877251881Speter/* Walk up PARENT_PATH to the root of the tree, adjusting each node's
878251881Speter   mergeinfo count by COUNT_DELTA as part of Subversion transaction
879251881Speter   TXN_ID and TRAIL.  Use POOL for allocations. */
880251881Speterstatic svn_error_t *
881251881Speteradjust_parent_mergeinfo_counts(parent_path_t *parent_path,
882251881Speter                               apr_int64_t count_delta,
883251881Speter                               const char *txn_id,
884251881Speter                               trail_t *trail,
885251881Speter                               apr_pool_t *pool)
886251881Speter{
887251881Speter  apr_pool_t *iterpool;
888251881Speter  parent_path_t *pp = parent_path;
889251881Speter
890251881Speter  if (count_delta == 0)
891251881Speter    return SVN_NO_ERROR;
892251881Speter
893251881Speter  iterpool = svn_pool_create(pool);
894251881Speter
895251881Speter  while (pp)
896251881Speter    {
897251881Speter      svn_pool_clear(iterpool);
898251881Speter      SVN_ERR(svn_fs_base__dag_adjust_mergeinfo_count(pp->node, count_delta,
899251881Speter                                                      txn_id, trail,
900251881Speter                                                      iterpool));
901251881Speter      pp = pp->parent;
902251881Speter    }
903251881Speter  svn_pool_destroy(iterpool);
904251881Speter
905251881Speter  return SVN_NO_ERROR;
906251881Speter}
907251881Speter
908251881Speter
909251881Speter/* Open the node identified by PATH in ROOT, as part of TRAIL.  Set
910251881Speter   *DAG_NODE_P to the node we find, allocated in TRAIL->pool.  Return
911251881Speter   the error SVN_ERR_FS_NOT_FOUND if this node doesn't exist. */
912251881Speterstatic svn_error_t *
913251881Speterget_dag(dag_node_t **dag_node_p,
914251881Speter        svn_fs_root_t *root,
915251881Speter        const char *path,
916251881Speter        trail_t *trail,
917251881Speter        apr_pool_t *pool)
918251881Speter{
919251881Speter  parent_path_t *parent_path;
920251881Speter  dag_node_t *node = NULL;
921251881Speter
922251881Speter  /* Canonicalize the input PATH. */
923251881Speter  path = svn_fs__canonicalize_abspath(path, pool);
924251881Speter
925251881Speter  /* If ROOT is a revision root, we'll look for the DAG in our cache. */
926251881Speter  node = dag_node_cache_get(root, path, pool);
927251881Speter  if (! node)
928251881Speter    {
929251881Speter      /* Call open_path with no flags, as we want this to return an error
930251881Speter         if the node for which we are searching doesn't exist. */
931251881Speter      SVN_ERR(open_path(&parent_path, root, path, 0, NULL, trail, pool));
932251881Speter      node = parent_path->node;
933251881Speter
934251881Speter      /* No need to cache our find -- open_path() will do that for us. */
935251881Speter    }
936251881Speter
937251881Speter  *dag_node_p = node;
938251881Speter  return SVN_NO_ERROR;
939251881Speter}
940251881Speter
941251881Speter
942251881Speter
943251881Speter/* Populating the `changes' table. */
944251881Speter
945251881Speter/* Add a change to the changes table in FS, keyed on transaction id
946251881Speter   TXN_ID, and indicated that a change of kind CHANGE_KIND occurred on
947251881Speter   PATH (whose node revision id is--or was, in the case of a
948251881Speter   deletion--NODEREV_ID), and optionally that TEXT_MODs or PROP_MODs
949251881Speter   occurred.  Do all this as part of TRAIL.  */
950251881Speterstatic svn_error_t *
951251881Speteradd_change(svn_fs_t *fs,
952251881Speter           const char *txn_id,
953251881Speter           const char *path,
954251881Speter           const svn_fs_id_t *noderev_id,
955251881Speter           svn_fs_path_change_kind_t change_kind,
956251881Speter           svn_boolean_t text_mod,
957251881Speter           svn_boolean_t prop_mod,
958251881Speter           trail_t *trail,
959251881Speter           apr_pool_t *pool)
960251881Speter{
961251881Speter  change_t change;
962251881Speter  change.path = svn_fs__canonicalize_abspath(path, pool);
963251881Speter  change.noderev_id = noderev_id;
964251881Speter  change.kind = change_kind;
965251881Speter  change.text_mod = text_mod;
966251881Speter  change.prop_mod = prop_mod;
967251881Speter  return svn_fs_bdb__changes_add(fs, txn_id, &change, trail, pool);
968251881Speter}
969251881Speter
970251881Speter
971251881Speter
972251881Speter/* Generic node operations.  */
973251881Speter
974251881Speter
975251881Speterstruct node_id_args {
976251881Speter  const svn_fs_id_t **id_p;
977251881Speter  svn_fs_root_t *root;
978251881Speter  const char *path;
979251881Speter};
980251881Speter
981251881Speter
982251881Speterstatic svn_error_t *
983251881Spetertxn_body_node_id(void *baton, trail_t *trail)
984251881Speter{
985251881Speter  struct node_id_args *args = baton;
986251881Speter  dag_node_t *node;
987251881Speter
988251881Speter  SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
989251881Speter  *args->id_p = svn_fs_base__id_copy(svn_fs_base__dag_get_id(node),
990251881Speter                                     trail->pool);
991251881Speter
992251881Speter  return SVN_NO_ERROR;
993251881Speter}
994251881Speter
995251881Speter
996251881Speterstatic svn_error_t *
997251881Speterbase_node_id(const svn_fs_id_t **id_p,
998251881Speter             svn_fs_root_t *root,
999251881Speter             const char *path,
1000251881Speter             apr_pool_t *pool)
1001251881Speter{
1002251881Speter  base_root_data_t *brd = root->fsap_data;
1003251881Speter
1004251881Speter  if (! root->is_txn_root
1005251881Speter      && (path[0] == '\0' || ((path[0] == '/') && (path[1] == '\0'))))
1006251881Speter    {
1007251881Speter      /* Optimize the case where we don't need any db access at all.
1008251881Speter         The root directory ("" or "/") node is stored in the
1009251881Speter         svn_fs_root_t object, and never changes when it's a revision
1010251881Speter         root, so we can just reach in and grab it directly. */
1011251881Speter      *id_p = svn_fs_base__id_copy(svn_fs_base__dag_get_id(brd->root_dir),
1012251881Speter                                   pool);
1013251881Speter    }
1014251881Speter  else
1015251881Speter    {
1016251881Speter      const svn_fs_id_t *id;
1017251881Speter      struct node_id_args args;
1018251881Speter
1019251881Speter      args.id_p = &id;
1020251881Speter      args.root = root;
1021251881Speter      args.path = path;
1022251881Speter
1023251881Speter      SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_id, &args,
1024251881Speter                                     FALSE, pool));
1025251881Speter      *id_p = id;
1026251881Speter    }
1027251881Speter  return SVN_NO_ERROR;
1028251881Speter}
1029251881Speter
1030251881Speter
1031251881Speterstruct node_created_rev_args {
1032251881Speter  svn_revnum_t revision;
1033251881Speter  svn_fs_root_t *root;
1034251881Speter  const char *path;
1035251881Speter};
1036251881Speter
1037251881Speter
1038251881Speterstatic svn_error_t *
1039251881Spetertxn_body_node_created_rev(void *baton, trail_t *trail)
1040251881Speter{
1041251881Speter  struct node_created_rev_args *args = baton;
1042251881Speter  dag_node_t *node;
1043251881Speter
1044251881Speter  SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1045251881Speter  return svn_fs_base__dag_get_revision(&(args->revision), node,
1046251881Speter                                       trail, trail->pool);
1047251881Speter}
1048251881Speter
1049251881Speter
1050251881Speterstatic svn_error_t *
1051251881Speterbase_node_created_rev(svn_revnum_t *revision,
1052251881Speter                      svn_fs_root_t *root,
1053251881Speter                      const char *path,
1054251881Speter                      apr_pool_t *pool)
1055251881Speter{
1056251881Speter  struct node_created_rev_args args;
1057251881Speter
1058251881Speter  args.revision = SVN_INVALID_REVNUM;
1059251881Speter  args.root = root;
1060251881Speter  args.path = path;
1061251881Speter  SVN_ERR(svn_fs_base__retry_txn
1062251881Speter          (root->fs, txn_body_node_created_rev, &args, TRUE, pool));
1063251881Speter  *revision = args.revision;
1064251881Speter  return SVN_NO_ERROR;
1065251881Speter}
1066251881Speter
1067251881Speter
1068251881Speterstruct node_created_path_args {
1069251881Speter  const char **created_path;
1070251881Speter  svn_fs_root_t *root;
1071251881Speter  const char *path;
1072251881Speter};
1073251881Speter
1074251881Speter
1075251881Speterstatic svn_error_t *
1076251881Spetertxn_body_node_created_path(void *baton, trail_t *trail)
1077251881Speter{
1078251881Speter  struct node_created_path_args *args = baton;
1079251881Speter  dag_node_t *node;
1080251881Speter
1081251881Speter  SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1082251881Speter  *args->created_path = svn_fs_base__dag_get_created_path(node);
1083251881Speter  return SVN_NO_ERROR;
1084251881Speter}
1085251881Speter
1086251881Speter
1087251881Speterstatic svn_error_t *
1088251881Speterbase_node_created_path(const char **created_path,
1089251881Speter                       svn_fs_root_t *root,
1090251881Speter                       const char *path,
1091251881Speter                       apr_pool_t *pool)
1092251881Speter{
1093251881Speter  struct node_created_path_args args;
1094251881Speter  apr_pool_t *scratch_pool = svn_pool_create(pool);
1095251881Speter
1096251881Speter  args.created_path = created_path;
1097251881Speter  args.root = root;
1098251881Speter  args.path = path;
1099251881Speter
1100251881Speter  SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_created_path, &args,
1101251881Speter                                 FALSE, scratch_pool));
1102251881Speter  if (*created_path)
1103251881Speter    *created_path = apr_pstrdup(pool, *created_path);
1104251881Speter  svn_pool_destroy(scratch_pool);
1105251881Speter  return SVN_NO_ERROR;
1106251881Speter}
1107251881Speter
1108251881Speter
1109251881Speterstruct node_kind_args {
1110251881Speter  const svn_fs_id_t *id;
1111251881Speter  svn_node_kind_t kind; /* OUT parameter */
1112251881Speter};
1113251881Speter
1114251881Speter
1115251881Speterstatic svn_error_t *
1116251881Spetertxn_body_node_kind(void *baton, trail_t *trail)
1117251881Speter{
1118251881Speter  struct node_kind_args *args = baton;
1119251881Speter  dag_node_t *node;
1120251881Speter
1121251881Speter  SVN_ERR(svn_fs_base__dag_get_node(&node, trail->fs, args->id,
1122251881Speter                                    trail, trail->pool));
1123251881Speter  args->kind = svn_fs_base__dag_node_kind(node);
1124251881Speter
1125251881Speter  return SVN_NO_ERROR;
1126251881Speter}
1127251881Speter
1128251881Speter
1129251881Speterstatic svn_error_t *
1130251881Speternode_kind(svn_node_kind_t *kind_p,
1131251881Speter          svn_fs_root_t *root,
1132251881Speter          const char *path,
1133251881Speter          apr_pool_t *pool)
1134251881Speter{
1135251881Speter  struct node_kind_args args;
1136251881Speter  const svn_fs_id_t *node_id;
1137251881Speter
1138251881Speter  /* Get the node id. */
1139251881Speter  SVN_ERR(base_node_id(&node_id, root, path, pool));
1140251881Speter
1141251881Speter  /* Use the node id to get the real kind. */
1142251881Speter  args.id = node_id;
1143251881Speter  SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_kind, &args,
1144251881Speter                                 TRUE, pool));
1145251881Speter
1146251881Speter  *kind_p = args.kind;
1147251881Speter  return SVN_NO_ERROR;
1148251881Speter}
1149251881Speter
1150251881Speter
1151251881Speterstatic svn_error_t *
1152251881Speterbase_check_path(svn_node_kind_t *kind_p,
1153251881Speter                svn_fs_root_t *root,
1154251881Speter                const char *path,
1155251881Speter                apr_pool_t *pool)
1156251881Speter{
1157251881Speter  svn_error_t *err = node_kind(kind_p, root, path, pool);
1158251881Speter  if (err &&
1159251881Speter      ((err->apr_err == SVN_ERR_FS_NOT_FOUND)
1160251881Speter       || (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY)))
1161251881Speter    {
1162251881Speter      svn_error_clear(err);
1163251881Speter      err = SVN_NO_ERROR;
1164251881Speter      *kind_p = svn_node_none;
1165251881Speter    }
1166251881Speter
1167251881Speter  return svn_error_trace(err);
1168251881Speter}
1169251881Speter
1170251881Speter
1171251881Speterstruct node_prop_args
1172251881Speter{
1173251881Speter  svn_string_t **value_p;
1174251881Speter  svn_fs_root_t *root;
1175251881Speter  const char *path;
1176251881Speter  const char *propname;
1177251881Speter};
1178251881Speter
1179251881Speter
1180251881Speterstatic svn_error_t *
1181251881Spetertxn_body_node_prop(void *baton,
1182251881Speter                   trail_t *trail)
1183251881Speter{
1184251881Speter  struct node_prop_args *args = baton;
1185251881Speter  dag_node_t *node;
1186251881Speter  apr_hash_t *proplist;
1187251881Speter
1188251881Speter  SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1189251881Speter  SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, node,
1190251881Speter                                        trail, trail->pool));
1191251881Speter  *(args->value_p) = NULL;
1192251881Speter  if (proplist)
1193251881Speter    *(args->value_p) = svn_hash_gets(proplist, args->propname);
1194251881Speter  return SVN_NO_ERROR;
1195251881Speter}
1196251881Speter
1197251881Speter
1198251881Speterstatic svn_error_t *
1199251881Speterbase_node_prop(svn_string_t **value_p,
1200251881Speter               svn_fs_root_t *root,
1201251881Speter               const char *path,
1202251881Speter               const char *propname,
1203251881Speter               apr_pool_t *pool)
1204251881Speter{
1205251881Speter  struct node_prop_args args;
1206251881Speter  svn_string_t *value;
1207251881Speter  apr_pool_t *scratch_pool = svn_pool_create(pool);
1208251881Speter
1209251881Speter  args.value_p  = &value;
1210251881Speter  args.root     = root;
1211251881Speter  args.path     = path;
1212251881Speter  args.propname = propname;
1213251881Speter  SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_prop, &args,
1214251881Speter                                 FALSE, scratch_pool));
1215251881Speter  *value_p = value ? svn_string_dup(value, pool) : NULL;
1216251881Speter  svn_pool_destroy(scratch_pool);
1217251881Speter  return SVN_NO_ERROR;
1218251881Speter}
1219251881Speter
1220251881Speter
1221251881Speterstruct node_proplist_args {
1222251881Speter  apr_hash_t **table_p;
1223251881Speter  svn_fs_root_t *root;
1224251881Speter  const char *path;
1225251881Speter};
1226251881Speter
1227251881Speter
1228251881Speterstatic svn_error_t *
1229251881Spetertxn_body_node_proplist(void *baton, trail_t *trail)
1230251881Speter{
1231251881Speter  struct node_proplist_args *args = baton;
1232251881Speter  dag_node_t *node;
1233251881Speter  apr_hash_t *proplist;
1234251881Speter
1235251881Speter  SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1236251881Speter  SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, node,
1237251881Speter                                        trail, trail->pool));
1238251881Speter  *args->table_p = proplist ? proplist : apr_hash_make(trail->pool);
1239251881Speter  return SVN_NO_ERROR;
1240251881Speter}
1241251881Speter
1242251881Speter
1243251881Speterstatic svn_error_t *
1244251881Speterbase_node_proplist(apr_hash_t **table_p,
1245251881Speter                   svn_fs_root_t *root,
1246251881Speter                   const char *path,
1247251881Speter                   apr_pool_t *pool)
1248251881Speter{
1249251881Speter  apr_hash_t *table;
1250251881Speter  struct node_proplist_args args;
1251251881Speter
1252251881Speter  args.table_p = &table;
1253251881Speter  args.root = root;
1254251881Speter  args.path = path;
1255251881Speter
1256251881Speter  SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_proplist, &args,
1257251881Speter                                 FALSE, pool));
1258251881Speter
1259251881Speter  *table_p = table;
1260251881Speter  return SVN_NO_ERROR;
1261251881Speter}
1262251881Speter
1263251881Speter
1264251881Speterstruct change_node_prop_args {
1265251881Speter  svn_fs_root_t *root;
1266251881Speter  const char *path;
1267251881Speter  const char *name;
1268251881Speter  const svn_string_t *value;
1269251881Speter};
1270251881Speter
1271251881Speter
1272251881Speterstatic svn_error_t *
1273251881Spetertxn_body_change_node_prop(void *baton,
1274251881Speter                          trail_t *trail)
1275251881Speter{
1276251881Speter  struct change_node_prop_args *args = baton;
1277251881Speter  parent_path_t *parent_path;
1278251881Speter  apr_hash_t *proplist;
1279251881Speter  const char *txn_id = args->root->txn;
1280251881Speter  base_fs_data_t *bfd = trail->fs->fsap_data;
1281251881Speter
1282251881Speter  SVN_ERR(open_path(&parent_path, args->root, args->path, 0, txn_id,
1283251881Speter                    trail, trail->pool));
1284251881Speter
1285251881Speter  /* Check to see if path is locked; if so, check that we can use it.
1286251881Speter     Notice that we're doing this non-recursively, regardless of node kind. */
1287251881Speter  if (args->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
1288251881Speter    SVN_ERR(svn_fs_base__allow_locked_operation
1289251881Speter            (args->path, FALSE, trail, trail->pool));
1290251881Speter
1291251881Speter  SVN_ERR(make_path_mutable(args->root, parent_path, args->path,
1292251881Speter                            trail, trail->pool));
1293251881Speter  SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, parent_path->node,
1294251881Speter                                        trail, trail->pool));
1295251881Speter
1296251881Speter  /* If there's no proplist, but we're just deleting a property, exit now. */
1297251881Speter  if ((! proplist) && (! args->value))
1298251881Speter    return SVN_NO_ERROR;
1299251881Speter
1300251881Speter  /* Now, if there's no proplist, we know we need to make one. */
1301251881Speter  if (! proplist)
1302251881Speter    proplist = apr_hash_make(trail->pool);
1303251881Speter
1304251881Speter  /* Set the property. */
1305251881Speter  svn_hash_sets(proplist, args->name, args->value);
1306251881Speter
1307251881Speter  /* Overwrite the node's proplist. */
1308251881Speter  SVN_ERR(svn_fs_base__dag_set_proplist(parent_path->node, proplist,
1309251881Speter                                        txn_id, trail, trail->pool));
1310251881Speter
1311251881Speter  /* If this was a change to the mergeinfo property, and our version
1312251881Speter     of the filesystem cares, we have some extra recording to do.
1313251881Speter
1314251881Speter     ### If the format *doesn't* support mergeinfo recording, should
1315251881Speter     ### we fuss about attempts to change the svn:mergeinfo property
1316251881Speter     ### in any way save to delete it?  */
1317251881Speter  if ((bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
1318251881Speter      && (strcmp(args->name, SVN_PROP_MERGEINFO) == 0))
1319251881Speter    {
1320251881Speter      svn_boolean_t had_mergeinfo, has_mergeinfo = args->value != NULL;
1321251881Speter
1322251881Speter      /* First, note on our node that it has mergeinfo. */
1323251881Speter      SVN_ERR(svn_fs_base__dag_set_has_mergeinfo(parent_path->node,
1324251881Speter                                                 has_mergeinfo,
1325251881Speter                                                 &had_mergeinfo, txn_id,
1326251881Speter                                                 trail, trail->pool));
1327251881Speter
1328251881Speter      /* If this is a change from the old state, we need to update our
1329251881Speter         node's parents' mergeinfo counts by a factor of 1. */
1330251881Speter      if (parent_path->parent && ((! had_mergeinfo) != (! has_mergeinfo)))
1331251881Speter        SVN_ERR(adjust_parent_mergeinfo_counts(parent_path->parent,
1332251881Speter                                               has_mergeinfo ? 1 : -1,
1333251881Speter                                               txn_id, trail, trail->pool));
1334251881Speter    }
1335251881Speter
1336251881Speter  /* Make a record of this modification in the changes table. */
1337251881Speter  return add_change(args->root->fs, txn_id,
1338251881Speter                    args->path, svn_fs_base__dag_get_id(parent_path->node),
1339251881Speter                    svn_fs_path_change_modify, FALSE, TRUE, trail,
1340251881Speter                    trail->pool);
1341251881Speter}
1342251881Speter
1343251881Speter
1344251881Speterstatic svn_error_t *
1345251881Speterbase_change_node_prop(svn_fs_root_t *root,
1346251881Speter                      const char *path,
1347251881Speter                      const char *name,
1348251881Speter                      const svn_string_t *value,
1349251881Speter                      apr_pool_t *pool)
1350251881Speter{
1351251881Speter  struct change_node_prop_args args;
1352251881Speter
1353251881Speter  if (! root->is_txn_root)
1354251881Speter    return SVN_FS__NOT_TXN(root);
1355251881Speter
1356251881Speter  args.root  = root;
1357251881Speter  args.path  = path;
1358251881Speter  args.name  = name;
1359251881Speter  args.value = value;
1360251881Speter  return svn_fs_base__retry_txn(root->fs, txn_body_change_node_prop, &args,
1361251881Speter                                TRUE, pool);
1362251881Speter}
1363251881Speter
1364251881Speter
1365251881Speterstruct things_changed_args
1366251881Speter{
1367251881Speter  svn_boolean_t *changed_p;
1368251881Speter  svn_fs_root_t *root1;
1369251881Speter  svn_fs_root_t *root2;
1370251881Speter  const char *path1;
1371251881Speter  const char *path2;
1372251881Speter  apr_pool_t *pool;
1373251881Speter};
1374251881Speter
1375251881Speter
1376251881Speterstatic svn_error_t *
1377251881Spetertxn_body_props_changed(void *baton, trail_t *trail)
1378251881Speter{
1379251881Speter  struct things_changed_args *args = baton;
1380251881Speter  dag_node_t *node1, *node2;
1381251881Speter
1382251881Speter  SVN_ERR(get_dag(&node1, args->root1, args->path1, trail, trail->pool));
1383251881Speter  SVN_ERR(get_dag(&node2, args->root2, args->path2, trail, trail->pool));
1384251881Speter  return svn_fs_base__things_different(args->changed_p, NULL,
1385251881Speter                                       node1, node2, trail, trail->pool);
1386251881Speter}
1387251881Speter
1388251881Speter
1389251881Speterstatic svn_error_t *
1390251881Speterbase_props_changed(svn_boolean_t *changed_p,
1391251881Speter                   svn_fs_root_t *root1,
1392251881Speter                   const char *path1,
1393251881Speter                   svn_fs_root_t *root2,
1394251881Speter                   const char *path2,
1395251881Speter                   apr_pool_t *pool)
1396251881Speter{
1397251881Speter  struct things_changed_args args;
1398251881Speter
1399251881Speter  /* Check that roots are in the same fs. */
1400251881Speter  if (root1->fs != root2->fs)
1401251881Speter    return svn_error_create
1402251881Speter      (SVN_ERR_FS_GENERAL, NULL,
1403251881Speter       _("Cannot compare property value between two different filesystems"));
1404251881Speter
1405251881Speter  args.root1      = root1;
1406251881Speter  args.root2      = root2;
1407251881Speter  args.path1      = path1;
1408251881Speter  args.path2      = path2;
1409251881Speter  args.changed_p  = changed_p;
1410251881Speter  args.pool       = pool;
1411251881Speter
1412251881Speter  return svn_fs_base__retry_txn(root1->fs, txn_body_props_changed, &args,
1413251881Speter                                TRUE, pool);
1414251881Speter}
1415251881Speter
1416251881Speter
1417251881Speter
1418251881Speter/* Miscellaneous table handling */
1419251881Speter
1420251881Speterstruct miscellaneous_set_args
1421251881Speter{
1422251881Speter  const char *key;
1423251881Speter  const char *val;
1424251881Speter};
1425251881Speter
1426251881Speterstatic svn_error_t *
1427251881Spetertxn_body_miscellaneous_set(void *baton, trail_t *trail)
1428251881Speter{
1429251881Speter  struct miscellaneous_set_args *msa = baton;
1430251881Speter
1431251881Speter  return svn_fs_bdb__miscellaneous_set(trail->fs, msa->key, msa->val, trail,
1432251881Speter                                       trail->pool);
1433251881Speter}
1434251881Speter
1435251881Spetersvn_error_t *
1436251881Spetersvn_fs_base__miscellaneous_set(svn_fs_t *fs,
1437251881Speter                               const char *key,
1438251881Speter                               const char *val,
1439251881Speter                               apr_pool_t *pool)
1440251881Speter{
1441251881Speter  struct miscellaneous_set_args msa;
1442251881Speter  msa.key = key;
1443251881Speter  msa.val = val;
1444251881Speter
1445251881Speter  return svn_fs_base__retry_txn(fs, txn_body_miscellaneous_set, &msa,
1446251881Speter                                TRUE, pool);
1447251881Speter}
1448251881Speter
1449251881Speterstruct miscellaneous_get_args
1450251881Speter{
1451251881Speter  const char *key;
1452251881Speter  const char **val;
1453251881Speter};
1454251881Speter
1455251881Speterstatic svn_error_t *
1456251881Spetertxn_body_miscellaneous_get(void *baton, trail_t *trail)
1457251881Speter{
1458251881Speter  struct miscellaneous_get_args *mga = baton;
1459251881Speter  return svn_fs_bdb__miscellaneous_get(mga->val, trail->fs, mga->key, trail,
1460251881Speter                                       trail->pool);
1461251881Speter}
1462251881Speter
1463251881Spetersvn_error_t *
1464251881Spetersvn_fs_base__miscellaneous_get(const char **val,
1465251881Speter                               svn_fs_t *fs,
1466251881Speter                               const char *key,
1467251881Speter                               apr_pool_t *pool)
1468251881Speter{
1469251881Speter  struct miscellaneous_get_args mga;
1470251881Speter  apr_pool_t *scratch_pool = svn_pool_create(pool);
1471251881Speter
1472251881Speter  mga.key = key;
1473251881Speter  mga.val = val;
1474251881Speter  SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_miscellaneous_get, &mga,
1475251881Speter                                 FALSE, scratch_pool));
1476251881Speter  if (*val)
1477251881Speter    *val = apr_pstrdup(pool, *val);
1478251881Speter  svn_pool_destroy(scratch_pool);
1479251881Speter  return SVN_NO_ERROR;
1480251881Speter}
1481251881Speter
1482251881Speter
1483251881Speter
1484251881Speter/* Getting a directory's entries */
1485251881Speter
1486251881Speter
1487251881Speterstruct dir_entries_args
1488251881Speter{
1489251881Speter  apr_hash_t **table_p;
1490251881Speter  svn_fs_root_t *root;
1491251881Speter  const char *path;
1492251881Speter};
1493251881Speter
1494251881Speter
1495251881Speter/* *(BATON->table_p) will never be NULL on successful return */
1496251881Speterstatic svn_error_t *
1497251881Spetertxn_body_dir_entries(void *baton,
1498251881Speter                     trail_t *trail)
1499251881Speter{
1500251881Speter  struct dir_entries_args *args = baton;
1501251881Speter  dag_node_t *node;
1502251881Speter  apr_hash_t *entries;
1503251881Speter
1504251881Speter  SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1505251881Speter
1506251881Speter  /* Get the entries for PARENT_PATH. */
1507251881Speter  SVN_ERR(svn_fs_base__dag_dir_entries(&entries, node, trail, trail->pool));
1508251881Speter
1509251881Speter  /* Potentially initialize the return value to an empty hash. */
1510251881Speter  *args->table_p = entries ? entries : apr_hash_make(trail->pool);
1511251881Speter  return SVN_NO_ERROR;
1512251881Speter}
1513251881Speter
1514251881Speter
1515251881Speterstatic svn_error_t *
1516251881Speterbase_dir_entries(apr_hash_t **table_p,
1517251881Speter                 svn_fs_root_t *root,
1518251881Speter                 const char *path,
1519251881Speter                 apr_pool_t *pool)
1520251881Speter{
1521251881Speter  struct dir_entries_args args;
1522251881Speter  apr_pool_t *iterpool;
1523251881Speter  apr_hash_t *table;
1524251881Speter  svn_fs_t *fs = root->fs;
1525251881Speter  apr_hash_index_t *hi;
1526251881Speter
1527251881Speter  args.table_p = &table;
1528251881Speter  args.root    = root;
1529251881Speter  args.path    = path;
1530251881Speter  SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_dir_entries, &args,
1531251881Speter                                 FALSE, pool));
1532251881Speter
1533251881Speter  iterpool = svn_pool_create(pool);
1534251881Speter
1535251881Speter  /* Add in the kind data. */
1536251881Speter  for (hi = apr_hash_first(pool, table); hi; hi = apr_hash_next(hi))
1537251881Speter    {
1538251881Speter      svn_fs_dirent_t *entry;
1539251881Speter      struct node_kind_args nk_args;
1540251881Speter      void *val;
1541251881Speter
1542251881Speter      svn_pool_clear(iterpool);
1543251881Speter
1544251881Speter      /* KEY will be the entry name in ancestor (about which we
1545251881Speter         simply don't care), VAL the dirent. */
1546251881Speter      apr_hash_this(hi, NULL, NULL, &val);
1547251881Speter      entry = val;
1548251881Speter      nk_args.id = entry->id;
1549251881Speter
1550251881Speter      /* We don't need to have the retry function destroy the trail
1551251881Speter         pool because we're already doing that via the use of an
1552251881Speter         iteration pool. */
1553251881Speter      SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_node_kind, &nk_args,
1554251881Speter                                     FALSE, iterpool));
1555251881Speter      entry->kind = nk_args.kind;
1556251881Speter    }
1557251881Speter
1558251881Speter  svn_pool_destroy(iterpool);
1559251881Speter
1560251881Speter  *table_p = table;
1561251881Speter  return SVN_NO_ERROR;
1562251881Speter}
1563251881Speter
1564251881Speter
1565251881Speter
1566251881Speter/* Merges and commits. */
1567251881Speter
1568251881Speter
1569251881Speterstruct deltify_committed_args
1570251881Speter{
1571251881Speter  svn_fs_t *fs; /* the filesystem */
1572251881Speter  svn_revnum_t rev; /* revision just committed */
1573251881Speter  const char *txn_id; /* transaction just committed */
1574251881Speter};
1575251881Speter
1576251881Speter
1577251881Speterstruct txn_deltify_args
1578251881Speter{
1579251881Speter  /* The transaction ID whose nodes are being deltified. */
1580251881Speter  const char *txn_id;
1581251881Speter
1582251881Speter  /* The target is what we're deltifying. */
1583251881Speter  const svn_fs_id_t *tgt_id;
1584251881Speter
1585251881Speter  /* The base is what we're deltifying against.  It's not necessarily
1586251881Speter     the "next" revision of the node; skip deltas mean we sometimes
1587251881Speter     deltify against a successor many generations away.  This may be
1588251881Speter     NULL, in which case we'll avoid deltification and simply index
1589251881Speter     TGT_ID's data checksum. */
1590251881Speter  const svn_fs_id_t *base_id;
1591251881Speter
1592251881Speter  /* We only deltify props for directories.
1593251881Speter     ### Didn't we try removing this horrid little optimization once?
1594251881Speter     ### What was the result?  I would have thought that skip deltas
1595251881Speter     ### mean directory undeltification is cheap enough now. */
1596251881Speter  svn_boolean_t is_dir;
1597251881Speter};
1598251881Speter
1599251881Speter
1600251881Speterstatic svn_error_t *
1601251881Spetertxn_body_txn_deltify(void *baton, trail_t *trail)
1602251881Speter{
1603251881Speter  struct txn_deltify_args *args = baton;
1604251881Speter  dag_node_t *tgt_node, *base_node;
1605251881Speter  base_fs_data_t *bfd = trail->fs->fsap_data;
1606251881Speter
1607251881Speter  SVN_ERR(svn_fs_base__dag_get_node(&tgt_node, trail->fs, args->tgt_id,
1608251881Speter                                    trail, trail->pool));
1609251881Speter  /* If we have something to deltify against, do so. */
1610251881Speter  if (args->base_id)
1611251881Speter    {
1612251881Speter      SVN_ERR(svn_fs_base__dag_get_node(&base_node, trail->fs, args->base_id,
1613251881Speter                                        trail, trail->pool));
1614251881Speter      SVN_ERR(svn_fs_base__dag_deltify(tgt_node, base_node, args->is_dir,
1615251881Speter                                       args->txn_id, trail, trail->pool));
1616251881Speter    }
1617251881Speter
1618251881Speter  /* If we support rep sharing, and this isn't a directory, record a
1619251881Speter     mapping of TGT_NODE's data checksum to its representation key. */
1620251881Speter  if (bfd->format >= SVN_FS_BASE__MIN_REP_SHARING_FORMAT)
1621251881Speter    SVN_ERR(svn_fs_base__dag_index_checksums(tgt_node, trail, trail->pool));
1622251881Speter
1623251881Speter  return SVN_NO_ERROR;
1624251881Speter}
1625251881Speter
1626251881Speter
1627251881Speterstruct txn_pred_count_args
1628251881Speter{
1629251881Speter  const svn_fs_id_t *id;
1630251881Speter  int pred_count;
1631251881Speter};
1632251881Speter
1633251881Speter
1634251881Speterstatic svn_error_t *
1635251881Spetertxn_body_pred_count(void *baton, trail_t *trail)
1636251881Speter{
1637251881Speter  node_revision_t *noderev;
1638251881Speter  struct txn_pred_count_args *args = baton;
1639251881Speter
1640251881Speter  SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, trail->fs,
1641251881Speter                                        args->id, trail, trail->pool));
1642251881Speter  args->pred_count = noderev->predecessor_count;
1643251881Speter  return SVN_NO_ERROR;
1644251881Speter}
1645251881Speter
1646251881Speter
1647251881Speterstruct txn_pred_id_args
1648251881Speter{
1649251881Speter  const svn_fs_id_t *id;      /* The node id whose predecessor we want. */
1650251881Speter  const svn_fs_id_t *pred_id; /* The returned predecessor id. */
1651251881Speter  apr_pool_t *pool;           /* The pool in which to allocate pred_id. */
1652251881Speter};
1653251881Speter
1654251881Speter
1655251881Speterstatic svn_error_t *
1656251881Spetertxn_body_pred_id(void *baton, trail_t *trail)
1657251881Speter{
1658251881Speter  node_revision_t *nr;
1659251881Speter  struct txn_pred_id_args *args = baton;
1660251881Speter
1661251881Speter  SVN_ERR(svn_fs_bdb__get_node_revision(&nr, trail->fs, args->id,
1662251881Speter                                        trail, trail->pool));
1663251881Speter  if (nr->predecessor_id)
1664251881Speter    args->pred_id = svn_fs_base__id_copy(nr->predecessor_id, args->pool);
1665251881Speter  else
1666251881Speter    args->pred_id = NULL;
1667251881Speter
1668251881Speter  return SVN_NO_ERROR;
1669251881Speter}
1670251881Speter
1671251881Speter
1672251881Speter/* Deltify PATH in ROOT's predecessor iff PATH is mutable under TXN_ID
1673251881Speter   in FS.  If PATH is a mutable directory, recurse.
1674251881Speter
1675251881Speter   NODE_ID is the node revision ID for PATH in ROOT, or NULL if that
1676251881Speter   value isn't known.  KIND is the node kind for PATH in ROOT, or
1677251881Speter   svn_node_unknown is the kind isn't known.
1678251881Speter
1679251881Speter   Use POOL for necessary allocations.  */
1680251881Speterstatic svn_error_t *
1681251881Speterdeltify_mutable(svn_fs_t *fs,
1682251881Speter                svn_fs_root_t *root,
1683251881Speter                const char *path,
1684251881Speter                const svn_fs_id_t *node_id,
1685251881Speter                svn_node_kind_t kind,
1686251881Speter                const char *txn_id,
1687251881Speter                apr_pool_t *pool)
1688251881Speter{
1689251881Speter  const svn_fs_id_t *id = node_id;
1690251881Speter  apr_hash_t *entries = NULL;
1691251881Speter  struct txn_deltify_args td_args;
1692251881Speter  base_fs_data_t *bfd = fs->fsap_data;
1693251881Speter
1694251881Speter  /* Get the ID for PATH under ROOT if it wasn't provided. */
1695251881Speter  if (! node_id)
1696251881Speter    SVN_ERR(base_node_id(&id, root, path, pool));
1697251881Speter
1698251881Speter  /* Check for mutability.  Not mutable?  Go no further.  This is safe
1699251881Speter     to do because for items in the tree to be mutable, their parent
1700251881Speter     dirs must also be mutable.  Therefore, if a directory is not
1701251881Speter     mutable under TXN_ID, its children cannot be.  */
1702251881Speter  if (strcmp(svn_fs_base__id_txn_id(id), txn_id))
1703251881Speter    return SVN_NO_ERROR;
1704251881Speter
1705251881Speter  /* Is this a directory?  */
1706251881Speter  if (kind == svn_node_unknown)
1707251881Speter    SVN_ERR(base_check_path(&kind, root, path, pool));
1708251881Speter
1709251881Speter  /* If this is a directory, read its entries.  */
1710251881Speter  if (kind == svn_node_dir)
1711251881Speter    SVN_ERR(base_dir_entries(&entries, root, path, pool));
1712251881Speter
1713251881Speter  /* If there are entries, recurse on 'em.  */
1714251881Speter  if (entries)
1715251881Speter    {
1716251881Speter      apr_pool_t *subpool = svn_pool_create(pool);
1717251881Speter      apr_hash_index_t *hi;
1718251881Speter
1719251881Speter      for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
1720251881Speter        {
1721251881Speter          /* KEY will be the entry name, VAL the dirent */
1722251881Speter          const void *key;
1723251881Speter          void *val;
1724251881Speter          svn_fs_dirent_t *entry;
1725251881Speter          svn_pool_clear(subpool);
1726251881Speter          apr_hash_this(hi, &key, NULL, &val);
1727251881Speter          entry = val;
1728251881Speter          SVN_ERR(deltify_mutable(fs, root,
1729251881Speter                                  svn_fspath__join(path, key, subpool),
1730251881Speter                                  entry->id, entry->kind, txn_id, subpool));
1731251881Speter        }
1732251881Speter
1733251881Speter      svn_pool_destroy(subpool);
1734251881Speter    }
1735251881Speter
1736251881Speter  /* Index ID's data checksum. */
1737251881Speter  td_args.txn_id = txn_id;
1738251881Speter  td_args.tgt_id = id;
1739251881Speter  td_args.base_id = NULL;
1740251881Speter  td_args.is_dir = (kind == svn_node_dir);
1741251881Speter  SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_deltify, &td_args,
1742251881Speter                                 TRUE, pool));
1743251881Speter
1744251881Speter  /* Finally, deltify old data against this node. */
1745251881Speter  {
1746251881Speter    /* Prior to 1.6, we use the following algorithm to deltify nodes:
1747251881Speter
1748251881Speter       Redeltify predecessor node-revisions of the one we added.  The
1749251881Speter       idea is to require at most 2*lg(N) deltas to be applied to get
1750251881Speter       to any node-revision in a chain of N predecessors.  We do this
1751251881Speter       using a technique derived from skip lists:
1752251881Speter
1753251881Speter          - Always redeltify the immediate parent
1754251881Speter
1755251881Speter          - If the number of predecessors is divisible by 2,
1756251881Speter              redeltify the revision two predecessors back
1757251881Speter
1758251881Speter          - If the number of predecessors is divisible by 4,
1759251881Speter              redeltify the revision four predecessors back
1760251881Speter
1761251881Speter       ... and so on.
1762251881Speter
1763251881Speter       That's the theory, anyway.  Unfortunately, if we strictly
1764251881Speter       follow that theory we get a bunch of overhead up front and no
1765251881Speter       great benefit until the number of predecessors gets large.  So,
1766251881Speter       stop at redeltifying the parent if the number of predecessors
1767251881Speter       is less than 32, and also skip the second level (redeltifying
1768251881Speter       two predecessors back), since that doesn't help much.  Also,
1769251881Speter       don't redeltify the oldest node-revision; it's potentially
1770251881Speter       expensive and doesn't help retrieve any other revision.
1771251881Speter       (Retrieving the oldest node-revision will still be fast, just
1772251881Speter       not as blindingly so.)
1773251881Speter
1774251881Speter       For 1.6 and beyond, we just deltify the current node against its
1775251881Speter       predecessors, using skip deltas similar to the way FSFS does.  */
1776251881Speter
1777251881Speter    int pred_count;
1778251881Speter    const svn_fs_id_t *pred_id;
1779251881Speter    struct txn_pred_count_args tpc_args;
1780251881Speter    apr_pool_t *subpools[2];
1781251881Speter    int active_subpool = 0;
1782251881Speter    svn_revnum_t forward_delta_rev = 0;
1783251881Speter
1784251881Speter    tpc_args.id = id;
1785251881Speter    SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_count, &tpc_args,
1786251881Speter                                   TRUE, pool));
1787251881Speter    pred_count = tpc_args.pred_count;
1788251881Speter
1789251881Speter    /* If nothing to deltify, then we're done. */
1790251881Speter    if (pred_count == 0)
1791251881Speter      return SVN_NO_ERROR;
1792251881Speter
1793251881Speter    subpools[0] = svn_pool_create(pool);
1794251881Speter    subpools[1] = svn_pool_create(pool);
1795251881Speter
1796251881Speter    /* If we support the 'miscellaneous' table, check it to see if
1797251881Speter       there is a point in time before which we don't want to do
1798251881Speter       deltification. */
1799251881Speter    /* ### FIXME:  I think this is an unnecessary restriction.  We
1800251881Speter       ### should be able to do something meaningful for most
1801251881Speter       ### deltification requests -- what that is depends on the
1802251881Speter       ### directory of the deltas for that revision, though. */
1803251881Speter    if (bfd->format >= SVN_FS_BASE__MIN_MISCELLANY_FORMAT)
1804251881Speter      {
1805251881Speter        const char *val;
1806251881Speter        SVN_ERR(svn_fs_base__miscellaneous_get
1807251881Speter                (&val, fs, SVN_FS_BASE__MISC_FORWARD_DELTA_UPGRADE, pool));
1808251881Speter        if (val)
1809251881Speter          SVN_ERR(svn_revnum_parse(&forward_delta_rev, val, NULL));
1810251881Speter      }
1811251881Speter
1812251881Speter    if (bfd->format >= SVN_FS_BASE__MIN_FORWARD_DELTAS_FORMAT
1813251881Speter          && forward_delta_rev <= root->rev)
1814251881Speter      {
1815251881Speter        /**** FORWARD DELTA STORAGE ****/
1816251881Speter
1817251881Speter        /* Decide which predecessor to deltify against.  Flip the rightmost '1'
1818251881Speter           bit of the predecessor count to determine which file rev (counting
1819251881Speter           from 0) we want to use.  (To see why count & (count - 1) unsets the
1820251881Speter           rightmost set bit, think about how you decrement a binary number. */
1821251881Speter        pred_count = pred_count & (pred_count - 1);
1822251881Speter
1823251881Speter        /* Walk back a number of predecessors equal to the difference between
1824251881Speter           pred_count and the original predecessor count.  (For example, if
1825251881Speter           the node has ten predecessors and we want the eighth node, walk back
1826251881Speter           two predecessors. */
1827251881Speter        pred_id = id;
1828251881Speter
1829251881Speter        /* We need to use two alternating pools because the id used in the
1830251881Speter           call to txn_body_pred_id is allocated by the previous inner
1831251881Speter           loop iteration.  If we would clear the pool each iteration we
1832251881Speter           would free the previous result.  */
1833251881Speter        while ((pred_count++) < tpc_args.pred_count)
1834251881Speter          {
1835251881Speter            struct txn_pred_id_args tpi_args;
1836251881Speter
1837251881Speter            active_subpool = !active_subpool;
1838251881Speter            svn_pool_clear(subpools[active_subpool]);
1839251881Speter
1840251881Speter            tpi_args.id = pred_id;
1841251881Speter            tpi_args.pool = subpools[active_subpool];
1842251881Speter            SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_id, &tpi_args,
1843251881Speter                                           FALSE, subpools[active_subpool]));
1844251881Speter            pred_id = tpi_args.pred_id;
1845251881Speter
1846251881Speter            if (pred_id == NULL)
1847251881Speter              return svn_error_create
1848251881Speter                (SVN_ERR_FS_CORRUPT, 0,
1849251881Speter                 _("Corrupt DB: faulty predecessor count"));
1850251881Speter
1851251881Speter          }
1852251881Speter
1853251881Speter        /* Finally, do the deltification. */
1854251881Speter        td_args.txn_id = txn_id;
1855251881Speter        td_args.tgt_id = id;
1856251881Speter        td_args.base_id = pred_id;
1857251881Speter        td_args.is_dir = (kind == svn_node_dir);
1858251881Speter        SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_deltify, &td_args,
1859251881Speter                                       TRUE, subpools[active_subpool]));
1860251881Speter      }
1861251881Speter    else
1862251881Speter      {
1863251881Speter        int nlevels, lev, count;
1864251881Speter
1865251881Speter        /**** REVERSE DELTA STORAGE ****/
1866251881Speter
1867251881Speter        /* Decide how many predecessors to redeltify.  To save overhead,
1868251881Speter           don't redeltify anything but the immediate predecessor if there
1869251881Speter           are less than 32 predecessors. */
1870251881Speter        nlevels = 1;
1871251881Speter        if (pred_count >= 32)
1872251881Speter          {
1873251881Speter            while (pred_count % 2 == 0)
1874251881Speter              {
1875251881Speter                pred_count /= 2;
1876251881Speter                nlevels++;
1877251881Speter              }
1878251881Speter
1879251881Speter            /* Don't redeltify the oldest revision. */
1880251881Speter            if (1 << (nlevels - 1) == pred_count)
1881251881Speter              nlevels--;
1882251881Speter          }
1883251881Speter
1884251881Speter        /* Redeltify the desired number of predecessors. */
1885251881Speter        count = 0;
1886251881Speter        pred_id = id;
1887251881Speter
1888251881Speter        /* We need to use two alternating pools because the id used in the
1889251881Speter           call to txn_body_pred_id is allocated by the previous inner
1890251881Speter           loop iteration.  If we would clear the pool each iteration we
1891251881Speter           would free the previous result.  */
1892251881Speter        for (lev = 0; lev < nlevels; lev++)
1893251881Speter          {
1894251881Speter            /* To save overhead, skip the second level (that is, never
1895251881Speter               redeltify the node-revision two predecessors back). */
1896251881Speter            if (lev == 1)
1897251881Speter              continue;
1898251881Speter
1899251881Speter            /* Note that COUNT is not reset between levels, and neither is
1900251881Speter               PREDNODE; we just keep counting from where we were up to
1901251881Speter               where we're supposed to get. */
1902251881Speter            while (count < (1 << lev))
1903251881Speter              {
1904251881Speter                struct txn_pred_id_args tpi_args;
1905251881Speter
1906251881Speter                active_subpool = !active_subpool;
1907251881Speter                svn_pool_clear(subpools[active_subpool]);
1908251881Speter
1909251881Speter                tpi_args.id = pred_id;
1910251881Speter                tpi_args.pool = subpools[active_subpool];
1911251881Speter                SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_id,
1912251881Speter                                               &tpi_args, FALSE,
1913251881Speter                                               subpools[active_subpool]));
1914251881Speter                pred_id = tpi_args.pred_id;
1915251881Speter
1916251881Speter                if (pred_id == NULL)
1917251881Speter                  return svn_error_create
1918251881Speter                    (SVN_ERR_FS_CORRUPT, 0,
1919251881Speter                     _("Corrupt DB: faulty predecessor count"));
1920251881Speter
1921251881Speter                count++;
1922251881Speter              }
1923251881Speter
1924251881Speter            /* Finally, do the deltification. */
1925251881Speter            td_args.txn_id = NULL;  /* Don't require mutable reps */
1926251881Speter            td_args.tgt_id = pred_id;
1927251881Speter            td_args.base_id = id;
1928251881Speter            td_args.is_dir = (kind == svn_node_dir);
1929251881Speter            SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_deltify, &td_args,
1930251881Speter                                           TRUE, subpools[active_subpool]));
1931251881Speter
1932251881Speter          }
1933251881Speter      }
1934251881Speter
1935251881Speter    svn_pool_destroy(subpools[0]);
1936251881Speter    svn_pool_destroy(subpools[1]);
1937251881Speter  }
1938251881Speter
1939251881Speter  return SVN_NO_ERROR;
1940251881Speter}
1941251881Speter
1942251881Speter
1943251881Speterstruct get_root_args
1944251881Speter{
1945251881Speter  svn_fs_root_t *root;
1946251881Speter  dag_node_t *node;
1947251881Speter};
1948251881Speter
1949251881Speter
1950251881Speter/* Set ARGS->node to the root node of ARGS->root.  */
1951251881Speterstatic svn_error_t *
1952251881Spetertxn_body_get_root(void *baton, trail_t *trail)
1953251881Speter{
1954251881Speter  struct get_root_args *args = baton;
1955251881Speter  return get_dag(&(args->node), args->root, "", trail, trail->pool);
1956251881Speter}
1957251881Speter
1958251881Speter
1959251881Speter
1960251881Speterstatic svn_error_t *
1961251881Speterupdate_ancestry(svn_fs_t *fs,
1962251881Speter                const svn_fs_id_t *source_id,
1963251881Speter                const svn_fs_id_t *target_id,
1964251881Speter                const char *txn_id,
1965251881Speter                const char *target_path,
1966251881Speter                int source_pred_count,
1967251881Speter                trail_t *trail,
1968251881Speter                apr_pool_t *pool)
1969251881Speter{
1970251881Speter  node_revision_t *noderev;
1971251881Speter
1972251881Speter  /* Set target's predecessor-id to source_id.  */
1973251881Speter  if (strcmp(svn_fs_base__id_txn_id(target_id), txn_id))
1974251881Speter    return svn_error_createf
1975251881Speter      (SVN_ERR_FS_NOT_MUTABLE, NULL,
1976251881Speter       _("Unexpected immutable node at '%s'"), target_path);
1977251881Speter  SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, fs, target_id,
1978251881Speter                                        trail, pool));
1979251881Speter  noderev->predecessor_id = source_id;
1980251881Speter  noderev->predecessor_count = source_pred_count;
1981251881Speter  if (noderev->predecessor_count != -1)
1982251881Speter    noderev->predecessor_count++;
1983251881Speter  return svn_fs_bdb__put_node_revision(fs, target_id, noderev, trail, pool);
1984251881Speter}
1985251881Speter
1986251881Speter
1987251881Speter/* Set the contents of CONFLICT_PATH to PATH, and return an
1988251881Speter   SVN_ERR_FS_CONFLICT error that indicates that there was a conflict
1989251881Speter   at PATH.  Perform all allocations in POOL (except the allocation of
1990251881Speter   CONFLICT_PATH, which should be handled outside this function).  */
1991251881Speterstatic svn_error_t *
1992251881Speterconflict_err(svn_stringbuf_t *conflict_path,
1993251881Speter             const char *path)
1994251881Speter{
1995251881Speter  svn_stringbuf_set(conflict_path, path);
1996251881Speter  return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL,
1997251881Speter                           _("Conflict at '%s'"), path);
1998251881Speter}
1999251881Speter
2000251881Speter
2001251881Speter/* Merge changes between ANCESTOR and SOURCE into TARGET as part of
2002251881Speter * TRAIL.  ANCESTOR and TARGET must be distinct node revisions.
2003251881Speter * TARGET_PATH should correspond to TARGET's full path in its
2004251881Speter * filesystem, and is used for reporting conflict location.
2005251881Speter *
2006251881Speter * SOURCE, TARGET, and ANCESTOR are generally directories; this
2007251881Speter * function recursively merges the directories' contents.  If any are
2008251881Speter * files, this function simply returns an error whenever SOURCE,
2009251881Speter * TARGET, and ANCESTOR are all distinct node revisions.
2010251881Speter *
2011251881Speter * If there are differences between ANCESTOR and SOURCE that conflict
2012251881Speter * with changes between ANCESTOR and TARGET, this function returns an
2013251881Speter * SVN_ERR_FS_CONFLICT error, and updates CONFLICT_P to the name of the
2014251881Speter * conflicting node in TARGET, with TARGET_PATH prepended as a path.
2015251881Speter *
2016251881Speter * If there are no conflicting differences, CONFLICT_P is updated to
2017251881Speter * the empty string.
2018251881Speter *
2019251881Speter * CONFLICT_P must point to a valid svn_stringbuf_t.
2020251881Speter *
2021251881Speter * Do any necessary temporary allocation in POOL.
2022251881Speter */
2023251881Speterstatic svn_error_t *
2024251881Spetermerge(svn_stringbuf_t *conflict_p,
2025251881Speter      const char *target_path,
2026251881Speter      dag_node_t *target,
2027251881Speter      dag_node_t *source,
2028251881Speter      dag_node_t *ancestor,
2029251881Speter      const char *txn_id,
2030251881Speter      apr_int64_t *mergeinfo_increment_out,
2031251881Speter      trail_t *trail,
2032251881Speter      apr_pool_t *pool)
2033251881Speter{
2034251881Speter  const svn_fs_id_t *source_id, *target_id, *ancestor_id;
2035251881Speter  apr_hash_t *s_entries, *t_entries, *a_entries;
2036251881Speter  apr_hash_index_t *hi;
2037251881Speter  apr_pool_t *iterpool;
2038251881Speter  svn_fs_t *fs;
2039251881Speter  int pred_count;
2040251881Speter  apr_int64_t mergeinfo_increment = 0;
2041251881Speter  base_fs_data_t *bfd = trail->fs->fsap_data;
2042251881Speter
2043251881Speter  /* Make sure everyone comes from the same filesystem. */
2044251881Speter  fs = svn_fs_base__dag_get_fs(ancestor);
2045251881Speter  if ((fs != svn_fs_base__dag_get_fs(source))
2046251881Speter      || (fs != svn_fs_base__dag_get_fs(target)))
2047251881Speter    {
2048251881Speter      return svn_error_create
2049251881Speter        (SVN_ERR_FS_CORRUPT, NULL,
2050251881Speter         _("Bad merge; ancestor, source, and target not all in same fs"));
2051251881Speter    }
2052251881Speter
2053251881Speter  /* We have the same fs, now check it. */
2054251881Speter  SVN_ERR(svn_fs__check_fs(fs, TRUE));
2055251881Speter
2056251881Speter  source_id   = svn_fs_base__dag_get_id(source);
2057251881Speter  target_id   = svn_fs_base__dag_get_id(target);
2058251881Speter  ancestor_id = svn_fs_base__dag_get_id(ancestor);
2059251881Speter
2060251881Speter  /* It's improper to call this function with ancestor == target. */
2061251881Speter  if (svn_fs_base__id_eq(ancestor_id, target_id))
2062251881Speter    {
2063251881Speter      svn_string_t *id_str = svn_fs_base__id_unparse(target_id, pool);
2064251881Speter      return svn_error_createf
2065251881Speter        (SVN_ERR_FS_GENERAL, NULL,
2066251881Speter         _("Bad merge; target '%s' has id '%s', same as ancestor"),
2067251881Speter         target_path, id_str->data);
2068251881Speter    }
2069251881Speter
2070251881Speter  svn_stringbuf_setempty(conflict_p);
2071251881Speter
2072251881Speter  /* Base cases:
2073251881Speter   * Either no change made in source, or same change as made in target.
2074251881Speter   * Both mean nothing to merge here.
2075251881Speter   */
2076251881Speter  if (svn_fs_base__id_eq(ancestor_id, source_id)
2077251881Speter      || (svn_fs_base__id_eq(source_id, target_id)))
2078251881Speter    return SVN_NO_ERROR;
2079251881Speter
2080251881Speter  /* Else proceed, knowing all three are distinct node revisions.
2081251881Speter   *
2082251881Speter   * How to merge from this point:
2083251881Speter   *
2084251881Speter   * if (not all 3 are directories)
2085251881Speter   *   {
2086251881Speter   *     early exit with conflict;
2087251881Speter   *   }
2088251881Speter   *
2089251881Speter   * // Property changes may only be made to up-to-date
2090251881Speter   * // directories, because once the client commits the prop
2091251881Speter   * // change, it bumps the directory's revision, and therefore
2092251881Speter   * // must be able to depend on there being no other changes to
2093251881Speter   * // that directory in the repository.
2094251881Speter   * if (target's property list differs from ancestor's)
2095251881Speter   *    conflict;
2096251881Speter   *
2097251881Speter   * For each entry NAME in the directory ANCESTOR:
2098251881Speter   *
2099251881Speter   *   Let ANCESTOR-ENTRY, SOURCE-ENTRY, and TARGET-ENTRY be the IDs of
2100251881Speter   *   the name within ANCESTOR, SOURCE, and TARGET respectively.
2101251881Speter   *   (Possibly null if NAME does not exist in SOURCE or TARGET.)
2102251881Speter   *
2103251881Speter   *   If ANCESTOR-ENTRY == SOURCE-ENTRY, then:
2104251881Speter   *     No changes were made to this entry while the transaction was in
2105251881Speter   *     progress, so do nothing to the target.
2106251881Speter   *
2107251881Speter   *   Else if ANCESTOR-ENTRY == TARGET-ENTRY, then:
2108251881Speter   *     A change was made to this entry while the transaction was in
2109251881Speter   *     process, but the transaction did not touch this entry.  Replace
2110251881Speter   *     TARGET-ENTRY with SOURCE-ENTRY.
2111251881Speter   *
2112251881Speter   *   Else:
2113251881Speter   *     Changes were made to this entry both within the transaction and
2114251881Speter   *     to the repository while the transaction was in progress.  They
2115251881Speter   *     must be merged or declared to be in conflict.
2116251881Speter   *
2117251881Speter   *     If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
2118251881Speter   *     double delete; flag a conflict.
2119251881Speter   *
2120251881Speter   *     If any of the three entries is of type file, declare a conflict.
2121251881Speter   *
2122251881Speter   *     If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
2123251881Speter   *     modification of ANCESTOR-ENTRY (determine by comparing the
2124251881Speter   *     node-id fields), declare a conflict.  A replacement is
2125251881Speter   *     incompatible with a modification or other replacement--even
2126251881Speter   *     an identical replacement.
2127251881Speter   *
2128251881Speter   *     Direct modifications were made to the directory ANCESTOR-ENTRY
2129251881Speter   *     in both SOURCE and TARGET.  Recursively merge these
2130251881Speter   *     modifications.
2131251881Speter   *
2132251881Speter   * For each leftover entry NAME in the directory SOURCE:
2133251881Speter   *
2134251881Speter   *   If NAME exists in TARGET, declare a conflict.  Even if SOURCE and
2135251881Speter   *   TARGET are adding exactly the same thing, two additions are not
2136251881Speter   *   auto-mergeable with each other.
2137251881Speter   *
2138251881Speter   *   Add NAME to TARGET with the entry from SOURCE.
2139251881Speter   *
2140251881Speter   * Now that we are done merging the changes from SOURCE into the
2141251881Speter   * directory TARGET, update TARGET's predecessor to be SOURCE.
2142251881Speter   */
2143251881Speter
2144251881Speter  if ((svn_fs_base__dag_node_kind(source) != svn_node_dir)
2145251881Speter      || (svn_fs_base__dag_node_kind(target) != svn_node_dir)
2146251881Speter      || (svn_fs_base__dag_node_kind(ancestor) != svn_node_dir))
2147251881Speter    {
2148251881Speter      return conflict_err(conflict_p, target_path);
2149251881Speter    }
2150251881Speter
2151251881Speter
2152251881Speter  /* Possible early merge failure: if target and ancestor have
2153251881Speter     different property lists, then the merge should fail.
2154251881Speter     Propchanges can *only* be committed on an up-to-date directory.
2155251881Speter     ### TODO: see issue #418 about the inelegance of this.
2156251881Speter
2157251881Speter     Another possible, similar, early merge failure: if source and
2158251881Speter     ancestor have different property lists (meaning someone else
2159251881Speter     changed directory properties while our commit transaction was
2160251881Speter     happening), the merge should fail.  See issue #2751.
2161251881Speter  */
2162251881Speter  {
2163251881Speter    node_revision_t *tgt_nr, *anc_nr, *src_nr;
2164251881Speter
2165251881Speter    /* Get node revisions for our id's. */
2166251881Speter    SVN_ERR(svn_fs_bdb__get_node_revision(&tgt_nr, fs, target_id,
2167251881Speter                                          trail, pool));
2168251881Speter    SVN_ERR(svn_fs_bdb__get_node_revision(&anc_nr, fs, ancestor_id,
2169251881Speter                                          trail, pool));
2170251881Speter    SVN_ERR(svn_fs_bdb__get_node_revision(&src_nr, fs, source_id,
2171251881Speter                                          trail, pool));
2172251881Speter
2173251881Speter    /* Now compare the prop-keys of the skels.  Note that just because
2174251881Speter       the keys are different -doesn't- mean the proplists have
2175251881Speter       different contents.  But merge() isn't concerned with contents;
2176251881Speter       it doesn't do a brute-force comparison on textual contents, so
2177251881Speter       it won't do that here either.  Checking to see if the propkey
2178251881Speter       atoms are `equal' is enough. */
2179251881Speter    if (! svn_fs_base__same_keys(tgt_nr->prop_key, anc_nr->prop_key))
2180251881Speter      return conflict_err(conflict_p, target_path);
2181251881Speter    if (! svn_fs_base__same_keys(src_nr->prop_key, anc_nr->prop_key))
2182251881Speter      return conflict_err(conflict_p, target_path);
2183251881Speter  }
2184251881Speter
2185251881Speter  /* ### todo: it would be more efficient to simply check for a NULL
2186251881Speter     entries hash where necessary below than to allocate an empty hash
2187251881Speter     here, but another day, another day... */
2188251881Speter  SVN_ERR(svn_fs_base__dag_dir_entries(&s_entries, source, trail, pool));
2189251881Speter  if (! s_entries)
2190251881Speter    s_entries = apr_hash_make(pool);
2191251881Speter  SVN_ERR(svn_fs_base__dag_dir_entries(&t_entries, target, trail, pool));
2192251881Speter  if (! t_entries)
2193251881Speter    t_entries = apr_hash_make(pool);
2194251881Speter  SVN_ERR(svn_fs_base__dag_dir_entries(&a_entries, ancestor, trail, pool));
2195251881Speter  if (! a_entries)
2196251881Speter    a_entries = apr_hash_make(pool);
2197251881Speter
2198251881Speter  /* for each entry E in a_entries... */
2199251881Speter  iterpool = svn_pool_create(pool);
2200251881Speter  for (hi = apr_hash_first(pool, a_entries);
2201251881Speter       hi;
2202251881Speter       hi = apr_hash_next(hi))
2203251881Speter    {
2204251881Speter      svn_fs_dirent_t *s_entry, *t_entry, *a_entry;
2205251881Speter
2206251881Speter      const void *key;
2207251881Speter      void *val;
2208251881Speter      apr_ssize_t klen;
2209251881Speter
2210251881Speter      svn_pool_clear(iterpool);
2211251881Speter
2212251881Speter      /* KEY will be the entry name in ancestor, VAL the dirent */
2213251881Speter      apr_hash_this(hi, &key, &klen, &val);
2214251881Speter      a_entry = val;
2215251881Speter
2216251881Speter      s_entry = apr_hash_get(s_entries, key, klen);
2217251881Speter      t_entry = apr_hash_get(t_entries, key, klen);
2218251881Speter
2219251881Speter      /* No changes were made to this entry while the transaction was
2220251881Speter         in progress, so do nothing to the target. */
2221251881Speter      if (s_entry && svn_fs_base__id_eq(a_entry->id, s_entry->id))
2222251881Speter        goto end;
2223251881Speter
2224251881Speter      /* A change was made to this entry while the transaction was in
2225251881Speter         process, but the transaction did not touch this entry. */
2226251881Speter      else if (t_entry && svn_fs_base__id_eq(a_entry->id, t_entry->id))
2227251881Speter        {
2228251881Speter          dag_node_t *t_ent_node;
2229251881Speter          apr_int64_t mergeinfo_start;
2230251881Speter          SVN_ERR(svn_fs_base__dag_get_node(&t_ent_node, fs,
2231251881Speter                                            t_entry->id, trail, iterpool));
2232251881Speter          SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, &mergeinfo_start,
2233251881Speter                                                       t_ent_node, trail,
2234251881Speter                                                       iterpool));
2235251881Speter          mergeinfo_increment -= mergeinfo_start;
2236251881Speter
2237251881Speter           if (s_entry)
2238251881Speter             {
2239251881Speter              dag_node_t *s_ent_node;
2240251881Speter              apr_int64_t mergeinfo_end;
2241251881Speter              SVN_ERR(svn_fs_base__dag_get_node(&s_ent_node, fs,
2242251881Speter                                                s_entry->id, trail,
2243251881Speter                                                iterpool));
2244251881Speter              SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL,
2245251881Speter                                                           &mergeinfo_end,
2246251881Speter                                                           s_ent_node, trail,
2247251881Speter                                                           iterpool));
2248251881Speter              mergeinfo_increment += mergeinfo_end;
2249251881Speter              SVN_ERR(svn_fs_base__dag_set_entry(target, key, s_entry->id,
2250251881Speter                                                 txn_id, trail, iterpool));
2251251881Speter            }
2252251881Speter          else
2253251881Speter            {
2254251881Speter              SVN_ERR(svn_fs_base__dag_delete(target, key, txn_id,
2255251881Speter                                              trail, iterpool));
2256251881Speter            }
2257251881Speter        }
2258251881Speter
2259251881Speter      /* Changes were made to this entry both within the transaction
2260251881Speter         and to the repository while the transaction was in progress.
2261251881Speter         They must be merged or declared to be in conflict. */
2262251881Speter      else
2263251881Speter        {
2264251881Speter          dag_node_t *s_ent_node, *t_ent_node, *a_ent_node;
2265251881Speter          const char *new_tpath;
2266251881Speter          apr_int64_t sub_mergeinfo_increment;
2267251881Speter
2268251881Speter          /* If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
2269251881Speter             double delete; if one of them is null, that's a delete versus
2270251881Speter             a modification. In any of these cases, flag a conflict. */
2271251881Speter          if (s_entry == NULL || t_entry == NULL)
2272251881Speter            return conflict_err(conflict_p,
2273251881Speter                                svn_fspath__join(target_path,
2274251881Speter                                                a_entry->name,
2275251881Speter                                                iterpool));
2276251881Speter
2277251881Speter          /* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
2278251881Speter             modification of ANCESTOR-ENTRY, declare a conflict. */
2279251881Speter          if (strcmp(svn_fs_base__id_node_id(s_entry->id),
2280251881Speter                     svn_fs_base__id_node_id(a_entry->id)) != 0
2281251881Speter              || strcmp(svn_fs_base__id_copy_id(s_entry->id),
2282251881Speter                        svn_fs_base__id_copy_id(a_entry->id)) != 0
2283251881Speter              || strcmp(svn_fs_base__id_node_id(t_entry->id),
2284251881Speter                        svn_fs_base__id_node_id(a_entry->id)) != 0
2285251881Speter              || strcmp(svn_fs_base__id_copy_id(t_entry->id),
2286251881Speter                        svn_fs_base__id_copy_id(a_entry->id)) != 0)
2287251881Speter            return conflict_err(conflict_p,
2288251881Speter                                svn_fspath__join(target_path,
2289251881Speter                                                a_entry->name,
2290251881Speter                                                iterpool));
2291251881Speter
2292251881Speter          /* Fetch the nodes for our entries. */
2293251881Speter          SVN_ERR(svn_fs_base__dag_get_node(&s_ent_node, fs,
2294251881Speter                                            s_entry->id, trail, iterpool));
2295251881Speter          SVN_ERR(svn_fs_base__dag_get_node(&t_ent_node, fs,
2296251881Speter                                            t_entry->id, trail, iterpool));
2297251881Speter          SVN_ERR(svn_fs_base__dag_get_node(&a_ent_node, fs,
2298251881Speter                                            a_entry->id, trail, iterpool));
2299251881Speter
2300251881Speter          /* If any of the three entries is of type file, flag a conflict. */
2301251881Speter          if ((svn_fs_base__dag_node_kind(s_ent_node) == svn_node_file)
2302251881Speter              || (svn_fs_base__dag_node_kind(t_ent_node) == svn_node_file)
2303251881Speter              || (svn_fs_base__dag_node_kind(a_ent_node) == svn_node_file))
2304251881Speter            return conflict_err(conflict_p,
2305251881Speter                                svn_fspath__join(target_path,
2306251881Speter                                                a_entry->name,
2307251881Speter                                                iterpool));
2308251881Speter
2309251881Speter          /* Direct modifications were made to the directory
2310251881Speter             ANCESTOR-ENTRY in both SOURCE and TARGET.  Recursively
2311251881Speter             merge these modifications. */
2312251881Speter          new_tpath = svn_fspath__join(target_path, t_entry->name, iterpool);
2313251881Speter          SVN_ERR(merge(conflict_p, new_tpath,
2314251881Speter                        t_ent_node, s_ent_node, a_ent_node,
2315251881Speter                        txn_id, &sub_mergeinfo_increment, trail, iterpool));
2316251881Speter          mergeinfo_increment += sub_mergeinfo_increment;
2317251881Speter        }
2318251881Speter
2319251881Speter      /* We've taken care of any possible implications E could have.
2320251881Speter         Remove it from source_entries, so it's easy later to loop
2321251881Speter         over all the source entries that didn't exist in
2322251881Speter         ancestor_entries. */
2323251881Speter    end:
2324251881Speter      apr_hash_set(s_entries, key, klen, NULL);
2325251881Speter    }
2326251881Speter
2327251881Speter  /* For each entry E in source but not in ancestor */
2328251881Speter  for (hi = apr_hash_first(pool, s_entries);
2329251881Speter       hi;
2330251881Speter       hi = apr_hash_next(hi))
2331251881Speter    {
2332251881Speter      svn_fs_dirent_t *s_entry, *t_entry;
2333251881Speter      const void *key;
2334251881Speter      void *val;
2335251881Speter      apr_ssize_t klen;
2336251881Speter      dag_node_t *s_ent_node;
2337251881Speter      apr_int64_t mergeinfo_s;
2338251881Speter
2339251881Speter      svn_pool_clear(iterpool);
2340251881Speter
2341251881Speter      apr_hash_this(hi, &key, &klen, &val);
2342251881Speter      s_entry = val;
2343251881Speter      t_entry = apr_hash_get(t_entries, key, klen);
2344251881Speter
2345251881Speter      /* If NAME exists in TARGET, declare a conflict. */
2346251881Speter      if (t_entry)
2347251881Speter        return conflict_err(conflict_p,
2348251881Speter                            svn_fspath__join(target_path,
2349251881Speter                                            t_entry->name,
2350251881Speter                                            iterpool));
2351251881Speter
2352251881Speter      SVN_ERR(svn_fs_base__dag_get_node(&s_ent_node, fs,
2353251881Speter                                        s_entry->id, trail, iterpool));
2354251881Speter      SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, &mergeinfo_s,
2355251881Speter                                                   s_ent_node, trail,
2356251881Speter                                                   iterpool));
2357251881Speter      mergeinfo_increment += mergeinfo_s;
2358251881Speter      SVN_ERR(svn_fs_base__dag_set_entry
2359251881Speter              (target, s_entry->name, s_entry->id, txn_id, trail, iterpool));
2360251881Speter    }
2361251881Speter  svn_pool_destroy(iterpool);
2362251881Speter
2363251881Speter  /* Now that TARGET has absorbed all of the history between ANCESTOR
2364251881Speter     and SOURCE, we can update its predecessor to point to SOURCE.  */
2365251881Speter  SVN_ERR(svn_fs_base__dag_get_predecessor_count(&pred_count, source,
2366251881Speter                                                 trail, pool));
2367251881Speter  SVN_ERR(update_ancestry(fs, source_id, target_id, txn_id, target_path,
2368251881Speter                          pred_count, trail, pool));
2369251881Speter
2370251881Speter  /* Tweak mergeinfo data if our format supports it. */
2371251881Speter  if (bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
2372251881Speter    {
2373251881Speter      SVN_ERR(svn_fs_base__dag_adjust_mergeinfo_count(target,
2374251881Speter                                                      mergeinfo_increment,
2375251881Speter                                                      txn_id, trail, pool));
2376251881Speter    }
2377251881Speter
2378251881Speter  if (mergeinfo_increment_out)
2379251881Speter    *mergeinfo_increment_out = mergeinfo_increment;
2380251881Speter
2381251881Speter  return SVN_NO_ERROR;
2382251881Speter}
2383251881Speter
2384251881Speter
2385251881Speterstruct merge_args
2386251881Speter{
2387251881Speter  /* The ancestor for the merge.  If this is null, then TXN's base is
2388251881Speter     used as the ancestor for the merge. */
2389251881Speter  dag_node_t *ancestor_node;
2390251881Speter
2391251881Speter  /* This is the SOURCE node for the merge.  It may not be null. */
2392251881Speter  dag_node_t *source_node;
2393251881Speter
2394251881Speter  /* This is the TARGET of the merge.  It may not be null.  If
2395251881Speter     ancestor_node above is null, then this txn's base is used as the
2396251881Speter     ancestor for the merge. */
2397251881Speter  svn_fs_txn_t *txn;
2398251881Speter
2399251881Speter  /* If a conflict results, this is updated to the path in the txn that
2400251881Speter     conflicted.  It must point to a valid svn_stringbuf_t before calling
2401251881Speter     svn_fs_base__retry_txn, as this determines the pool used to allocate any
2402251881Speter     required memory. */
2403251881Speter  svn_stringbuf_t *conflict;
2404251881Speter};
2405251881Speter
2406251881Speter
2407251881Speter/* Merge changes between an ancestor and BATON->source_node into
2408251881Speter   BATON->txn.  The ancestor is either BATON->ancestor_node, or if
2409251881Speter   that is null, BATON->txn's base node.
2410251881Speter
2411251881Speter   If the merge is successful, BATON->txn's base will become
2412251881Speter   BATON->source_node, and its root node will have a new ID, a
2413251881Speter   successor of BATON->source_node. */
2414251881Speterstatic svn_error_t *
2415251881Spetertxn_body_merge(void *baton, trail_t *trail)
2416251881Speter{
2417251881Speter  struct merge_args *args = baton;
2418251881Speter  dag_node_t *source_node, *txn_root_node, *ancestor_node;
2419251881Speter  const svn_fs_id_t *source_id;
2420251881Speter  svn_fs_t *fs = args->txn->fs;
2421251881Speter  const char *txn_id = args->txn->id;
2422251881Speter
2423251881Speter  source_node = args->source_node;
2424251881Speter  ancestor_node = args->ancestor_node;
2425251881Speter  source_id = svn_fs_base__dag_get_id(source_node);
2426251881Speter
2427251881Speter  SVN_ERR(svn_fs_base__dag_txn_root(&txn_root_node, fs, txn_id,
2428251881Speter                                    trail, trail->pool));
2429251881Speter
2430251881Speter  if (ancestor_node == NULL)
2431251881Speter    {
2432251881Speter      SVN_ERR(svn_fs_base__dag_txn_base_root(&ancestor_node, fs,
2433251881Speter                                             txn_id, trail, trail->pool));
2434251881Speter    }
2435251881Speter
2436251881Speter  if (svn_fs_base__id_eq(svn_fs_base__dag_get_id(ancestor_node),
2437251881Speter                         svn_fs_base__dag_get_id(txn_root_node)))
2438251881Speter    {
2439251881Speter      /* If no changes have been made in TXN since its current base,
2440251881Speter         then it can't conflict with any changes since that base.  So
2441251881Speter         we just set *both* its base and root to source, making TXN
2442251881Speter         in effect a repeat of source. */
2443251881Speter
2444251881Speter      /* ### kff todo: this would, of course, be a mighty silly thing
2445251881Speter         for the caller to do, and we might want to consider whether
2446251881Speter         this response is really appropriate. */
2447251881Speter
2448251881Speter      SVN_ERR(svn_fs_base__set_txn_base(fs, txn_id, source_id,
2449251881Speter                                        trail, trail->pool));
2450251881Speter      SVN_ERR(svn_fs_base__set_txn_root(fs, txn_id, source_id,
2451251881Speter                                        trail, trail->pool));
2452251881Speter    }
2453251881Speter  else
2454251881Speter    {
2455251881Speter      int pred_count;
2456251881Speter
2457251881Speter      SVN_ERR(merge(args->conflict, "/", txn_root_node, source_node,
2458251881Speter                    ancestor_node, txn_id, NULL, trail, trail->pool));
2459251881Speter
2460251881Speter      SVN_ERR(svn_fs_base__dag_get_predecessor_count(&pred_count,
2461251881Speter                                                     source_node, trail,
2462251881Speter                                                     trail->pool));
2463251881Speter
2464251881Speter      /* After the merge, txn's new "ancestor" is now really the node
2465251881Speter         at source_id, so record that fact.  Think of this as
2466251881Speter         ratcheting the txn forward in time, so it can't backslide and
2467251881Speter         forget the merging work that's already been done. */
2468251881Speter      SVN_ERR(update_ancestry(fs, source_id,
2469251881Speter                              svn_fs_base__dag_get_id(txn_root_node),
2470251881Speter                              txn_id, "/", pred_count, trail, trail->pool));
2471251881Speter      SVN_ERR(svn_fs_base__set_txn_base(fs, txn_id, source_id,
2472251881Speter                                        trail, trail->pool));
2473251881Speter    }
2474251881Speter
2475251881Speter  return SVN_NO_ERROR;
2476251881Speter}
2477251881Speter
2478251881Speter
2479251881Speter/* Verify that there are registered with TRAIL->fs all the locks
2480251881Speter   necessary to permit all the changes associated with TXN_NAME. */
2481251881Speterstatic svn_error_t *
2482251881Speterverify_locks(const char *txn_name,
2483251881Speter             trail_t *trail,
2484251881Speter             apr_pool_t *pool)
2485251881Speter{
2486251881Speter  apr_pool_t *subpool = svn_pool_create(pool);
2487251881Speter  apr_hash_t *changes;
2488251881Speter  apr_hash_index_t *hi;
2489251881Speter  apr_array_header_t *changed_paths;
2490251881Speter  svn_stringbuf_t *last_recursed = NULL;
2491251881Speter  int i;
2492251881Speter
2493251881Speter  /* Fetch the changes for this transaction. */
2494251881Speter  SVN_ERR(svn_fs_bdb__changes_fetch(&changes, trail->fs, txn_name,
2495251881Speter                                    trail, pool));
2496251881Speter
2497251881Speter  /* Make an array of the changed paths, and sort them depth-first-ily.  */
2498251881Speter  changed_paths = apr_array_make(pool, apr_hash_count(changes) + 1,
2499251881Speter                                 sizeof(const char *));
2500251881Speter  for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
2501251881Speter    {
2502251881Speter      const void *key;
2503251881Speter      apr_hash_this(hi, &key, NULL, NULL);
2504251881Speter      APR_ARRAY_PUSH(changed_paths, const char *) = key;
2505251881Speter    }
2506251881Speter  qsort(changed_paths->elts, changed_paths->nelts,
2507251881Speter        changed_paths->elt_size, svn_sort_compare_paths);
2508251881Speter
2509251881Speter  /* Now, traverse the array of changed paths, verify locks.  Note
2510251881Speter     that if we need to do a recursive verification a path, we'll skip
2511251881Speter     over children of that path when we get to them. */
2512251881Speter  for (i = 0; i < changed_paths->nelts; i++)
2513251881Speter    {
2514251881Speter      const char *path;
2515251881Speter      svn_fs_path_change2_t *change;
2516251881Speter      svn_boolean_t recurse = TRUE;
2517251881Speter
2518251881Speter      svn_pool_clear(subpool);
2519251881Speter      path = APR_ARRAY_IDX(changed_paths, i, const char *);
2520251881Speter
2521251881Speter      /* If this path has already been verified as part of a recursive
2522251881Speter         check of one of its parents, no need to do it again.  */
2523251881Speter      if (last_recursed
2524251881Speter          && svn_fspath__skip_ancestor(last_recursed->data, path))
2525251881Speter        continue;
2526251881Speter
2527251881Speter      /* Fetch the change associated with our path.  */
2528251881Speter      change = svn_hash_gets(changes, path);
2529251881Speter
2530251881Speter      /* What does it mean to succeed at lock verification for a given
2531251881Speter         path?  For an existing file or directory getting modified
2532251881Speter         (text, props), it means we hold the lock on the file or
2533251881Speter         directory.  For paths being added or removed, we need to hold
2534251881Speter         the locks for that path and any children of that path.
2535251881Speter
2536251881Speter         WHEW!  We have no reliable way to determine the node kind of
2537251881Speter         deleted items, but fortunately we are going to do a recursive
2538251881Speter         check on deleted paths regardless of their kind.  */
2539251881Speter      if (change->change_kind == svn_fs_path_change_modify)
2540251881Speter        recurse = FALSE;
2541251881Speter      SVN_ERR(svn_fs_base__allow_locked_operation(path, recurse,
2542251881Speter                                                  trail, subpool));
2543251881Speter
2544251881Speter      /* If we just did a recursive check, remember the path we
2545251881Speter         checked (so children can be skipped).  */
2546251881Speter      if (recurse)
2547251881Speter        {
2548251881Speter          if (! last_recursed)
2549251881Speter            last_recursed = svn_stringbuf_create(path, pool);
2550251881Speter          else
2551251881Speter            svn_stringbuf_set(last_recursed, path);
2552251881Speter        }
2553251881Speter    }
2554251881Speter  svn_pool_destroy(subpool);
2555251881Speter  return SVN_NO_ERROR;
2556251881Speter}
2557251881Speter
2558251881Speter
2559251881Speterstruct commit_args
2560251881Speter{
2561251881Speter  svn_fs_txn_t *txn;
2562251881Speter  svn_revnum_t new_rev;
2563251881Speter};
2564251881Speter
2565251881Speter
2566251881Speter/* Commit ARGS->txn, setting ARGS->new_rev to the resulting new
2567251881Speter * revision, if ARGS->txn is up-to-date with respect to the repository.
2568251881Speter *
2569251881Speter * Up-to-date means that ARGS->txn's base root is the same as the root
2570251881Speter * of the youngest revision.  If ARGS->txn is not up-to-date, the
2571251881Speter * error SVN_ERR_FS_TXN_OUT_OF_DATE is returned, and the commit fails: no
2572251881Speter * new revision is created, and ARGS->new_rev is not touched.
2573251881Speter *
2574251881Speter * If the commit succeeds, ARGS->txn is destroyed.
2575251881Speter */
2576251881Speterstatic svn_error_t *
2577251881Spetertxn_body_commit(void *baton, trail_t *trail)
2578251881Speter{
2579251881Speter  struct commit_args *args = baton;
2580251881Speter
2581251881Speter  svn_fs_txn_t *txn = args->txn;
2582251881Speter  svn_fs_t *fs = txn->fs;
2583251881Speter  const char *txn_name = txn->id;
2584251881Speter
2585251881Speter  svn_revnum_t youngest_rev;
2586251881Speter  const svn_fs_id_t *y_rev_root_id;
2587251881Speter  dag_node_t *txn_base_root_node;
2588251881Speter
2589251881Speter  /* Getting the youngest revision locks the revisions table until
2590251881Speter     this trail is done. */
2591251881Speter  SVN_ERR(svn_fs_bdb__youngest_rev(&youngest_rev, fs, trail, trail->pool));
2592251881Speter
2593251881Speter  /* If the root of the youngest revision is the same as txn's base,
2594251881Speter     then no further merging is necessary and we can commit. */
2595251881Speter  SVN_ERR(svn_fs_base__rev_get_root(&y_rev_root_id, fs, youngest_rev,
2596251881Speter                                    trail, trail->pool));
2597251881Speter  SVN_ERR(svn_fs_base__dag_txn_base_root(&txn_base_root_node, fs, txn_name,
2598251881Speter                                         trail, trail->pool));
2599251881Speter  /* ### kff todo: it seems weird to grab the ID for one, and the node
2600251881Speter     for the other.  We can certainly do the comparison we need, but
2601251881Speter     it would be nice to grab the same type of information from the
2602251881Speter     start, instead of having to transform one of them. */
2603251881Speter  if (! svn_fs_base__id_eq(y_rev_root_id,
2604251881Speter                           svn_fs_base__dag_get_id(txn_base_root_node)))
2605251881Speter    {
2606251881Speter      svn_string_t *id_str = svn_fs_base__id_unparse(y_rev_root_id,
2607251881Speter                                                     trail->pool);
2608251881Speter      return svn_error_createf
2609251881Speter        (SVN_ERR_FS_TXN_OUT_OF_DATE, NULL,
2610251881Speter         _("Transaction '%s' out-of-date with respect to revision '%s'"),
2611251881Speter         txn_name, id_str->data);
2612251881Speter    }
2613251881Speter
2614251881Speter  /* Locks may have been added (or stolen) between the calling of
2615251881Speter     previous svn_fs.h functions and svn_fs_commit_txn(), so we need
2616251881Speter     to re-examine every changed-path in the txn and re-verify all
2617251881Speter     discovered locks. */
2618251881Speter  SVN_ERR(verify_locks(txn_name, trail, trail->pool));
2619251881Speter
2620251881Speter  /* Else, commit the txn. */
2621251881Speter  return svn_fs_base__dag_commit_txn(&(args->new_rev), txn, trail,
2622251881Speter                                     trail->pool);
2623251881Speter}
2624251881Speter
2625251881Speter
2626251881Speter/* Note:  it is acceptable for this function to call back into
2627251881Speter   top-level FS interfaces because it does not itself use trails.  */
2628251881Spetersvn_error_t *
2629251881Spetersvn_fs_base__commit_txn(const char **conflict_p,
2630251881Speter                        svn_revnum_t *new_rev,
2631251881Speter                        svn_fs_txn_t *txn,
2632251881Speter                        apr_pool_t *pool)
2633251881Speter{
2634251881Speter  /* How do commits work in Subversion?
2635251881Speter   *
2636251881Speter   * When you're ready to commit, here's what you have:
2637251881Speter   *
2638251881Speter   *    1. A transaction, with a mutable tree hanging off it.
2639251881Speter   *    2. A base revision, against which TXN_TREE was made.
2640251881Speter   *    3. A latest revision, which may be newer than the base rev.
2641251881Speter   *
2642251881Speter   * The problem is that if latest != base, then one can't simply
2643251881Speter   * attach the txn root as the root of the new revision, because that
2644251881Speter   * would lose all the changes between base and latest.  It is also
2645251881Speter   * not acceptable to insist that base == latest; in a busy
2646251881Speter   * repository, commits happen too fast to insist that everyone keep
2647251881Speter   * their entire tree up-to-date at all times.  Non-overlapping
2648251881Speter   * changes should not interfere with each other.
2649251881Speter   *
2650251881Speter   * The solution is to merge the changes between base and latest into
2651251881Speter   * the txn tree [see the function merge()].  The txn tree is the
2652251881Speter   * only one of the three trees that is mutable, so it has to be the
2653251881Speter   * one to adjust.
2654251881Speter   *
2655251881Speter   * You might have to adjust it more than once, if a new latest
2656251881Speter   * revision gets committed while you were merging in the previous
2657251881Speter   * one.  For example:
2658251881Speter   *
2659251881Speter   *    1. Jane starts txn T, based at revision 6.
2660251881Speter   *    2. Someone commits (or already committed) revision 7.
2661251881Speter   *    3. Jane's starts merging the changes between 6 and 7 into T.
2662251881Speter   *    4. Meanwhile, someone commits revision 8.
2663251881Speter   *    5. Jane finishes the 6-->7 merge.  T could now be committed
2664251881Speter   *       against a latest revision of 7, if only that were still the
2665251881Speter   *       latest.  Unfortunately, 8 is now the latest, so...
2666251881Speter   *    6. Jane starts merging the changes between 7 and 8 into T.
2667251881Speter   *    7. Meanwhile, no one commits any new revisions.  Whew.
2668251881Speter   *    8. Jane commits T, creating revision 9, whose tree is exactly
2669251881Speter   *       T's tree, except immutable now.
2670251881Speter   *
2671251881Speter   * Lather, rinse, repeat.
2672251881Speter   */
2673251881Speter
2674251881Speter  svn_error_t *err;
2675251881Speter  svn_fs_t *fs = txn->fs;
2676251881Speter  apr_pool_t *subpool = svn_pool_create(pool);
2677251881Speter
2678251881Speter  /* Initialize output params. */
2679251881Speter  *new_rev = SVN_INVALID_REVNUM;
2680251881Speter  if (conflict_p)
2681251881Speter    *conflict_p = NULL;
2682251881Speter
2683251881Speter  while (1729)
2684251881Speter    {
2685251881Speter      struct get_root_args get_root_args;
2686251881Speter      struct merge_args merge_args;
2687251881Speter      struct commit_args commit_args;
2688251881Speter      svn_revnum_t youngish_rev;
2689251881Speter      svn_fs_root_t *youngish_root;
2690251881Speter      dag_node_t *youngish_root_node;
2691251881Speter
2692251881Speter      svn_pool_clear(subpool);
2693251881Speter
2694251881Speter      /* Get the *current* youngest revision, in one short-lived
2695251881Speter         Berkeley transaction.  (We don't want the revisions table
2696251881Speter         locked while we do the main merge.)  We call it "youngish"
2697251881Speter         because new revisions might get committed after we've
2698251881Speter         obtained it. */
2699251881Speter
2700251881Speter      SVN_ERR(svn_fs_base__youngest_rev(&youngish_rev, fs, subpool));
2701251881Speter      SVN_ERR(svn_fs_base__revision_root(&youngish_root, fs, youngish_rev,
2702251881Speter                                         subpool));
2703251881Speter
2704251881Speter      /* Get the dag node for the youngest revision, also in one
2705251881Speter         Berkeley transaction.  Later we'll use it as the SOURCE
2706251881Speter         argument to a merge, and if the merge succeeds, this youngest
2707251881Speter         root node will become the new base root for the svn txn that
2708251881Speter         was the target of the merge (but note that the youngest rev
2709251881Speter         may have changed by then -- that's why we're careful to get
2710251881Speter         this root in its own bdb txn here). */
2711251881Speter      get_root_args.root = youngish_root;
2712251881Speter      SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_root, &get_root_args,
2713251881Speter                                     FALSE, subpool));
2714251881Speter      youngish_root_node = get_root_args.node;
2715251881Speter
2716251881Speter      /* Try to merge.  If the merge succeeds, the base root node of
2717251881Speter         TARGET's txn will become the same as youngish_root_node, so
2718251881Speter         any future merges will only be between that node and whatever
2719251881Speter         the root node of the youngest rev is by then. */
2720251881Speter      merge_args.ancestor_node = NULL;
2721251881Speter      merge_args.source_node = youngish_root_node;
2722251881Speter      merge_args.txn = txn;
2723251881Speter      merge_args.conflict = svn_stringbuf_create_empty(pool); /* use pool */
2724251881Speter      err = svn_fs_base__retry_txn(fs, txn_body_merge, &merge_args,
2725251881Speter                                   FALSE, subpool);
2726251881Speter      if (err)
2727251881Speter        {
2728251881Speter          if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2729251881Speter            *conflict_p = merge_args.conflict->data;
2730251881Speter          return svn_error_trace(err);
2731251881Speter        }
2732251881Speter
2733251881Speter      /* Try to commit. */
2734251881Speter      commit_args.txn = txn;
2735251881Speter      err = svn_fs_base__retry_txn(fs, txn_body_commit, &commit_args,
2736251881Speter                                   FALSE, subpool);
2737251881Speter      if (err && (err->apr_err == SVN_ERR_FS_TXN_OUT_OF_DATE))
2738251881Speter        {
2739251881Speter          /* Did someone else finish committing a new revision while we
2740251881Speter             were in mid-merge or mid-commit?  If so, we'll need to
2741251881Speter             loop again to merge the new changes in, then try to
2742251881Speter             commit again.  Or if that's not what happened, then just
2743251881Speter             return the error. */
2744251881Speter          svn_revnum_t youngest_rev;
2745251881Speter          svn_error_t *err2 = svn_fs_base__youngest_rev(&youngest_rev, fs,
2746251881Speter                                                        subpool);
2747251881Speter          if (err2)
2748251881Speter            {
2749251881Speter              svn_error_clear(err);
2750251881Speter              return svn_error_trace(err2);  /* err2 is bad,
2751251881Speter                                                 it should not occur */
2752251881Speter            }
2753251881Speter          else if (youngest_rev == youngish_rev)
2754251881Speter            return svn_error_trace(err);
2755251881Speter          else
2756251881Speter            svn_error_clear(err);
2757251881Speter        }
2758251881Speter      else if (err)
2759251881Speter        {
2760251881Speter          return svn_error_trace(err);
2761251881Speter        }
2762251881Speter      else
2763251881Speter        {
2764251881Speter          /* Set the return value -- our brand spankin' new revision! */
2765251881Speter          *new_rev = commit_args.new_rev;
2766251881Speter          break;
2767251881Speter        }
2768251881Speter    }
2769251881Speter
2770251881Speter  svn_pool_destroy(subpool);
2771251881Speter  return SVN_NO_ERROR;
2772251881Speter}
2773251881Speter
2774251881Speter/* Note:  it is acceptable for this function to call back into
2775251881Speter   public FS API interfaces because it does not itself use trails.  */
2776251881Speterstatic svn_error_t *
2777251881Speterbase_merge(const char **conflict_p,
2778251881Speter           svn_fs_root_t *source_root,
2779251881Speter           const char *source_path,
2780251881Speter           svn_fs_root_t *target_root,
2781251881Speter           const char *target_path,
2782251881Speter           svn_fs_root_t *ancestor_root,
2783251881Speter           const char *ancestor_path,
2784251881Speter           apr_pool_t *pool)
2785251881Speter{
2786251881Speter  dag_node_t *source, *ancestor;
2787251881Speter  struct get_root_args get_root_args;
2788251881Speter  struct merge_args merge_args;
2789251881Speter  svn_fs_txn_t *txn;
2790251881Speter  svn_error_t *err;
2791251881Speter  svn_fs_t *fs;
2792251881Speter
2793251881Speter  if (! target_root->is_txn_root)
2794251881Speter    return SVN_FS__NOT_TXN(target_root);
2795251881Speter
2796251881Speter  /* Paranoia. */
2797251881Speter  fs = ancestor_root->fs;
2798251881Speter  if ((source_root->fs != fs) || (target_root->fs != fs))
2799251881Speter    {
2800251881Speter      return svn_error_create
2801251881Speter        (SVN_ERR_FS_CORRUPT, NULL,
2802251881Speter         _("Bad merge; ancestor, source, and target not all in same fs"));
2803251881Speter    }
2804251881Speter
2805251881Speter  /* ### kff todo: is there any compelling reason to get the nodes in
2806251881Speter     one db transaction?  Right now we don't; txn_body_get_root() gets
2807251881Speter     one node at a time.  This will probably need to change:
2808251881Speter
2809251881Speter     Jim Blandy <jimb@zwingli.cygnus.com> writes:
2810251881Speter     > svn_fs_merge needs to be a single transaction, to protect it against
2811251881Speter     > people deleting parents of nodes it's working on, etc.
2812251881Speter  */
2813251881Speter
2814251881Speter  /* Get the ancestor node. */
2815251881Speter  get_root_args.root = ancestor_root;
2816251881Speter  SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_root, &get_root_args,
2817251881Speter                                 FALSE, pool));
2818251881Speter  ancestor = get_root_args.node;
2819251881Speter
2820251881Speter  /* Get the source node. */
2821251881Speter  get_root_args.root = source_root;
2822251881Speter  SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_root, &get_root_args,
2823251881Speter                                 FALSE, pool));
2824251881Speter  source = get_root_args.node;
2825251881Speter
2826251881Speter  /* Open a txn for the txn root into which we're merging. */
2827251881Speter  SVN_ERR(svn_fs_base__open_txn(&txn, fs, target_root->txn, pool));
2828251881Speter
2829251881Speter  /* Merge changes between ANCESTOR and SOURCE into TXN. */
2830251881Speter  merge_args.source_node = source;
2831251881Speter  merge_args.ancestor_node = ancestor;
2832251881Speter  merge_args.txn = txn;
2833251881Speter  merge_args.conflict = svn_stringbuf_create_empty(pool);
2834251881Speter  err = svn_fs_base__retry_txn(fs, txn_body_merge, &merge_args, FALSE, pool);
2835251881Speter  if (err)
2836251881Speter    {
2837251881Speter      if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2838251881Speter        *conflict_p = merge_args.conflict->data;
2839251881Speter      return svn_error_trace(err);
2840251881Speter    }
2841251881Speter
2842251881Speter  return SVN_NO_ERROR;
2843251881Speter}
2844251881Speter
2845251881Speter
2846251881Speterstruct rev_get_txn_id_args
2847251881Speter{
2848251881Speter  const char **txn_id;
2849251881Speter  svn_revnum_t revision;
2850251881Speter};
2851251881Speter
2852251881Speter
2853251881Speterstatic svn_error_t *
2854251881Spetertxn_body_rev_get_txn_id(void *baton, trail_t *trail)
2855251881Speter{
2856251881Speter  struct rev_get_txn_id_args *args = baton;
2857251881Speter  return svn_fs_base__rev_get_txn_id(args->txn_id, trail->fs,
2858251881Speter                                     args->revision, trail, trail->pool);
2859251881Speter}
2860251881Speter
2861251881Speter
2862251881Spetersvn_error_t *
2863251881Spetersvn_fs_base__deltify(svn_fs_t *fs,
2864251881Speter                     svn_revnum_t revision,
2865251881Speter                     apr_pool_t *pool)
2866251881Speter{
2867251881Speter  svn_fs_root_t *root;
2868251881Speter  const char *txn_id;
2869251881Speter  struct rev_get_txn_id_args args;
2870251881Speter  base_fs_data_t *bfd = fs->fsap_data;
2871251881Speter
2872251881Speter  if (bfd->format >= SVN_FS_BASE__MIN_MISCELLANY_FORMAT)
2873251881Speter    {
2874251881Speter      const char *val;
2875251881Speter      svn_revnum_t forward_delta_rev = 0;
2876251881Speter
2877251881Speter      SVN_ERR(svn_fs_base__miscellaneous_get
2878251881Speter              (&val, fs, SVN_FS_BASE__MISC_FORWARD_DELTA_UPGRADE, pool));
2879251881Speter      if (val)
2880251881Speter        SVN_ERR(svn_revnum_parse(&forward_delta_rev, val, NULL));
2881251881Speter
2882251881Speter      /* ### FIXME:  Unnecessarily harsh requirement? (cmpilato). */
2883251881Speter      if (revision <= forward_delta_rev)
2884251881Speter        return svn_error_createf
2885251881Speter          (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2886251881Speter           _("Cannot deltify revisions prior to r%ld"), forward_delta_rev+1);
2887251881Speter    }
2888251881Speter
2889251881Speter  SVN_ERR(svn_fs_base__revision_root(&root, fs, revision, pool));
2890251881Speter
2891251881Speter  args.txn_id = &txn_id;
2892251881Speter  args.revision = revision;
2893251881Speter  SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_rev_get_txn_id, &args,
2894251881Speter                                 FALSE, pool));
2895251881Speter
2896251881Speter  return deltify_mutable(fs, root, "/", NULL, svn_node_dir, txn_id, pool);
2897251881Speter}
2898251881Speter
2899251881Speter
2900251881Speter/* Modifying directories */
2901251881Speter
2902251881Speter
2903251881Speterstruct make_dir_args
2904251881Speter{
2905251881Speter  svn_fs_root_t *root;
2906251881Speter  const char *path;
2907251881Speter};
2908251881Speter
2909251881Speter
2910251881Speterstatic svn_error_t *
2911251881Spetertxn_body_make_dir(void *baton,
2912251881Speter                  trail_t *trail)
2913251881Speter{
2914251881Speter  struct make_dir_args *args = baton;
2915251881Speter  svn_fs_root_t *root = args->root;
2916251881Speter  const char *path = args->path;
2917251881Speter  parent_path_t *parent_path;
2918251881Speter  dag_node_t *sub_dir;
2919251881Speter  const char *txn_id = root->txn;
2920251881Speter
2921251881Speter  SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
2922251881Speter                    txn_id, trail, trail->pool));
2923251881Speter
2924251881Speter  /* If there's already a sub-directory by that name, complain.  This
2925251881Speter     also catches the case of trying to make a subdirectory named `/'.  */
2926251881Speter  if (parent_path->node)
2927251881Speter    return SVN_FS__ALREADY_EXISTS(root, path);
2928251881Speter
2929251881Speter  /* Check to see if some lock is 'reserving' a file-path or dir-path
2930251881Speter     at that location, or even some child-path;  if so, check that we
2931251881Speter     can use it. */
2932251881Speter  if (args->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2933251881Speter    {
2934251881Speter      SVN_ERR(svn_fs_base__allow_locked_operation(path, TRUE,
2935251881Speter                                                  trail, trail->pool));
2936251881Speter    }
2937251881Speter
2938251881Speter  /* Create the subdirectory.  */
2939251881Speter  SVN_ERR(make_path_mutable(root, parent_path->parent, path,
2940251881Speter                            trail, trail->pool));
2941251881Speter  SVN_ERR(svn_fs_base__dag_make_dir(&sub_dir,
2942251881Speter                                    parent_path->parent->node,
2943251881Speter                                    parent_path_path(parent_path->parent,
2944251881Speter                                                     trail->pool),
2945251881Speter                                    parent_path->entry,
2946251881Speter                                    txn_id,
2947251881Speter                                    trail, trail->pool));
2948251881Speter
2949251881Speter  /* Make a record of this modification in the changes table. */
2950251881Speter  return add_change(root->fs, txn_id, path,
2951251881Speter                    svn_fs_base__dag_get_id(sub_dir),
2952251881Speter                    svn_fs_path_change_add, FALSE, FALSE,
2953251881Speter                    trail, trail->pool);
2954251881Speter}
2955251881Speter
2956251881Speter
2957251881Speterstatic svn_error_t *
2958251881Speterbase_make_dir(svn_fs_root_t *root,
2959251881Speter              const char *path,
2960251881Speter              apr_pool_t *pool)
2961251881Speter{
2962251881Speter  struct make_dir_args args;
2963251881Speter
2964251881Speter  if (! root->is_txn_root)
2965251881Speter    return SVN_FS__NOT_TXN(root);
2966251881Speter
2967251881Speter  args.root = root;
2968251881Speter  args.path = path;
2969251881Speter  return svn_fs_base__retry_txn(root->fs, txn_body_make_dir, &args,
2970251881Speter                                TRUE, pool);
2971251881Speter}
2972251881Speter
2973251881Speter
2974251881Speterstruct delete_args
2975251881Speter{
2976251881Speter  svn_fs_root_t *root;
2977251881Speter  const char *path;
2978251881Speter};
2979251881Speter
2980251881Speter
2981251881Speter/* If this returns SVN_ERR_FS_NO_SUCH_ENTRY, it means that the
2982251881Speter   basename of PATH is missing from its parent, that is, the final
2983251881Speter   target of the deletion is missing.  */
2984251881Speterstatic svn_error_t *
2985251881Spetertxn_body_delete(void *baton,
2986251881Speter                trail_t *trail)
2987251881Speter{
2988251881Speter  struct delete_args *args = baton;
2989251881Speter  svn_fs_root_t *root = args->root;
2990251881Speter  const char *path = args->path;
2991251881Speter  parent_path_t *parent_path;
2992251881Speter  const char *txn_id = root->txn;
2993251881Speter  base_fs_data_t *bfd = trail->fs->fsap_data;
2994251881Speter
2995251881Speter  if (! root->is_txn_root)
2996251881Speter    return SVN_FS__NOT_TXN(root);
2997251881Speter
2998251881Speter  SVN_ERR(open_path(&parent_path, root, path, 0, txn_id,
2999251881Speter                    trail, trail->pool));
3000251881Speter
3001251881Speter  /* We can't remove the root of the filesystem.  */
3002251881Speter  if (! parent_path->parent)
3003251881Speter    return svn_error_create(SVN_ERR_FS_ROOT_DIR, NULL,
3004251881Speter                            _("The root directory cannot be deleted"));
3005251881Speter
3006251881Speter  /* Check to see if path (or any child thereof) is locked; if so,
3007251881Speter     check that we can use the existing lock(s). */
3008251881Speter  if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3009251881Speter    {
3010251881Speter      SVN_ERR(svn_fs_base__allow_locked_operation(path, TRUE,
3011251881Speter                                                  trail, trail->pool));
3012251881Speter    }
3013251881Speter
3014251881Speter  /* Make the parent directory mutable. */
3015251881Speter  SVN_ERR(make_path_mutable(root, parent_path->parent, path,
3016251881Speter                            trail, trail->pool));
3017251881Speter
3018251881Speter  /* Decrement mergeinfo counts on the parents of this node by the
3019251881Speter     count it previously carried, if our format supports it. */
3020251881Speter  if (bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
3021251881Speter    {
3022251881Speter      apr_int64_t mergeinfo_count;
3023251881Speter      SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, &mergeinfo_count,
3024251881Speter                                                   parent_path->node,
3025251881Speter                                                   trail, trail->pool));
3026251881Speter      SVN_ERR(adjust_parent_mergeinfo_counts(parent_path->parent,
3027251881Speter                                             -mergeinfo_count, txn_id,
3028251881Speter                                             trail, trail->pool));
3029251881Speter    }
3030251881Speter
3031251881Speter  /* Do the deletion. */
3032251881Speter  SVN_ERR(svn_fs_base__dag_delete(parent_path->parent->node,
3033251881Speter                                  parent_path->entry,
3034251881Speter                                  txn_id, trail, trail->pool));
3035251881Speter
3036251881Speter
3037251881Speter  /* Make a record of this modification in the changes table. */
3038251881Speter  return add_change(root->fs, txn_id, path,
3039251881Speter                    svn_fs_base__dag_get_id(parent_path->node),
3040251881Speter                    svn_fs_path_change_delete, FALSE, FALSE, trail,
3041251881Speter                    trail->pool);
3042251881Speter}
3043251881Speter
3044251881Speter
3045251881Speterstatic svn_error_t *
3046251881Speterbase_delete_node(svn_fs_root_t *root,
3047251881Speter                 const char *path,
3048251881Speter                 apr_pool_t *pool)
3049251881Speter{
3050251881Speter  struct delete_args args;
3051251881Speter
3052251881Speter  args.root        = root;
3053251881Speter  args.path        = path;
3054251881Speter  return svn_fs_base__retry_txn(root->fs, txn_body_delete, &args,
3055251881Speter                                TRUE, pool);
3056251881Speter}
3057251881Speter
3058251881Speter
3059251881Speterstruct copy_args
3060251881Speter{
3061251881Speter  svn_fs_root_t *from_root;
3062251881Speter  const char *from_path;
3063251881Speter  svn_fs_root_t *to_root;
3064251881Speter  const char *to_path;
3065251881Speter  svn_boolean_t preserve_history;
3066251881Speter};
3067251881Speter
3068251881Speter
3069251881Speterstatic svn_error_t *
3070251881Spetertxn_body_copy(void *baton,
3071251881Speter              trail_t *trail)
3072251881Speter{
3073251881Speter  struct copy_args *args = baton;
3074251881Speter  svn_fs_root_t *from_root = args->from_root;
3075251881Speter  const char *from_path = args->from_path;
3076251881Speter  svn_fs_root_t *to_root = args->to_root;
3077251881Speter  const char *to_path = args->to_path;
3078251881Speter  dag_node_t *from_node;
3079251881Speter  parent_path_t *to_parent_path;
3080251881Speter  const char *txn_id = to_root->txn;
3081251881Speter
3082251881Speter  /* Get the NODE for FROM_PATH in FROM_ROOT.*/
3083251881Speter  SVN_ERR(get_dag(&from_node, from_root, from_path, trail, trail->pool));
3084251881Speter
3085251881Speter  /* Build up the parent path from TO_PATH in TO_ROOT.  If the last
3086251881Speter     component does not exist, it's not that big a deal.  We'll just
3087251881Speter     make one there. */
3088251881Speter  SVN_ERR(open_path(&to_parent_path, to_root, to_path,
3089251881Speter                    open_path_last_optional, txn_id, trail, trail->pool));
3090251881Speter
3091251881Speter  /* Check to see if to-path (or any child thereof) is locked, or at
3092251881Speter     least 'reserved', whether it exists or not; if so, check that we
3093251881Speter     can use the existing lock(s). */
3094251881Speter  if (to_root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3095251881Speter    {
3096251881Speter      SVN_ERR(svn_fs_base__allow_locked_operation(to_path, TRUE,
3097251881Speter                                                  trail, trail->pool));
3098251881Speter    }
3099251881Speter
3100251881Speter  /* If the destination node already exists as the same node as the
3101251881Speter     source (in other words, this operation would result in nothing
3102251881Speter     happening at all), just do nothing an return successfully,
3103251881Speter     proud that you saved yourself from a tiresome task. */
3104251881Speter  if ((to_parent_path->node)
3105251881Speter      && (svn_fs_base__id_compare(svn_fs_base__dag_get_id(from_node),
3106251881Speter                                  svn_fs_base__dag_get_id
3107251881Speter                                  (to_parent_path->node)) == 0))
3108251881Speter    return SVN_NO_ERROR;
3109251881Speter
3110251881Speter  if (! from_root->is_txn_root)
3111251881Speter    {
3112251881Speter      svn_fs_path_change_kind_t kind;
3113251881Speter      dag_node_t *new_node;
3114251881Speter      apr_int64_t old_mergeinfo_count = 0, mergeinfo_count;
3115251881Speter      base_fs_data_t *bfd = trail->fs->fsap_data;
3116251881Speter
3117251881Speter      /* If TO_PATH already existed prior to the copy, note that this
3118251881Speter         operation is a replacement, not an addition. */
3119251881Speter      if (to_parent_path->node)
3120251881Speter        kind = svn_fs_path_change_replace;
3121251881Speter      else
3122251881Speter        kind = svn_fs_path_change_add;
3123251881Speter
3124251881Speter      /* Make sure the target node's parents are mutable.  */
3125251881Speter      SVN_ERR(make_path_mutable(to_root, to_parent_path->parent,
3126251881Speter                                to_path, trail, trail->pool));
3127251881Speter
3128251881Speter      /* If this is a replacement operation, we need to know the old
3129251881Speter         node's mergeinfo count. */
3130251881Speter      if (to_parent_path->node)
3131251881Speter        SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL,
3132251881Speter                                                     &old_mergeinfo_count,
3133251881Speter                                                     to_parent_path->node,
3134251881Speter                                                     trail, trail->pool));
3135251881Speter      /* Do the copy. */
3136251881Speter      SVN_ERR(svn_fs_base__dag_copy(to_parent_path->parent->node,
3137251881Speter                                    to_parent_path->entry,
3138251881Speter                                    from_node,
3139251881Speter                                    args->preserve_history,
3140251881Speter                                    from_root->rev,
3141251881Speter                                    from_path, txn_id, trail, trail->pool));
3142251881Speter
3143251881Speter      /* Adjust the mergeinfo counts of the destination's parents if
3144251881Speter         our format supports it. */
3145251881Speter      if (bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
3146251881Speter        {
3147251881Speter          SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL,
3148251881Speter                                                       &mergeinfo_count,
3149251881Speter                                                       from_node, trail,
3150251881Speter                                                       trail->pool));
3151251881Speter          SVN_ERR(adjust_parent_mergeinfo_counts
3152251881Speter                  (to_parent_path->parent,
3153251881Speter                   mergeinfo_count - old_mergeinfo_count,
3154251881Speter                   txn_id, trail, trail->pool));
3155251881Speter        }
3156251881Speter
3157251881Speter      /* Make a record of this modification in the changes table. */
3158251881Speter      SVN_ERR(get_dag(&new_node, to_root, to_path, trail, trail->pool));
3159251881Speter      SVN_ERR(add_change(to_root->fs, txn_id, to_path,
3160251881Speter                         svn_fs_base__dag_get_id(new_node),
3161251881Speter                         kind, FALSE, FALSE, trail, trail->pool));
3162251881Speter    }
3163251881Speter  else
3164251881Speter    {
3165251881Speter      /* See IZ Issue #436 */
3166251881Speter      /* Copying from transaction roots not currently available.
3167251881Speter
3168251881Speter         ### cmpilato todo someday: make this not so. :-) Note that
3169251881Speter         when copying from mutable trees, you have to make sure that
3170251881Speter         you aren't creating a cyclic graph filesystem, and a simple
3171251881Speter         referencing operation won't cut it.  Currently, we should not
3172251881Speter         be able to reach this clause, and the interface reports that
3173251881Speter         this only works from immutable trees anyway, but JimB has
3174251881Speter         stated that this requirement need not be necessary in the
3175251881Speter         future. */
3176251881Speter
3177251881Speter      SVN_ERR_MALFUNCTION();
3178251881Speter    }
3179251881Speter
3180251881Speter  return SVN_NO_ERROR;
3181251881Speter}
3182251881Speter
3183251881Speter
3184251881Speter/* Set *SAME_P to TRUE if FS1 and FS2 have the same UUID, else set to FALSE.
3185251881Speter   Use POOL for temporary allocation only.
3186251881Speter   Note: this code is duplicated between libsvn_fs_fs and libsvn_fs_base. */
3187251881Speterstatic svn_error_t *
3188251881Speterfs_same_p(svn_boolean_t *same_p,
3189251881Speter          svn_fs_t *fs1,
3190251881Speter          svn_fs_t *fs2,
3191251881Speter          apr_pool_t *pool)
3192251881Speter{
3193251881Speter  *same_p = ! strcmp(fs1->uuid, fs2->uuid);
3194251881Speter  return SVN_NO_ERROR;
3195251881Speter}
3196251881Speter
3197251881Speter/* Copy the node at FROM_PATH under FROM_ROOT to TO_PATH under
3198251881Speter   TO_ROOT.  If PRESERVE_HISTORY is set, then the copy is recorded in
3199251881Speter   the copies table.  Perform temporary allocations in POOL. */
3200251881Speterstatic svn_error_t *
3201251881Spetercopy_helper(svn_fs_root_t *from_root,
3202251881Speter            const char *from_path,
3203251881Speter            svn_fs_root_t *to_root,
3204251881Speter            const char *to_path,
3205251881Speter            svn_boolean_t preserve_history,
3206251881Speter            apr_pool_t *pool)
3207251881Speter{
3208251881Speter  struct copy_args args;
3209251881Speter  svn_boolean_t same_p;
3210251881Speter
3211251881Speter  /* Use an error check, not an assert, because even the caller cannot
3212251881Speter     guarantee that a filesystem's UUID has not changed "on the fly". */
3213251881Speter  SVN_ERR(fs_same_p(&same_p, from_root->fs, to_root->fs, pool));
3214251881Speter  if (! same_p)
3215251881Speter    return svn_error_createf
3216251881Speter      (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
3217251881Speter       _("Cannot copy between two different filesystems ('%s' and '%s')"),
3218251881Speter       from_root->fs->path, to_root->fs->path);
3219251881Speter
3220251881Speter  if (! to_root->is_txn_root)
3221251881Speter    return SVN_FS__NOT_TXN(to_root);
3222251881Speter
3223251881Speter  if (from_root->is_txn_root)
3224251881Speter    return svn_error_create
3225251881Speter      (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
3226251881Speter       _("Copy from mutable tree not currently supported"));
3227251881Speter
3228251881Speter  args.from_root         = from_root;
3229251881Speter  args.from_path         = from_path;
3230251881Speter  args.to_root           = to_root;
3231251881Speter  args.to_path           = to_path;
3232251881Speter  args.preserve_history  = preserve_history;
3233251881Speter
3234251881Speter  return svn_fs_base__retry_txn(to_root->fs, txn_body_copy, &args,
3235251881Speter                                TRUE, pool);
3236251881Speter}
3237251881Speter
3238251881Speterstatic svn_error_t *
3239251881Speterbase_copy(svn_fs_root_t *from_root,
3240251881Speter          const char *from_path,
3241251881Speter          svn_fs_root_t *to_root,
3242251881Speter          const char *to_path,
3243251881Speter          apr_pool_t *pool)
3244251881Speter{
3245251881Speter  return copy_helper(from_root, from_path, to_root, to_path, TRUE, pool);
3246251881Speter}
3247251881Speter
3248251881Speter
3249251881Speterstatic svn_error_t *
3250251881Speterbase_revision_link(svn_fs_root_t *from_root,
3251251881Speter                   svn_fs_root_t *to_root,
3252251881Speter                   const char *path,
3253251881Speter                   apr_pool_t *pool)
3254251881Speter{
3255251881Speter  return copy_helper(from_root, path, to_root, path, FALSE, pool);
3256251881Speter}
3257251881Speter
3258251881Speter
3259251881Speterstruct copied_from_args
3260251881Speter{
3261251881Speter  svn_fs_root_t *root;      /* Root for the node whose ancestry we seek. */
3262251881Speter  const char *path;         /* Path for the node whose ancestry we seek. */
3263251881Speter
3264251881Speter  svn_revnum_t result_rev;  /* Revision, if any, of the ancestor. */
3265251881Speter  const char *result_path;  /* Path, if any, of the ancestor. */
3266251881Speter
3267251881Speter  apr_pool_t *pool;         /* Allocate `result_path' here. */
3268251881Speter};
3269251881Speter
3270251881Speter
3271251881Speterstatic svn_error_t *
3272251881Spetertxn_body_copied_from(void *baton, trail_t *trail)
3273251881Speter{
3274251881Speter  struct copied_from_args *args = baton;
3275251881Speter  const svn_fs_id_t *node_id, *pred_id;
3276251881Speter  dag_node_t *node;
3277251881Speter  svn_fs_t *fs = args->root->fs;
3278251881Speter
3279251881Speter  /* Clear the return variables. */
3280251881Speter  args->result_path = NULL;
3281251881Speter  args->result_rev = SVN_INVALID_REVNUM;
3282251881Speter
3283251881Speter  /* Fetch the NODE in question. */
3284251881Speter  SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
3285251881Speter  node_id = svn_fs_base__dag_get_id(node);
3286251881Speter
3287251881Speter  /* Check the node's predecessor-ID.  If it doesn't have one, it
3288251881Speter     isn't a copy. */
3289251881Speter  SVN_ERR(svn_fs_base__dag_get_predecessor_id(&pred_id, node,
3290251881Speter                                              trail, trail->pool));
3291251881Speter  if (! pred_id)
3292251881Speter    return SVN_NO_ERROR;
3293251881Speter
3294251881Speter  /* If NODE's copy-ID is the same as that of its predecessor... */
3295251881Speter  if (svn_fs_base__key_compare(svn_fs_base__id_copy_id(node_id),
3296251881Speter                               svn_fs_base__id_copy_id(pred_id)) != 0)
3297251881Speter    {
3298251881Speter      /* ... then NODE was either the target of a copy operation,
3299251881Speter         a copied subtree item.  We examine the actual copy record
3300251881Speter         to determine which is the case.  */
3301251881Speter      copy_t *copy;
3302251881Speter      SVN_ERR(svn_fs_bdb__get_copy(&copy, fs,
3303251881Speter                                   svn_fs_base__id_copy_id(node_id),
3304251881Speter                                   trail, trail->pool));
3305251881Speter      if ((copy->kind == copy_kind_real)
3306251881Speter          && svn_fs_base__id_eq(copy->dst_noderev_id, node_id))
3307251881Speter        {
3308251881Speter          args->result_path = copy->src_path;
3309251881Speter          SVN_ERR(svn_fs_base__txn_get_revision(&(args->result_rev), fs,
3310251881Speter                                                copy->src_txn_id,
3311251881Speter                                                trail, trail->pool));
3312251881Speter        }
3313251881Speter    }
3314251881Speter  return SVN_NO_ERROR;
3315251881Speter}
3316251881Speter
3317251881Speter
3318251881Speterstatic svn_error_t *
3319251881Speterbase_copied_from(svn_revnum_t *rev_p,
3320251881Speter                 const char **path_p,
3321251881Speter                 svn_fs_root_t *root,
3322251881Speter                 const char *path,
3323251881Speter                 apr_pool_t *pool)
3324251881Speter{
3325251881Speter  struct copied_from_args args;
3326251881Speter  apr_pool_t *scratch_pool = svn_pool_create(pool);
3327251881Speter  args.root = root;
3328251881Speter  args.path = path;
3329251881Speter  args.pool = pool;
3330251881Speter
3331251881Speter  SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_copied_from, &args,
3332251881Speter                                 FALSE, scratch_pool));
3333251881Speter
3334251881Speter  *rev_p  = args.result_rev;
3335251881Speter  *path_p = args.result_path ? apr_pstrdup(pool, args.result_path) : NULL;
3336251881Speter
3337251881Speter  svn_pool_destroy(scratch_pool);
3338251881Speter  return SVN_NO_ERROR;
3339251881Speter}
3340251881Speter
3341251881Speter
3342251881Speter
3343251881Speter/* Files.  */
3344251881Speter
3345251881Speter
3346251881Speterstruct make_file_args
3347251881Speter{
3348251881Speter  svn_fs_root_t *root;
3349251881Speter  const char *path;
3350251881Speter};
3351251881Speter
3352251881Speter
3353251881Speterstatic svn_error_t *
3354251881Spetertxn_body_make_file(void *baton,
3355251881Speter                   trail_t *trail)
3356251881Speter{
3357251881Speter  struct make_file_args *args = baton;
3358251881Speter  svn_fs_root_t *root = args->root;
3359251881Speter  const char *path = args->path;
3360251881Speter  parent_path_t *parent_path;
3361251881Speter  dag_node_t *child;
3362251881Speter  const char *txn_id = root->txn;
3363251881Speter
3364251881Speter  SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
3365251881Speter                    txn_id, trail, trail->pool));
3366251881Speter
3367251881Speter  /* If there's already a file by that name, complain.
3368251881Speter     This also catches the case of trying to make a file named `/'.  */
3369251881Speter  if (parent_path->node)
3370251881Speter    return SVN_FS__ALREADY_EXISTS(root, path);
3371251881Speter
3372251881Speter  /* Check to see if some lock is 'reserving' a file-path or dir-path
3373251881Speter     at that location, or even some child-path;  if so, check that we
3374251881Speter     can use it. */
3375251881Speter  if (args->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3376251881Speter    {
3377251881Speter      SVN_ERR(svn_fs_base__allow_locked_operation(path, TRUE,
3378251881Speter                                                  trail, trail->pool));
3379251881Speter    }
3380251881Speter
3381251881Speter  /* Create the file.  */
3382251881Speter  SVN_ERR(make_path_mutable(root, parent_path->parent, path,
3383251881Speter                            trail, trail->pool));
3384251881Speter  SVN_ERR(svn_fs_base__dag_make_file(&child,
3385251881Speter                                     parent_path->parent->node,
3386251881Speter                                     parent_path_path(parent_path->parent,
3387251881Speter                                                      trail->pool),
3388251881Speter                                     parent_path->entry,
3389251881Speter                                     txn_id,
3390251881Speter                                     trail, trail->pool));
3391251881Speter
3392251881Speter  /* Make a record of this modification in the changes table. */
3393251881Speter  return add_change(root->fs, txn_id, path,
3394251881Speter                    svn_fs_base__dag_get_id(child),
3395251881Speter                    svn_fs_path_change_add, TRUE, FALSE,
3396251881Speter                    trail, trail->pool);
3397251881Speter}
3398251881Speter
3399251881Speter
3400251881Speterstatic svn_error_t *
3401251881Speterbase_make_file(svn_fs_root_t *root,
3402251881Speter               const char *path,
3403251881Speter               apr_pool_t *pool)
3404251881Speter{
3405251881Speter  struct make_file_args args;
3406251881Speter
3407251881Speter  args.root = root;
3408251881Speter  args.path = path;
3409251881Speter  return svn_fs_base__retry_txn(root->fs, txn_body_make_file, &args,
3410251881Speter                                TRUE, pool);
3411251881Speter}
3412251881Speter
3413251881Speter
3414251881Speter
3415251881Speterstruct file_length_args
3416251881Speter{
3417251881Speter  svn_fs_root_t *root;
3418251881Speter  const char *path;
3419251881Speter  svn_filesize_t length;       /* OUT parameter */
3420251881Speter};
3421251881Speter
3422251881Speterstatic svn_error_t *
3423251881Spetertxn_body_file_length(void *baton,
3424251881Speter                     trail_t *trail)
3425251881Speter{
3426251881Speter  struct file_length_args *args = baton;
3427251881Speter  dag_node_t *file;
3428251881Speter
3429251881Speter  /* First create a dag_node_t from the root/path pair. */
3430251881Speter  SVN_ERR(get_dag(&file, args->root, args->path, trail, trail->pool));
3431251881Speter
3432251881Speter  /* Now fetch its length */
3433251881Speter  return svn_fs_base__dag_file_length(&args->length, file,
3434251881Speter                                      trail, trail->pool);
3435251881Speter}
3436251881Speter
3437251881Speterstatic svn_error_t *
3438251881Speterbase_file_length(svn_filesize_t *length_p,
3439251881Speter                 svn_fs_root_t *root,
3440251881Speter                 const char *path,
3441251881Speter                 apr_pool_t *pool)
3442251881Speter{
3443251881Speter  struct file_length_args args;
3444251881Speter
3445251881Speter  args.root = root;
3446251881Speter  args.path = path;
3447251881Speter  SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_file_length, &args,
3448251881Speter                                 TRUE, pool));
3449251881Speter
3450251881Speter  *length_p = args.length;
3451251881Speter  return SVN_NO_ERROR;
3452251881Speter}
3453251881Speter
3454251881Speter
3455251881Speterstruct file_checksum_args
3456251881Speter{
3457251881Speter  svn_fs_root_t *root;
3458251881Speter  const char *path;
3459251881Speter  svn_checksum_kind_t kind;
3460251881Speter  svn_checksum_t **checksum;  /* OUT parameter */
3461251881Speter};
3462251881Speter
3463251881Speterstatic svn_error_t *
3464251881Spetertxn_body_file_checksum(void *baton,
3465251881Speter                       trail_t *trail)
3466251881Speter{
3467251881Speter  struct file_checksum_args *args = baton;
3468251881Speter  dag_node_t *file;
3469251881Speter
3470251881Speter  SVN_ERR(get_dag(&file, args->root, args->path, trail, trail->pool));
3471251881Speter
3472251881Speter  return svn_fs_base__dag_file_checksum(args->checksum, args->kind, file,
3473251881Speter                                        trail, trail->pool);
3474251881Speter}
3475251881Speter
3476251881Speterstatic svn_error_t *
3477251881Speterbase_file_checksum(svn_checksum_t **checksum,
3478251881Speter                   svn_checksum_kind_t kind,
3479251881Speter                   svn_fs_root_t *root,
3480251881Speter                   const char *path,
3481251881Speter                   apr_pool_t *pool)
3482251881Speter{
3483251881Speter  struct file_checksum_args args;
3484251881Speter  apr_pool_t *scratch_pool = svn_pool_create(pool);
3485251881Speter
3486251881Speter  args.root = root;
3487251881Speter  args.path = path;
3488251881Speter  args.kind = kind;
3489251881Speter  args.checksum = checksum;
3490251881Speter  SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_file_checksum, &args,
3491251881Speter                                 FALSE, scratch_pool));
3492251881Speter  *checksum = svn_checksum_dup(*checksum, pool);
3493251881Speter  svn_pool_destroy(scratch_pool);
3494251881Speter  return SVN_NO_ERROR;
3495251881Speter}
3496251881Speter
3497251881Speter
3498251881Speter/* --- Machinery for svn_fs_file_contents() ---  */
3499251881Speter
3500251881Speter
3501251881Speter/* Local baton type for txn_body_get_file_contents. */
3502251881Spetertypedef struct file_contents_baton_t
3503251881Speter{
3504251881Speter  /* The file we want to read. */
3505251881Speter  svn_fs_root_t *root;
3506251881Speter  const char *path;
3507251881Speter
3508251881Speter  /* The dag_node that will be made from the above. */
3509251881Speter  dag_node_t *node;
3510251881Speter
3511251881Speter  /* The pool in which `file_stream' (below) is allocated. */
3512251881Speter  apr_pool_t *pool;
3513251881Speter
3514251881Speter  /* The readable file stream that will be made from the
3515251881Speter     dag_node. (And returned to the caller.) */
3516251881Speter  svn_stream_t *file_stream;
3517251881Speter
3518251881Speter} file_contents_baton_t;
3519251881Speter
3520251881Speter
3521251881Speter/* Main body of svn_fs_file_contents;  converts a root/path pair into
3522251881Speter   a readable file stream (in the context of a db txn). */
3523251881Speterstatic svn_error_t *
3524251881Spetertxn_body_get_file_contents(void *baton, trail_t *trail)
3525251881Speter{
3526251881Speter  file_contents_baton_t *fb = (file_contents_baton_t *) baton;
3527251881Speter
3528251881Speter  /* First create a dag_node_t from the root/path pair. */
3529251881Speter  SVN_ERR(get_dag(&(fb->node), fb->root, fb->path, trail, trail->pool));
3530251881Speter
3531251881Speter  /* Then create a readable stream from the dag_node_t. */
3532251881Speter  return svn_fs_base__dag_get_contents(&(fb->file_stream),
3533251881Speter                                       fb->node, trail, fb->pool);
3534251881Speter}
3535251881Speter
3536251881Speter
3537251881Speter
3538251881Speterstatic svn_error_t *
3539251881Speterbase_file_contents(svn_stream_t **contents,
3540251881Speter                   svn_fs_root_t *root,
3541251881Speter                   const char *path,
3542251881Speter                   apr_pool_t *pool)
3543251881Speter{
3544251881Speter  file_contents_baton_t *fb = apr_pcalloc(pool, sizeof(*fb));
3545251881Speter  fb->root = root;
3546251881Speter  fb->path = path;
3547251881Speter  fb->pool = pool;
3548251881Speter
3549251881Speter  /* Create the readable stream in the context of a db txn.  */
3550251881Speter  SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_get_file_contents, fb,
3551251881Speter                                 FALSE, pool));
3552251881Speter
3553251881Speter  *contents = fb->file_stream;
3554251881Speter  return SVN_NO_ERROR;
3555251881Speter}
3556251881Speter
3557251881Speter/* --- End machinery for svn_fs_file_contents() ---  */
3558251881Speter
3559251881Speter
3560251881Speter
3561251881Speter/* --- Machinery for svn_fs_apply_textdelta() ---  */
3562251881Speter
3563251881Speter
3564251881Speter/* Local baton type for all the helper functions below. */
3565251881Spetertypedef struct txdelta_baton_t
3566251881Speter{
3567251881Speter  /* This is the custom-built window consumer given to us by the delta
3568251881Speter     library;  it uniquely knows how to read data from our designated
3569251881Speter     "source" stream, interpret the window, and write data to our
3570251881Speter     designated "target" stream (in this case, our repos file.) */
3571251881Speter  svn_txdelta_window_handler_t interpreter;
3572251881Speter  void *interpreter_baton;
3573251881Speter
3574251881Speter  /* The original file info */
3575251881Speter  svn_fs_root_t *root;
3576251881Speter  const char *path;
3577251881Speter
3578251881Speter  /* Derived from the file info */
3579251881Speter  dag_node_t *node;
3580251881Speter
3581251881Speter  svn_stream_t *source_stream;
3582251881Speter  svn_stream_t *target_stream;
3583251881Speter  svn_stream_t *string_stream;
3584251881Speter  svn_stringbuf_t *target_string;
3585251881Speter
3586251881Speter  /* Checksums for the base text against which a delta is to be
3587251881Speter     applied, and for the resultant fulltext, respectively.  Either or
3588251881Speter     both may be null, in which case ignored. */
3589251881Speter  svn_checksum_t *base_checksum;
3590251881Speter  svn_checksum_t *result_checksum;
3591251881Speter
3592251881Speter  /* Pool used by db txns */
3593251881Speter  apr_pool_t *pool;
3594251881Speter
3595251881Speter} txdelta_baton_t;
3596251881Speter
3597251881Speter
3598251881Speter/* A trail-ready wrapper around svn_fs_base__dag_finalize_edits.
3599251881Speter * This closes BATON->target_stream.
3600251881Speter *
3601251881Speter * Note: If you're confused about how this function relates to another
3602251881Speter * of similar name, think of it this way:
3603251881Speter *
3604251881Speter * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits()
3605251881Speter * svn_fs_apply_text()      ==> ... ==> txn_body_fulltext_finalize_edits()
3606251881Speter */
3607251881Speterstatic svn_error_t *
3608251881Spetertxn_body_txdelta_finalize_edits(void *baton, trail_t *trail)
3609251881Speter{
3610251881Speter  txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3611251881Speter  SVN_ERR(svn_fs_base__dag_finalize_edits(tb->node,
3612251881Speter                                          tb->result_checksum,
3613251881Speter                                          tb->root->txn,
3614251881Speter                                          trail, trail->pool));
3615251881Speter
3616251881Speter  /* Make a record of this modification in the changes table. */
3617251881Speter  return add_change(tb->root->fs, tb->root->txn, tb->path,
3618251881Speter                    svn_fs_base__dag_get_id(tb->node),
3619251881Speter                    svn_fs_path_change_modify, TRUE, FALSE, trail,
3620251881Speter                    trail->pool);
3621251881Speter}
3622251881Speter
3623251881Speter
3624251881Speter/* ### see comment in window_consumer() regarding this function. */
3625251881Speter
3626251881Speter/* Helper function of generic type `svn_write_fn_t'.  Implements a
3627251881Speter   writable stream which appends to an svn_stringbuf_t. */
3628251881Speterstatic svn_error_t *
3629251881Speterwrite_to_string(void *baton, const char *data, apr_size_t *len)
3630251881Speter{
3631251881Speter  txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3632251881Speter  svn_stringbuf_appendbytes(tb->target_string, data, *len);
3633251881Speter  return SVN_NO_ERROR;
3634251881Speter}
3635251881Speter
3636251881Speter
3637251881Speter
3638251881Speter/* The main window handler returned by svn_fs_apply_textdelta. */
3639251881Speterstatic svn_error_t *
3640251881Speterwindow_consumer(svn_txdelta_window_t *window, void *baton)
3641251881Speter{
3642251881Speter  txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3643251881Speter
3644251881Speter  /* Send the window right through to the custom window interpreter.
3645251881Speter     In theory, the interpreter will then write more data to
3646251881Speter     cb->target_string. */
3647251881Speter  SVN_ERR(tb->interpreter(window, tb->interpreter_baton));
3648251881Speter
3649251881Speter  /* ### the write_to_string() callback for the txdelta's output stream
3650251881Speter     ### should be doing all the flush determination logic, not here.
3651251881Speter     ### in a drastic case, a window could generate a LOT more than the
3652251881Speter     ### maximum buffer size. we want to flush to the underlying target
3653251881Speter     ### stream much sooner (e.g. also in a streamy fashion). also, by
3654251881Speter     ### moving this logic inside the stream, the stream becomes nice
3655251881Speter     ### and encapsulated: it holds all the logic about buffering and
3656251881Speter     ### flushing.
3657251881Speter     ###
3658251881Speter     ### further: I believe the buffering should be removed from tree.c
3659251881Speter     ### the buffering should go into the target_stream itself, which
3660251881Speter     ### is defined by reps-string.c. Specifically, I think the
3661251881Speter     ### rep_write_contents() function will handle the buffering and
3662251881Speter     ### the spill to the underlying DB. by locating it there, then
3663251881Speter     ### anybody who gets a writable stream for FS content can take
3664251881Speter     ### advantage of the buffering capability. this will be important
3665251881Speter     ### when we export an FS API function for writing a fulltext into
3666251881Speter     ### the FS, rather than forcing that fulltext thru apply_textdelta.
3667251881Speter  */
3668251881Speter
3669251881Speter  /* Check to see if we need to purge the portion of the contents that
3670251881Speter     have been written thus far. */
3671251881Speter  if ((! window) || (tb->target_string->len > WRITE_BUFFER_SIZE))
3672251881Speter    {
3673251881Speter      apr_size_t len = tb->target_string->len;
3674251881Speter      SVN_ERR(svn_stream_write(tb->target_stream,
3675251881Speter                               tb->target_string->data,
3676251881Speter                               &len));
3677251881Speter      svn_stringbuf_setempty(tb->target_string);
3678251881Speter    }
3679251881Speter
3680251881Speter  /* Is the window NULL?  If so, we're done. */
3681251881Speter  if (! window)
3682251881Speter    {
3683251881Speter      /* Close the internal-use stream.  ### This used to be inside of
3684251881Speter         txn_body_fulltext_finalize_edits(), but that invoked a nested
3685251881Speter         Berkeley DB transaction -- scandalous! */
3686251881Speter      SVN_ERR(svn_stream_close(tb->target_stream));
3687251881Speter
3688251881Speter      /* Tell the dag subsystem that we're finished with our edits. */
3689251881Speter      SVN_ERR(svn_fs_base__retry_txn(tb->root->fs,
3690251881Speter                                     txn_body_txdelta_finalize_edits, tb,
3691251881Speter                                     FALSE, tb->pool));
3692251881Speter    }
3693251881Speter
3694251881Speter  return SVN_NO_ERROR;
3695251881Speter}
3696251881Speter
3697251881Speter
3698251881Speterstatic svn_error_t *
3699251881Spetertxn_body_apply_textdelta(void *baton, trail_t *trail)
3700251881Speter{
3701251881Speter  txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3702251881Speter  parent_path_t *parent_path;
3703251881Speter  const char *txn_id = tb->root->txn;
3704251881Speter
3705251881Speter  /* Call open_path with no flags, as we want this to return an error
3706251881Speter     if the node for which we are searching doesn't exist. */
3707251881Speter  SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id,
3708251881Speter                    trail, trail->pool));
3709251881Speter
3710251881Speter  /* Check to see if path is locked;  if so, check that we can use it. */
3711251881Speter  if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3712251881Speter    SVN_ERR(svn_fs_base__allow_locked_operation(tb->path, FALSE,
3713251881Speter                                                trail, trail->pool));
3714251881Speter
3715251881Speter  /* Now, make sure this path is mutable. */
3716251881Speter  SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path,
3717251881Speter                            trail, trail->pool));
3718251881Speter  tb->node = parent_path->node;
3719251881Speter
3720251881Speter  if (tb->base_checksum)
3721251881Speter    {
3722251881Speter      svn_checksum_t *checksum;
3723251881Speter
3724251881Speter      /* Until we finalize the node, its data_key points to the old
3725251881Speter         contents, in other words, the base text. */
3726251881Speter      SVN_ERR(svn_fs_base__dag_file_checksum(&checksum,
3727251881Speter                                             tb->base_checksum->kind,
3728251881Speter                                             tb->node, trail, trail->pool));
3729251881Speter      /* TODO: This only compares checksums if they are the same kind, but
3730251881Speter         we're calculating both SHA1 and MD5 checksums somewhere in
3731251881Speter         reps-strings.c.  Could we keep them both around somehow so this
3732251881Speter         check could be more comprehensive? */
3733251881Speter      if (!svn_checksum_match(tb->base_checksum, checksum))
3734251881Speter        return svn_checksum_mismatch_err(tb->base_checksum, checksum,
3735251881Speter                            trail->pool,
3736251881Speter                            _("Base checksum mismatch on '%s'"),
3737251881Speter                            tb->path);
3738251881Speter    }
3739251881Speter
3740251881Speter  /* Make a readable "source" stream out of the current contents of
3741251881Speter     ROOT/PATH; obviously, this must done in the context of a db_txn.
3742251881Speter     The stream is returned in tb->source_stream. */
3743251881Speter  SVN_ERR(svn_fs_base__dag_get_contents(&(tb->source_stream),
3744251881Speter                                        tb->node, trail, tb->pool));
3745251881Speter
3746251881Speter  /* Make a writable "target" stream */
3747251881Speter  SVN_ERR(svn_fs_base__dag_get_edit_stream(&(tb->target_stream), tb->node,
3748251881Speter                                           txn_id, trail, tb->pool));
3749251881Speter
3750251881Speter  /* Make a writable "string" stream which writes data to
3751251881Speter     tb->target_string. */
3752251881Speter  tb->target_string = svn_stringbuf_create_empty(tb->pool);
3753251881Speter  tb->string_stream = svn_stream_create(tb, tb->pool);
3754251881Speter  svn_stream_set_write(tb->string_stream, write_to_string);
3755251881Speter
3756251881Speter  /* Now, create a custom window handler that uses our two streams. */
3757251881Speter  svn_txdelta_apply(tb->source_stream,
3758251881Speter                    tb->string_stream,
3759251881Speter                    NULL,
3760251881Speter                    tb->path,
3761251881Speter                    tb->pool,
3762251881Speter                    &(tb->interpreter),
3763251881Speter                    &(tb->interpreter_baton));
3764251881Speter
3765251881Speter  return SVN_NO_ERROR;
3766251881Speter}
3767251881Speter
3768251881Speter
3769251881Speterstatic svn_error_t *
3770251881Speterbase_apply_textdelta(svn_txdelta_window_handler_t *contents_p,
3771251881Speter                     void **contents_baton_p,
3772251881Speter                     svn_fs_root_t *root,
3773251881Speter                     const char *path,
3774251881Speter                     svn_checksum_t *base_checksum,
3775251881Speter                     svn_checksum_t *result_checksum,
3776251881Speter                     apr_pool_t *pool)
3777251881Speter{
3778251881Speter  txdelta_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
3779251881Speter
3780251881Speter  tb->root = root;
3781251881Speter  tb->path = path;
3782251881Speter  tb->pool = pool;
3783251881Speter  tb->base_checksum = svn_checksum_dup(base_checksum, pool);
3784251881Speter  tb->result_checksum = svn_checksum_dup(result_checksum, pool);
3785251881Speter
3786251881Speter  SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_apply_textdelta, tb,
3787251881Speter                                 FALSE, pool));
3788251881Speter
3789251881Speter  *contents_p = window_consumer;
3790251881Speter  *contents_baton_p = tb;
3791251881Speter  return SVN_NO_ERROR;
3792251881Speter}
3793251881Speter
3794251881Speter/* --- End machinery for svn_fs_apply_textdelta() ---  */
3795251881Speter
3796251881Speter/* --- Machinery for svn_fs_apply_text() ---  */
3797251881Speter
3798251881Speter/* Baton for svn_fs_apply_text(). */
3799251881Speterstruct text_baton_t
3800251881Speter{
3801251881Speter  /* The original file info */
3802251881Speter  svn_fs_root_t *root;
3803251881Speter  const char *path;
3804251881Speter
3805251881Speter  /* Derived from the file info */
3806251881Speter  dag_node_t *node;
3807251881Speter
3808251881Speter  /* The returned stream that will accept the file's new contents. */
3809251881Speter  svn_stream_t *stream;
3810251881Speter
3811251881Speter  /* The actual fs stream that the returned stream will write to. */
3812251881Speter  svn_stream_t *file_stream;
3813251881Speter
3814251881Speter  /* Checksum for the final fulltext written to the file.  May
3815251881Speter     be null, in which case ignored. */
3816251881Speter  svn_checksum_t *result_checksum;
3817251881Speter
3818251881Speter  /* Pool used by db txns */
3819251881Speter  apr_pool_t *pool;
3820251881Speter};
3821251881Speter
3822251881Speter
3823251881Speter/* A trail-ready wrapper around svn_fs_base__dag_finalize_edits, but for
3824251881Speter * fulltext data, not text deltas.  Closes BATON->file_stream.
3825251881Speter *
3826251881Speter * Note: If you're confused about how this function relates to another
3827251881Speter * of similar name, think of it this way:
3828251881Speter *
3829251881Speter * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits()
3830251881Speter * svn_fs_apply_text()      ==> ... ==> txn_body_fulltext_finalize_edits()
3831251881Speter */
3832251881Speterstatic svn_error_t *
3833251881Spetertxn_body_fulltext_finalize_edits(void *baton, trail_t *trail)
3834251881Speter{
3835251881Speter  struct text_baton_t *tb = baton;
3836251881Speter  SVN_ERR(svn_fs_base__dag_finalize_edits(tb->node,
3837251881Speter                                          tb->result_checksum,
3838251881Speter                                          tb->root->txn,
3839251881Speter                                          trail, trail->pool));
3840251881Speter
3841251881Speter  /* Make a record of this modification in the changes table. */
3842251881Speter  return add_change(tb->root->fs, tb->root->txn, tb->path,
3843251881Speter                    svn_fs_base__dag_get_id(tb->node),
3844251881Speter                    svn_fs_path_change_modify, TRUE, FALSE, trail,
3845251881Speter                    trail->pool);
3846251881Speter}
3847251881Speter
3848251881Speter/* Write function for the publically returned stream. */
3849251881Speterstatic svn_error_t *
3850251881Spetertext_stream_writer(void *baton,
3851251881Speter                   const char *data,
3852251881Speter                   apr_size_t *len)
3853251881Speter{
3854251881Speter  struct text_baton_t *tb = baton;
3855251881Speter
3856251881Speter  /* Psst, here's some data.  Pass it on to the -real- file stream. */
3857251881Speter  return svn_stream_write(tb->file_stream, data, len);
3858251881Speter}
3859251881Speter
3860251881Speter/* Close function for the publically returned stream. */
3861251881Speterstatic svn_error_t *
3862251881Spetertext_stream_closer(void *baton)
3863251881Speter{
3864251881Speter  struct text_baton_t *tb = baton;
3865251881Speter
3866251881Speter  /* Close the internal-use stream.  ### This used to be inside of
3867251881Speter     txn_body_fulltext_finalize_edits(), but that invoked a nested
3868251881Speter     Berkeley DB transaction -- scandalous! */
3869251881Speter  SVN_ERR(svn_stream_close(tb->file_stream));
3870251881Speter
3871251881Speter  /* Need to tell fs that we're done sending text */
3872251881Speter  return svn_fs_base__retry_txn(tb->root->fs,
3873251881Speter                                txn_body_fulltext_finalize_edits, tb,
3874251881Speter                                FALSE, tb->pool);
3875251881Speter}
3876251881Speter
3877251881Speter
3878251881Speterstatic svn_error_t *
3879251881Spetertxn_body_apply_text(void *baton, trail_t *trail)
3880251881Speter{
3881251881Speter  struct text_baton_t *tb = baton;
3882251881Speter  parent_path_t *parent_path;
3883251881Speter  const char *txn_id = tb->root->txn;
3884251881Speter
3885251881Speter  /* Call open_path with no flags, as we want this to return an error
3886251881Speter     if the node for which we are searching doesn't exist. */
3887251881Speter  SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id,
3888251881Speter                    trail, trail->pool));
3889251881Speter
3890251881Speter  /* Check to see if path is locked;  if so, check that we can use it. */
3891251881Speter  if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3892251881Speter    SVN_ERR(svn_fs_base__allow_locked_operation(tb->path, FALSE,
3893251881Speter                                                trail, trail->pool));
3894251881Speter
3895251881Speter  /* Now, make sure this path is mutable. */
3896251881Speter  SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path,
3897251881Speter                            trail, trail->pool));
3898251881Speter  tb->node = parent_path->node;
3899251881Speter
3900251881Speter  /* Make a writable stream for replacing the file's text. */
3901251881Speter  SVN_ERR(svn_fs_base__dag_get_edit_stream(&(tb->file_stream), tb->node,
3902251881Speter                                           txn_id, trail, tb->pool));
3903251881Speter
3904251881Speter  /* Create a 'returnable' stream which writes to the file_stream. */
3905251881Speter  tb->stream = svn_stream_create(tb, tb->pool);
3906251881Speter  svn_stream_set_write(tb->stream, text_stream_writer);
3907251881Speter  svn_stream_set_close(tb->stream, text_stream_closer);
3908251881Speter
3909251881Speter  return SVN_NO_ERROR;
3910251881Speter}
3911251881Speter
3912251881Speter
3913251881Speterstatic svn_error_t *
3914251881Speterbase_apply_text(svn_stream_t **contents_p,
3915251881Speter                svn_fs_root_t *root,
3916251881Speter                const char *path,
3917251881Speter                svn_checksum_t *result_checksum,
3918251881Speter                apr_pool_t *pool)
3919251881Speter{
3920251881Speter  struct text_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
3921251881Speter
3922251881Speter  tb->root = root;
3923251881Speter  tb->path = path;
3924251881Speter  tb->pool = pool;
3925251881Speter  tb->result_checksum = svn_checksum_dup(result_checksum, pool);
3926251881Speter
3927251881Speter  SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_apply_text, tb,
3928251881Speter                                 FALSE, pool));
3929251881Speter
3930251881Speter  *contents_p = tb->stream;
3931251881Speter  return SVN_NO_ERROR;
3932251881Speter}
3933251881Speter
3934251881Speter/* --- End machinery for svn_fs_apply_text() ---  */
3935251881Speter
3936251881Speter
3937251881Speter/* Note: we're sharing the `things_changed_args' struct with
3938251881Speter   svn_fs_props_changed(). */
3939251881Speter
3940251881Speterstatic svn_error_t *
3941251881Spetertxn_body_contents_changed(void *baton, trail_t *trail)
3942251881Speter{
3943251881Speter  struct things_changed_args *args = baton;
3944251881Speter  dag_node_t *node1, *node2;
3945251881Speter
3946251881Speter  SVN_ERR(get_dag(&node1, args->root1, args->path1, trail, trail->pool));
3947251881Speter  SVN_ERR(get_dag(&node2, args->root2, args->path2, trail, trail->pool));
3948251881Speter  return svn_fs_base__things_different(NULL, args->changed_p,
3949251881Speter                                       node1, node2, trail, trail->pool);
3950251881Speter}
3951251881Speter
3952251881Speter
3953251881Speter/* Note:  it is acceptable for this function to call back into
3954251881Speter   top-level interfaces because it does not itself use trails.  */
3955251881Speterstatic svn_error_t *
3956251881Speterbase_contents_changed(svn_boolean_t *changed_p,
3957251881Speter                      svn_fs_root_t *root1,
3958251881Speter                      const char *path1,
3959251881Speter                      svn_fs_root_t *root2,
3960251881Speter                      const char *path2,
3961251881Speter                      apr_pool_t *pool)
3962251881Speter{
3963251881Speter  struct things_changed_args args;
3964251881Speter
3965251881Speter  /* Check that roots are in the same fs. */
3966251881Speter  if (root1->fs != root2->fs)
3967251881Speter    return svn_error_create
3968251881Speter      (SVN_ERR_FS_GENERAL, NULL,
3969251881Speter       _("Cannot compare file contents between two different filesystems"));
3970251881Speter
3971251881Speter  /* Check that both paths are files. */
3972251881Speter  {
3973251881Speter    svn_node_kind_t kind;
3974251881Speter
3975251881Speter    SVN_ERR(base_check_path(&kind, root1, path1, pool));
3976251881Speter    if (kind != svn_node_file)
3977251881Speter      return svn_error_createf
3978251881Speter        (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path1);
3979251881Speter
3980251881Speter    SVN_ERR(base_check_path(&kind, root2, path2, pool));
3981251881Speter    if (kind != svn_node_file)
3982251881Speter      return svn_error_createf
3983251881Speter        (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path2);
3984251881Speter  }
3985251881Speter
3986251881Speter  args.root1      = root1;
3987251881Speter  args.root2      = root2;
3988251881Speter  args.path1      = path1;
3989251881Speter  args.path2      = path2;
3990251881Speter  args.changed_p  = changed_p;
3991251881Speter  args.pool       = pool;
3992251881Speter
3993251881Speter  return svn_fs_base__retry_txn(root1->fs, txn_body_contents_changed, &args,
3994251881Speter                                TRUE, pool);
3995251881Speter}
3996251881Speter
3997251881Speter
3998251881Speter
3999251881Speter/* Public interface to computing file text deltas.  */
4000251881Speter
4001251881Speter/* Note:  it is acceptable for this function to call back into
4002251881Speter   public FS API interfaces because it does not itself use trails.  */
4003251881Speterstatic svn_error_t *
4004251881Speterbase_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
4005251881Speter                           svn_fs_root_t *source_root,
4006251881Speter                           const char *source_path,
4007251881Speter                           svn_fs_root_t *target_root,
4008251881Speter                           const char *target_path,
4009251881Speter                           apr_pool_t *pool)
4010251881Speter{
4011251881Speter  svn_stream_t *source, *target;
4012251881Speter  svn_txdelta_stream_t *delta_stream;
4013251881Speter
4014251881Speter  /* Get read functions for the source file contents.  */
4015251881Speter  if (source_root && source_path)
4016251881Speter    SVN_ERR(base_file_contents(&source, source_root, source_path, pool));
4017251881Speter  else
4018251881Speter    source = svn_stream_empty(pool);
4019251881Speter
4020251881Speter  /* Get read functions for the target file contents.  */
4021251881Speter  SVN_ERR(base_file_contents(&target, target_root, target_path, pool));
4022251881Speter
4023251881Speter  /* Create a delta stream that turns the ancestor into the target.  */
4024251881Speter  svn_txdelta2(&delta_stream, source, target, TRUE, pool);
4025251881Speter
4026251881Speter  *stream_p = delta_stream;
4027251881Speter  return SVN_NO_ERROR;
4028251881Speter}
4029251881Speter
4030251881Speter
4031251881Speter
4032251881Speter/* Finding Changes */
4033251881Speter
4034251881Speterstruct paths_changed_args
4035251881Speter{
4036251881Speter  apr_hash_t *changes;
4037251881Speter  svn_fs_root_t *root;
4038251881Speter};
4039251881Speter
4040251881Speter
4041251881Speterstatic svn_error_t *
4042251881Spetertxn_body_paths_changed(void *baton,
4043251881Speter                       trail_t *trail)
4044251881Speter{
4045251881Speter  /* WARNING: This is called *without* the protection of a Berkeley DB
4046251881Speter     transaction.  If you modify this function, keep that in mind. */
4047251881Speter
4048251881Speter  struct paths_changed_args *args = baton;
4049251881Speter  const char *txn_id;
4050251881Speter  svn_fs_t *fs = args->root->fs;
4051251881Speter
4052251881Speter  /* Get the transaction ID from ROOT. */
4053251881Speter  if (! args->root->is_txn_root)
4054251881Speter    SVN_ERR(svn_fs_base__rev_get_txn_id(&txn_id, fs, args->root->rev,
4055251881Speter                                        trail, trail->pool));
4056251881Speter  else
4057251881Speter    txn_id = args->root->txn;
4058251881Speter
4059251881Speter  return svn_fs_bdb__changes_fetch(&(args->changes), fs, txn_id,
4060251881Speter                                   trail, trail->pool);
4061251881Speter}
4062251881Speter
4063251881Speter
4064251881Speterstatic svn_error_t *
4065251881Speterbase_paths_changed(apr_hash_t **changed_paths_p,
4066251881Speter                   svn_fs_root_t *root,
4067251881Speter                   apr_pool_t *pool)
4068251881Speter{
4069251881Speter  struct paths_changed_args args;
4070251881Speter  args.root = root;
4071251881Speter  args.changes = NULL;
4072251881Speter  SVN_ERR(svn_fs_base__retry(root->fs, txn_body_paths_changed, &args,
4073251881Speter                             FALSE, pool));
4074251881Speter  *changed_paths_p = args.changes;
4075251881Speter  return SVN_NO_ERROR;
4076251881Speter}
4077251881Speter
4078251881Speter
4079251881Speter
4080251881Speter/* Our coolio opaque history object. */
4081251881Spetertypedef struct base_history_data_t
4082251881Speter{
4083251881Speter  /* filesystem object */
4084251881Speter  svn_fs_t *fs;
4085251881Speter
4086251881Speter  /* path and revision of historical location */
4087251881Speter  const char *path;
4088251881Speter  svn_revnum_t revision;
4089251881Speter
4090251881Speter  /* internal-use hints about where to resume the history search. */
4091251881Speter  const char *path_hint;
4092251881Speter  svn_revnum_t rev_hint;
4093251881Speter
4094251881Speter  /* FALSE until the first call to svn_fs_history_prev(). */
4095251881Speter  svn_boolean_t is_interesting;
4096251881Speter} base_history_data_t;
4097251881Speter
4098251881Speter
4099251881Speterstatic svn_fs_history_t *assemble_history(svn_fs_t *fs, const char *path,
4100251881Speter                                          svn_revnum_t revision,
4101251881Speter                                          svn_boolean_t is_interesting,
4102251881Speter                                          const char *path_hint,
4103251881Speter                                          svn_revnum_t rev_hint,
4104251881Speter                                          apr_pool_t *pool);
4105251881Speter
4106251881Speter
4107251881Speterstatic svn_error_t *
4108251881Speterbase_node_history(svn_fs_history_t **history_p,
4109251881Speter                  svn_fs_root_t *root,
4110251881Speter                  const char *path,
4111251881Speter                  apr_pool_t *pool)
4112251881Speter{
4113251881Speter  svn_node_kind_t kind;
4114251881Speter
4115251881Speter  /* We require a revision root. */
4116251881Speter  if (root->is_txn_root)
4117251881Speter    return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
4118251881Speter
4119251881Speter  /* And we require that the path exist in the root. */
4120251881Speter  SVN_ERR(base_check_path(&kind, root, path, pool));
4121251881Speter  if (kind == svn_node_none)
4122251881Speter    return SVN_FS__NOT_FOUND(root, path);
4123251881Speter
4124251881Speter  /* Okay, all seems well.  Build our history object and return it. */
4125251881Speter  *history_p = assemble_history(root->fs,
4126251881Speter                                svn_fs__canonicalize_abspath(path, pool),
4127251881Speter                                root->rev, FALSE, NULL,
4128251881Speter                                SVN_INVALID_REVNUM, pool);
4129251881Speter  return SVN_NO_ERROR;
4130251881Speter}
4131251881Speter
4132251881Speter
4133251881Speter/* Examine the PARENT_PATH structure chain to determine how copy IDs
4134251881Speter   would be doled out in the event that PARENT_PATH was made mutable.
4135251881Speter   Return the ID of the copy that last affected PARENT_PATH (and the
4136251881Speter   COPY itself, if we've already fetched it).
4137251881Speter*/
4138251881Speterstatic svn_error_t *
4139251881Speterexamine_copy_inheritance(const char **copy_id,
4140251881Speter                         copy_t **copy,
4141251881Speter                         svn_fs_t *fs,
4142251881Speter                         parent_path_t *parent_path,
4143251881Speter                         trail_t *trail,
4144251881Speter                         apr_pool_t *pool)
4145251881Speter{
4146251881Speter  /* The default response -- our current copy ID, and no fetched COPY. */
4147251881Speter  *copy_id = svn_fs_base__id_copy_id
4148251881Speter    (svn_fs_base__dag_get_id(parent_path->node));
4149251881Speter  *copy = NULL;
4150251881Speter
4151251881Speter  /* If we have no parent (we are looking at the root node), or if
4152251881Speter     this node is supposed to inherit from itself, return that fact. */
4153251881Speter  if (! parent_path->parent)
4154251881Speter    return SVN_NO_ERROR;
4155251881Speter
4156251881Speter  /* We could be a branch destination (which would answer our question
4157251881Speter     altogether)!  But then, again, we might just have been modified
4158251881Speter     in this revision, so all bets are off. */
4159251881Speter  if (parent_path->copy_inherit == copy_id_inherit_self)
4160251881Speter    {
4161251881Speter      /* A copy ID of "0" means we've never been branched.  Therefore,
4162251881Speter         there are no copies relevant to our history. */
4163251881Speter      if (((*copy_id)[0] == '0') && ((*copy_id)[1] == '\0'))
4164251881Speter        return SVN_NO_ERROR;
4165251881Speter
4166251881Speter      /* Get the COPY record.  If it was a real copy (not an implicit
4167251881Speter         one), we have our answer.  Otherwise, we fall through to the
4168251881Speter         recursive case. */
4169251881Speter      SVN_ERR(svn_fs_bdb__get_copy(copy, fs, *copy_id, trail, pool));
4170251881Speter      if ((*copy)->kind != copy_kind_soft)
4171251881Speter        return SVN_NO_ERROR;
4172251881Speter    }
4173251881Speter
4174251881Speter  /* Otherwise, our answer is dependent upon our parent. */
4175251881Speter  return examine_copy_inheritance(copy_id, copy, fs,
4176251881Speter                                  parent_path->parent, trail, pool);
4177251881Speter}
4178251881Speter
4179251881Speter
4180251881Speterstruct history_prev_args
4181251881Speter{
4182251881Speter  svn_fs_history_t **prev_history_p;
4183251881Speter  svn_fs_history_t *history;
4184251881Speter  svn_boolean_t cross_copies;
4185251881Speter  apr_pool_t *pool;
4186251881Speter};
4187251881Speter
4188251881Speter
4189251881Speterstatic svn_error_t *
4190251881Spetertxn_body_history_prev(void *baton, trail_t *trail)
4191251881Speter{
4192251881Speter  struct history_prev_args *args = baton;
4193251881Speter  svn_fs_history_t **prev_history = args->prev_history_p;
4194251881Speter  svn_fs_history_t *history = args->history;
4195251881Speter  base_history_data_t *bhd = history->fsap_data;
4196251881Speter  const char *commit_path, *src_path, *path = bhd->path;
4197251881Speter  svn_revnum_t commit_rev, src_rev, dst_rev, revision = bhd->revision;
4198251881Speter  apr_pool_t *retpool = args->pool;
4199251881Speter  svn_fs_t *fs = bhd->fs;
4200251881Speter  parent_path_t *parent_path;
4201251881Speter  dag_node_t *node;
4202251881Speter  svn_fs_root_t *root;
4203251881Speter  const svn_fs_id_t *node_id;
4204251881Speter  const char *end_copy_id = NULL;
4205251881Speter  struct revision_root_args rr_args;
4206251881Speter  svn_boolean_t reported = bhd->is_interesting;
4207251881Speter  const char *txn_id;
4208251881Speter  copy_t *copy = NULL;
4209251881Speter  svn_boolean_t retry = FALSE;
4210251881Speter
4211251881Speter  /* Initialize our return value. */
4212251881Speter  *prev_history = NULL;
4213251881Speter
4214251881Speter  /* If our last history report left us hints about where to pickup
4215251881Speter     the chase, then our last report was on the destination of a
4216251881Speter     copy.  If we are crossing copies, start from those locations,
4217251881Speter     otherwise, we're all done here.  */
4218251881Speter  if (bhd->path_hint && SVN_IS_VALID_REVNUM(bhd->rev_hint))
4219251881Speter    {
4220251881Speter      reported = FALSE;
4221251881Speter      if (! args->cross_copies)
4222251881Speter        return SVN_NO_ERROR;
4223251881Speter      path = bhd->path_hint;
4224251881Speter      revision = bhd->rev_hint;
4225251881Speter    }
4226251881Speter
4227251881Speter  /* Construct a ROOT for the current revision. */
4228251881Speter  rr_args.root_p = &root;
4229251881Speter  rr_args.rev = revision;
4230251881Speter  SVN_ERR(txn_body_revision_root(&rr_args, trail));
4231251881Speter
4232251881Speter  /* Open PATH/REVISION, and get its node and a bunch of other
4233251881Speter     goodies.  */
4234251881Speter  SVN_ERR(svn_fs_base__rev_get_txn_id(&txn_id, fs, revision, trail,
4235251881Speter                                      trail->pool));
4236251881Speter  SVN_ERR(open_path(&parent_path, root, path, 0, txn_id,
4237251881Speter                    trail, trail->pool));
4238251881Speter  node = parent_path->node;
4239251881Speter  node_id = svn_fs_base__dag_get_id(node);
4240251881Speter  commit_path = svn_fs_base__dag_get_created_path(node);
4241251881Speter  SVN_ERR(svn_fs_base__dag_get_revision(&commit_rev, node,
4242251881Speter                                        trail, trail->pool));
4243251881Speter
4244251881Speter  /* The Subversion filesystem is written in such a way that a given
4245251881Speter     line of history may have at most one interesting history point
4246251881Speter     per filesystem revision.  Either that node was edited (and
4247251881Speter     possibly copied), or it was copied but not edited.  And a copy
4248251881Speter     source cannot be from the same revision as its destination.  So,
4249251881Speter     if our history revision matches its node's commit revision, we
4250251881Speter     know that ... */
4251251881Speter  if (revision == commit_rev)
4252251881Speter    {
4253251881Speter      if (! reported)
4254251881Speter        {
4255251881Speter          /* ... we either have not yet reported on this revision (and
4256251881Speter             need now to do so) ... */
4257251881Speter          *prev_history = assemble_history(fs,
4258251881Speter                                           apr_pstrdup(retpool, commit_path),
4259251881Speter                                           commit_rev, TRUE, NULL,
4260251881Speter                                           SVN_INVALID_REVNUM, retpool);
4261251881Speter          return SVN_NO_ERROR;
4262251881Speter        }
4263251881Speter      else
4264251881Speter        {
4265251881Speter          /* ... or we *have* reported on this revision, and must now
4266251881Speter             progress toward this node's predecessor (unless there is
4267251881Speter             no predecessor, in which case we're all done!). */
4268251881Speter          const svn_fs_id_t *pred_id;
4269251881Speter
4270251881Speter          SVN_ERR(svn_fs_base__dag_get_predecessor_id(&pred_id, node,
4271251881Speter                                                      trail, trail->pool));
4272251881Speter          if (! pred_id)
4273251881Speter            return SVN_NO_ERROR;
4274251881Speter
4275251881Speter          /* Replace NODE and friends with the information from its
4276251881Speter             predecessor. */
4277251881Speter          SVN_ERR(svn_fs_base__dag_get_node(&node, fs, pred_id,
4278251881Speter                                            trail, trail->pool));
4279251881Speter          node_id = svn_fs_base__dag_get_id(node);
4280251881Speter          commit_path = svn_fs_base__dag_get_created_path(node);
4281251881Speter          SVN_ERR(svn_fs_base__dag_get_revision(&commit_rev, node,
4282251881Speter                                                trail, trail->pool));
4283251881Speter        }
4284251881Speter    }
4285251881Speter
4286251881Speter  /* Calculate a possibly relevant copy ID. */
4287251881Speter  SVN_ERR(examine_copy_inheritance(&end_copy_id, &copy, fs,
4288251881Speter                                   parent_path, trail, trail->pool));
4289251881Speter
4290251881Speter  /* Initialize some state variables. */
4291251881Speter  src_path = NULL;
4292251881Speter  src_rev = SVN_INVALID_REVNUM;
4293251881Speter  dst_rev = SVN_INVALID_REVNUM;
4294251881Speter
4295251881Speter  /* If our current copy ID (which is either the real copy ID of our
4296251881Speter     node, or the last copy ID which would affect our node if it were
4297251881Speter     to be made mutable) diffs at all from that of its predecessor
4298251881Speter     (which is either a real predecessor, or is the node itself
4299251881Speter     playing the predecessor role to an imaginary mutable successor),
4300251881Speter     then we need to report a copy.  */
4301251881Speter  if (svn_fs_base__key_compare(svn_fs_base__id_copy_id(node_id),
4302251881Speter                               end_copy_id) != 0)
4303251881Speter    {
4304251881Speter      const char *remainder;
4305251881Speter      dag_node_t *dst_node;
4306251881Speter      const char *copy_dst;
4307251881Speter
4308251881Speter      /* Get the COPY record if we haven't already fetched it. */
4309251881Speter      if (! copy)
4310251881Speter        SVN_ERR(svn_fs_bdb__get_copy(&copy, fs, end_copy_id, trail,
4311251881Speter                                     trail->pool));
4312251881Speter
4313251881Speter      /* Figure out the destination path of the copy operation. */
4314251881Speter      SVN_ERR(svn_fs_base__dag_get_node(&dst_node, fs,
4315251881Speter                                        copy->dst_noderev_id,
4316251881Speter                                        trail, trail->pool));
4317251881Speter      copy_dst = svn_fs_base__dag_get_created_path(dst_node);
4318251881Speter
4319251881Speter      /* If our current path was the very destination of the copy,
4320251881Speter         then our new current path will be the copy source.  If our
4321251881Speter         current path was instead the *child* of the destination of
4322251881Speter         the copy, then figure out its previous location by taking its
4323251881Speter         path relative to the copy destination and appending that to
4324251881Speter         the copy source.  Finally, if our current path doesn't meet
4325251881Speter         one of these other criteria ... ### for now just fallback to
4326251881Speter         the old copy hunt algorithm. */
4327251881Speter      remainder = svn_fspath__skip_ancestor(copy_dst, path);
4328251881Speter
4329251881Speter      if (remainder)
4330251881Speter        {
4331251881Speter          /* If we get here, then our current path is the destination
4332251881Speter             of, or the child of the destination of, a copy.  Fill
4333251881Speter             in the return values and get outta here.  */
4334251881Speter          SVN_ERR(svn_fs_base__txn_get_revision
4335251881Speter                  (&src_rev, fs, copy->src_txn_id, trail, trail->pool));
4336251881Speter          SVN_ERR(svn_fs_base__txn_get_revision
4337251881Speter                  (&dst_rev, fs,
4338251881Speter                   svn_fs_base__id_txn_id(copy->dst_noderev_id),
4339251881Speter                   trail, trail->pool));
4340251881Speter          src_path = svn_fspath__join(copy->src_path, remainder,
4341251881Speter                                     trail->pool);
4342251881Speter          if (copy->kind == copy_kind_soft)
4343251881Speter            retry = TRUE;
4344251881Speter        }
4345251881Speter    }
4346251881Speter
4347251881Speter  /* If we calculated a copy source path and revision, and the
4348251881Speter     copy source revision doesn't pre-date a revision in which we
4349251881Speter     *know* our node was modified, we'll make a 'copy-style' history
4350251881Speter     object. */
4351251881Speter  if (src_path && SVN_IS_VALID_REVNUM(src_rev) && (src_rev >= commit_rev))
4352251881Speter    {
4353251881Speter      /* It's possible for us to find a copy location that is the same
4354251881Speter         as the history point we've just reported.  If that happens,
4355251881Speter         we simply need to take another trip through this history
4356251881Speter         search. */
4357251881Speter      if ((dst_rev == revision) && reported)
4358251881Speter        retry = TRUE;
4359251881Speter
4360251881Speter      *prev_history = assemble_history(fs, apr_pstrdup(retpool, path),
4361251881Speter                                       dst_rev, ! retry,
4362251881Speter                                       src_path, src_rev, retpool);
4363251881Speter    }
4364251881Speter  else
4365251881Speter    {
4366251881Speter      *prev_history = assemble_history(fs, apr_pstrdup(retpool, commit_path),
4367251881Speter                                       commit_rev, TRUE, NULL,
4368251881Speter                                       SVN_INVALID_REVNUM, retpool);
4369251881Speter    }
4370251881Speter
4371251881Speter  return SVN_NO_ERROR;
4372251881Speter}
4373251881Speter
4374251881Speter
4375251881Speterstatic svn_error_t *
4376251881Speterbase_history_prev(svn_fs_history_t **prev_history_p,
4377251881Speter                  svn_fs_history_t *history,
4378251881Speter                  svn_boolean_t cross_copies,
4379251881Speter                  apr_pool_t *pool)
4380251881Speter{
4381251881Speter  svn_fs_history_t *prev_history = NULL;
4382251881Speter  base_history_data_t *bhd = history->fsap_data;
4383251881Speter  svn_fs_t *fs = bhd->fs;
4384251881Speter
4385251881Speter  /* Special case: the root directory changes in every single
4386251881Speter     revision, no exceptions.  And, the root can't be the target (or
4387251881Speter     child of a target -- duh) of a copy.  So, if that's our path,
4388251881Speter     then we need only decrement our revision by 1, and there you go. */
4389251881Speter  if (strcmp(bhd->path, "/") == 0)
4390251881Speter    {
4391251881Speter      if (! bhd->is_interesting)
4392251881Speter        prev_history = assemble_history(fs, "/", bhd->revision,
4393251881Speter                                        1, NULL, SVN_INVALID_REVNUM, pool);
4394251881Speter      else if (bhd->revision > 0)
4395251881Speter        prev_history = assemble_history(fs, "/", bhd->revision - 1,
4396251881Speter                                        1, NULL, SVN_INVALID_REVNUM, pool);
4397251881Speter    }
4398251881Speter  else
4399251881Speter    {
4400251881Speter      struct history_prev_args args;
4401251881Speter      prev_history = history;
4402251881Speter
4403251881Speter      while (1)
4404251881Speter        {
4405251881Speter          /* Get a trail, and get to work. */
4406251881Speter
4407251881Speter          args.prev_history_p = &prev_history;
4408251881Speter          args.history = prev_history;
4409251881Speter          args.cross_copies = cross_copies;
4410251881Speter          args.pool = pool;
4411251881Speter          SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_history_prev, &args,
4412251881Speter                                         FALSE, pool));
4413251881Speter          if (! prev_history)
4414251881Speter            break;
4415251881Speter          bhd = prev_history->fsap_data;
4416251881Speter          if (bhd->is_interesting)
4417251881Speter            break;
4418251881Speter        }
4419251881Speter    }
4420251881Speter
4421251881Speter  *prev_history_p = prev_history;
4422251881Speter  return SVN_NO_ERROR;
4423251881Speter}
4424251881Speter
4425251881Speter
4426251881Speterstatic svn_error_t *
4427251881Speterbase_history_location(const char **path,
4428251881Speter                      svn_revnum_t *revision,
4429251881Speter                      svn_fs_history_t *history,
4430251881Speter                      apr_pool_t *pool)
4431251881Speter{
4432251881Speter  base_history_data_t *bhd = history->fsap_data;
4433251881Speter
4434251881Speter  *path = apr_pstrdup(pool, bhd->path);
4435251881Speter  *revision = bhd->revision;
4436251881Speter  return SVN_NO_ERROR;
4437251881Speter}
4438251881Speter
4439251881Speter
4440251881Speterstatic history_vtable_t history_vtable = {
4441251881Speter  base_history_prev,
4442251881Speter  base_history_location
4443251881Speter};
4444251881Speter
4445251881Speter
4446251881Speter
4447251881Speterstruct closest_copy_args
4448251881Speter{
4449251881Speter  svn_fs_root_t **root_p;
4450251881Speter  const char **path_p;
4451251881Speter  svn_fs_root_t *root;
4452251881Speter  const char *path;
4453251881Speter  apr_pool_t *pool;
4454251881Speter};
4455251881Speter
4456251881Speter
4457251881Speterstatic svn_error_t *
4458251881Spetertxn_body_closest_copy(void *baton, trail_t *trail)
4459251881Speter{
4460251881Speter  struct closest_copy_args *args = baton;
4461251881Speter  svn_fs_root_t *root = args->root;
4462251881Speter  const char *path = args->path;
4463251881Speter  svn_fs_t *fs = root->fs;
4464251881Speter  parent_path_t *parent_path;
4465251881Speter  const svn_fs_id_t *node_id;
4466251881Speter  const char *txn_id, *copy_id;
4467251881Speter  copy_t *copy = NULL;
4468251881Speter  svn_fs_root_t *copy_dst_root;
4469251881Speter  dag_node_t *path_node_in_copy_dst, *copy_dst_node, *copy_dst_root_node;
4470251881Speter  const char *copy_dst_path;
4471251881Speter  svn_revnum_t copy_dst_rev, created_rev;
4472251881Speter  svn_error_t *err;
4473251881Speter
4474251881Speter  *(args->path_p) = NULL;
4475251881Speter  *(args->root_p) = NULL;
4476251881Speter
4477251881Speter  /* Get the transaction ID associated with our root. */
4478251881Speter  if (root->is_txn_root)
4479251881Speter    txn_id = root->txn;
4480251881Speter  else
4481251881Speter    SVN_ERR(svn_fs_base__rev_get_txn_id(&txn_id, fs, root->rev,
4482251881Speter                                        trail, trail->pool));
4483251881Speter
4484251881Speter  /* Open PATH in ROOT -- it must exist. */
4485251881Speter  SVN_ERR(open_path(&parent_path, root, path, 0, txn_id,
4486251881Speter                    trail, trail->pool));
4487251881Speter  node_id = svn_fs_base__dag_get_id(parent_path->node);
4488251881Speter
4489251881Speter  /* Now, examine the copy inheritance rules in play should our path
4490251881Speter     be made mutable in the future (if it isn't already).  This will
4491251881Speter     tell us about the youngest affecting copy.  */
4492251881Speter  SVN_ERR(examine_copy_inheritance(&copy_id, &copy, fs, parent_path,
4493251881Speter                                   trail, trail->pool));
4494251881Speter
4495251881Speter  /* Easy out:  if the copy ID is 0, there's nothing of interest here. */
4496251881Speter  if (((copy_id)[0] == '0') && ((copy_id)[1] == '\0'))
4497251881Speter    return SVN_NO_ERROR;
4498251881Speter
4499251881Speter  /* Fetch our copy if examine_copy_inheritance() didn't do it for us. */
4500251881Speter  if (! copy)
4501251881Speter    SVN_ERR(svn_fs_bdb__get_copy(&copy, fs, copy_id, trail, trail->pool));
4502251881Speter
4503251881Speter  /* Figure out the destination path and revision of the copy operation. */
4504251881Speter  SVN_ERR(svn_fs_base__dag_get_node(&copy_dst_node, fs, copy->dst_noderev_id,
4505251881Speter                                    trail, trail->pool));
4506251881Speter  copy_dst_path = svn_fs_base__dag_get_created_path(copy_dst_node);
4507251881Speter  SVN_ERR(svn_fs_base__dag_get_revision(&copy_dst_rev, copy_dst_node,
4508251881Speter                                        trail, trail->pool));
4509251881Speter
4510251881Speter  /* Turn that revision into a revision root. */
4511251881Speter  SVN_ERR(svn_fs_base__dag_revision_root(&copy_dst_root_node, fs,
4512251881Speter                                         copy_dst_rev, trail, args->pool));
4513251881Speter  copy_dst_root = make_revision_root(fs, copy_dst_rev,
4514251881Speter                                     copy_dst_root_node, args->pool);
4515251881Speter
4516251881Speter  /* It is possible that this node was created from scratch at some
4517251881Speter     revision between COPY_DST_REV and the transaction associated with
4518251881Speter     our ROOT.  Make sure that PATH exists as of COPY_DST_REV and is
4519251881Speter     related to this node-rev. */
4520251881Speter  err = get_dag(&path_node_in_copy_dst, copy_dst_root, path,
4521251881Speter                trail, trail->pool);
4522251881Speter  if (err)
4523251881Speter    {
4524251881Speter      if ((err->apr_err == SVN_ERR_FS_NOT_FOUND)
4525251881Speter          || (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY))
4526251881Speter        {
4527251881Speter          svn_error_clear(err);
4528251881Speter          return SVN_NO_ERROR;
4529251881Speter        }
4530251881Speter      return svn_error_trace(err);
4531251881Speter    }
4532251881Speter  if ((svn_fs_base__dag_node_kind(path_node_in_copy_dst) == svn_node_none)
4533251881Speter      || (! (svn_fs_base__id_check_related
4534251881Speter             (node_id, svn_fs_base__dag_get_id(path_node_in_copy_dst)))))
4535251881Speter    {
4536251881Speter      return SVN_NO_ERROR;
4537251881Speter    }
4538251881Speter
4539251881Speter  /* One final check must be done here.  If you copy a directory and
4540251881Speter     create a new entity somewhere beneath that directory in the same
4541251881Speter     txn, then we can't claim that the copy affected the new entity.
4542251881Speter     For example, if you do:
4543251881Speter
4544251881Speter        copy dir1 dir2
4545251881Speter        create dir2/new-thing
4546251881Speter        commit
4547251881Speter
4548251881Speter     then dir2/new-thing was not affected by the copy of dir1 to dir2.
4549251881Speter     We detect this situation by asking if PATH@COPY_DST_REV's
4550251881Speter     created-rev is COPY_DST_REV, and that node-revision has no
4551251881Speter     predecessors, then there is no relevant closest copy.
4552251881Speter  */
4553251881Speter  SVN_ERR(svn_fs_base__dag_get_revision(&created_rev, path_node_in_copy_dst,
4554251881Speter                                        trail, trail->pool));
4555251881Speter  if (created_rev == copy_dst_rev)
4556251881Speter    {
4557251881Speter      const svn_fs_id_t *pred_id;
4558251881Speter      SVN_ERR(svn_fs_base__dag_get_predecessor_id(&pred_id,
4559251881Speter                                                  path_node_in_copy_dst,
4560251881Speter                                                  trail, trail->pool));
4561251881Speter      if (! pred_id)
4562251881Speter        return SVN_NO_ERROR;
4563251881Speter    }
4564251881Speter
4565251881Speter  *(args->path_p) = apr_pstrdup(args->pool, copy_dst_path);
4566251881Speter  *(args->root_p) = copy_dst_root;
4567251881Speter
4568251881Speter  return SVN_NO_ERROR;
4569251881Speter}
4570251881Speter
4571251881Speter
4572251881Speterstatic svn_error_t *
4573251881Speterbase_closest_copy(svn_fs_root_t **root_p,
4574251881Speter                  const char **path_p,
4575251881Speter                  svn_fs_root_t *root,
4576251881Speter                  const char *path,
4577251881Speter                  apr_pool_t *pool)
4578251881Speter{
4579251881Speter  struct closest_copy_args args;
4580251881Speter  svn_fs_t *fs = root->fs;
4581251881Speter  svn_fs_root_t *closest_root = NULL;
4582251881Speter  const char *closest_path = NULL;
4583251881Speter
4584251881Speter  args.root_p = &closest_root;
4585251881Speter  args.path_p = &closest_path;
4586251881Speter  args.root = root;
4587251881Speter  args.path = path;
4588251881Speter  args.pool = pool;
4589251881Speter  SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_closest_copy, &args,
4590251881Speter                                 FALSE, pool));
4591251881Speter  *root_p = closest_root;
4592251881Speter  *path_p = closest_path;
4593251881Speter  return SVN_NO_ERROR;
4594251881Speter}
4595251881Speter
4596251881Speter
4597251881Speter/* Return a new history object (marked as "interesting") for PATH and
4598251881Speter   REVISION, allocated in POOL, and with its members set to the values
4599251881Speter   of the parameters provided.  Note that PATH and PATH_HINT are not
4600251881Speter   duped into POOL -- it is the responsibility of the caller to ensure
4601251881Speter   that this happens. */
4602251881Speterstatic svn_fs_history_t *
4603251881Speterassemble_history(svn_fs_t *fs,
4604251881Speter                 const char *path,
4605251881Speter                 svn_revnum_t revision,
4606251881Speter                 svn_boolean_t is_interesting,
4607251881Speter                 const char *path_hint,
4608251881Speter                 svn_revnum_t rev_hint,
4609251881Speter                 apr_pool_t *pool)
4610251881Speter{
4611251881Speter  svn_fs_history_t *history = apr_pcalloc(pool, sizeof(*history));
4612251881Speter  base_history_data_t *bhd = apr_pcalloc(pool, sizeof(*bhd));
4613251881Speter  bhd->path = path;
4614251881Speter  bhd->revision = revision;
4615251881Speter  bhd->is_interesting = is_interesting;
4616251881Speter  bhd->path_hint = path_hint;
4617251881Speter  bhd->rev_hint = rev_hint;
4618251881Speter  bhd->fs = fs;
4619251881Speter  history->vtable = &history_vtable;
4620251881Speter  history->fsap_data = bhd;
4621251881Speter  return history;
4622251881Speter}
4623251881Speter
4624251881Speter
4625251881Spetersvn_error_t *
4626251881Spetersvn_fs_base__get_path_kind(svn_node_kind_t *kind,
4627251881Speter                           const char *path,
4628251881Speter                           trail_t *trail,
4629251881Speter                           apr_pool_t *pool)
4630251881Speter{
4631251881Speter  svn_revnum_t head_rev;
4632251881Speter  svn_fs_root_t *root;
4633251881Speter  dag_node_t *root_dir, *path_node;
4634251881Speter  svn_error_t *err;
4635251881Speter
4636251881Speter  /* Get HEAD revision, */
4637251881Speter  SVN_ERR(svn_fs_bdb__youngest_rev(&head_rev, trail->fs, trail, pool));
4638251881Speter
4639251881Speter  /* Then convert it into a root_t, */
4640251881Speter  SVN_ERR(svn_fs_base__dag_revision_root(&root_dir, trail->fs, head_rev,
4641251881Speter                                         trail, pool));
4642251881Speter  root = make_revision_root(trail->fs, head_rev, root_dir, pool);
4643251881Speter
4644251881Speter  /* And get the dag_node for path in the root_t. */
4645251881Speter  err = get_dag(&path_node, root, path, trail, pool);
4646251881Speter  if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND))
4647251881Speter    {
4648251881Speter      svn_error_clear(err);
4649251881Speter      *kind = svn_node_none;
4650251881Speter      return SVN_NO_ERROR;
4651251881Speter    }
4652251881Speter  else if (err)
4653251881Speter    return svn_error_trace(err);
4654251881Speter
4655251881Speter  *kind = svn_fs_base__dag_node_kind(path_node);
4656251881Speter  return SVN_NO_ERROR;
4657251881Speter}
4658251881Speter
4659251881Speter
4660251881Spetersvn_error_t *
4661251881Spetersvn_fs_base__get_path_created_rev(svn_revnum_t *rev,
4662251881Speter                                  const char *path,
4663251881Speter                                  trail_t *trail,
4664251881Speter                                  apr_pool_t *pool)
4665251881Speter{
4666251881Speter  svn_revnum_t head_rev, created_rev;
4667251881Speter  svn_fs_root_t *root;
4668251881Speter  dag_node_t *root_dir, *path_node;
4669251881Speter  svn_error_t *err;
4670251881Speter
4671251881Speter  /* Get HEAD revision, */
4672251881Speter  SVN_ERR(svn_fs_bdb__youngest_rev(&head_rev, trail->fs, trail, pool));
4673251881Speter
4674251881Speter  /* Then convert it into a root_t, */
4675251881Speter  SVN_ERR(svn_fs_base__dag_revision_root(&root_dir, trail->fs, head_rev,
4676251881Speter                                         trail, pool));
4677251881Speter  root = make_revision_root(trail->fs, head_rev, root_dir, pool);
4678251881Speter
4679251881Speter  /* And get the dag_node for path in the root_t. */
4680251881Speter  err = get_dag(&path_node, root, path, trail, pool);
4681251881Speter  if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND))
4682251881Speter    {
4683251881Speter      svn_error_clear(err);
4684251881Speter      *rev = SVN_INVALID_REVNUM;
4685251881Speter      return SVN_NO_ERROR;
4686251881Speter    }
4687251881Speter  else if (err)
4688251881Speter    return svn_error_trace(err);
4689251881Speter
4690251881Speter  /* Find the created_rev of the dag_node. */
4691251881Speter  SVN_ERR(svn_fs_base__dag_get_revision(&created_rev, path_node,
4692251881Speter                                        trail, pool));
4693251881Speter
4694251881Speter  *rev = created_rev;
4695251881Speter  return SVN_NO_ERROR;
4696251881Speter}
4697251881Speter
4698251881Speter
4699251881Speter
4700251881Speter/*** Finding the Origin of a Line of History ***/
4701251881Speter
4702251881Speter/* Set *PREV_PATH and *PREV_REV to the path and revision which
4703251881Speter   represent the location at which PATH in FS was located immediately
4704251881Speter   prior to REVISION iff there was a copy operation (to PATH or one of
4705251881Speter   its parent directories) between that previous location and
4706251881Speter   PATH@REVISION.
4707251881Speter
4708251881Speter   If there was no such copy operation in that portion of PATH's
4709251881Speter   history, set *PREV_PATH to NULL and *PREV_REV to SVN_INVALID_REVNUM.
4710251881Speter
4711251881Speter   WARNING:  Do *not* call this from inside a trail. */
4712251881Speterstatic svn_error_t *
4713251881Speterprev_location(const char **prev_path,
4714251881Speter              svn_revnum_t *prev_rev,
4715251881Speter              svn_fs_t *fs,
4716251881Speter              svn_fs_root_t *root,
4717251881Speter              const char *path,
4718251881Speter              apr_pool_t *pool)
4719251881Speter{
4720251881Speter  const char *copy_path, *copy_src_path, *remainder;
4721251881Speter  svn_fs_root_t *copy_root;
4722251881Speter  svn_revnum_t copy_src_rev;
4723251881Speter
4724251881Speter  /* Ask about the most recent copy which affected PATH@REVISION.  If
4725251881Speter     there was no such copy, we're done.  */
4726251881Speter  SVN_ERR(base_closest_copy(&copy_root, &copy_path, root, path, pool));
4727251881Speter  if (! copy_root)
4728251881Speter    {
4729251881Speter      *prev_rev = SVN_INVALID_REVNUM;
4730251881Speter      *prev_path = NULL;
4731251881Speter      return SVN_NO_ERROR;
4732251881Speter    }
4733251881Speter
4734251881Speter  /* Ultimately, it's not the path of the closest copy's source that
4735251881Speter     we care about -- it's our own path's location in the copy source
4736251881Speter     revision.  So we'll tack the relative path that expresses the
4737251881Speter     difference between the copy destination and our path in the copy
4738251881Speter     revision onto the copy source path to determine this information.
4739251881Speter
4740251881Speter     In other words, if our path is "/branches/my-branch/foo/bar", and
4741251881Speter     we know that the closest relevant copy was a copy of "/trunk" to
4742251881Speter     "/branches/my-branch", then that relative path under the copy
4743251881Speter     destination is "/foo/bar".  Tacking that onto the copy source
4744251881Speter     path tells us that our path was located at "/trunk/foo/bar"
4745251881Speter     before the copy.
4746251881Speter  */
4747251881Speter  SVN_ERR(base_copied_from(&copy_src_rev, &copy_src_path,
4748251881Speter                           copy_root, copy_path, pool));
4749251881Speter  remainder = svn_fspath__skip_ancestor(copy_path, path);
4750251881Speter  *prev_path = svn_fspath__join(copy_src_path, remainder, pool);
4751251881Speter  *prev_rev = copy_src_rev;
4752251881Speter  return SVN_NO_ERROR;
4753251881Speter}
4754251881Speter
4755251881Speter
4756251881Speterstruct id_created_rev_args {
4757251881Speter  svn_revnum_t revision;
4758251881Speter  const svn_fs_id_t *id;
4759251881Speter  const char *path;
4760251881Speter};
4761251881Speter
4762251881Speter
4763251881Speterstatic svn_error_t *
4764251881Spetertxn_body_id_created_rev(void *baton, trail_t *trail)
4765251881Speter{
4766251881Speter  struct id_created_rev_args *args = baton;
4767251881Speter  dag_node_t *node;
4768251881Speter
4769251881Speter  SVN_ERR(svn_fs_base__dag_get_node(&node, trail->fs, args->id,
4770251881Speter                                    trail, trail->pool));
4771251881Speter  return svn_fs_base__dag_get_revision(&(args->revision), node,
4772251881Speter                                       trail, trail->pool);
4773251881Speter}
4774251881Speter
4775251881Speter
4776251881Speterstruct get_set_node_origin_args {
4777251881Speter  const svn_fs_id_t *origin_id;
4778251881Speter  const char *node_id;
4779251881Speter};
4780251881Speter
4781251881Speter
4782251881Speterstatic svn_error_t *
4783251881Spetertxn_body_get_node_origin(void *baton, trail_t *trail)
4784251881Speter{
4785251881Speter  struct get_set_node_origin_args *args = baton;
4786251881Speter  return svn_fs_bdb__get_node_origin(&(args->origin_id), trail->fs,
4787251881Speter                                     args->node_id, trail, trail->pool);
4788251881Speter}
4789251881Speter
4790251881Speterstatic svn_error_t *
4791251881Spetertxn_body_set_node_origin(void *baton, trail_t *trail)
4792251881Speter{
4793251881Speter  struct get_set_node_origin_args *args = baton;
4794251881Speter  return svn_fs_bdb__set_node_origin(trail->fs, args->node_id,
4795251881Speter                                     args->origin_id, trail, trail->pool);
4796251881Speter}
4797251881Speter
4798251881Speterstatic svn_error_t *
4799251881Speterbase_node_origin_rev(svn_revnum_t *revision,
4800251881Speter                     svn_fs_root_t *root,
4801251881Speter                     const char *path,
4802251881Speter                     apr_pool_t *pool)
4803251881Speter{
4804251881Speter  svn_fs_t *fs = root->fs;
4805251881Speter  base_fs_data_t *bfd = fs->fsap_data;
4806251881Speter  struct get_set_node_origin_args args;
4807251881Speter  const svn_fs_id_t *origin_id = NULL;
4808251881Speter  struct id_created_rev_args icr_args;
4809251881Speter
4810251881Speter  /* Canonicalize the input path so that the path-math that
4811251881Speter     prev_location() does below will work. */
4812251881Speter  path = svn_fs__canonicalize_abspath(path, pool);
4813251881Speter
4814253734Speter  /* Special-case the root node (for performance reasons) */
4815253734Speter  if (strcmp(path, "/") == 0)
4816253734Speter    {
4817253734Speter      *revision = 0;
4818253734Speter      return SVN_NO_ERROR;
4819253734Speter    }
4820253734Speter
4821251881Speter  /* If we have support for the node-origins table, we'll try to use
4822251881Speter     it. */
4823251881Speter  if (bfd->format >= SVN_FS_BASE__MIN_NODE_ORIGINS_FORMAT)
4824251881Speter    {
4825251881Speter      const svn_fs_id_t *id;
4826251881Speter      svn_error_t *err;
4827251881Speter
4828251881Speter      SVN_ERR(base_node_id(&id, root, path, pool));
4829251881Speter      args.node_id = svn_fs_base__id_node_id(id);
4830251881Speter      err = svn_fs_base__retry_txn(root->fs, txn_body_get_node_origin, &args,
4831251881Speter                                   FALSE, pool);
4832251881Speter
4833251881Speter      /* If we got a value for the origin node-revision-ID, that's
4834251881Speter         great.  If we didn't, that's sad but non-fatal -- we'll just
4835251881Speter         figure it out the hard way, then record it so we don't have
4836251881Speter         suffer again the next time. */
4837251881Speter      if (! err)
4838251881Speter        {
4839251881Speter          origin_id = args.origin_id;
4840251881Speter        }
4841251881Speter      else if (err->apr_err == SVN_ERR_FS_NO_SUCH_NODE_ORIGIN)
4842251881Speter        {
4843251881Speter          svn_error_clear(err);
4844251881Speter          err = SVN_NO_ERROR;
4845251881Speter        }
4846251881Speter      SVN_ERR(err);
4847251881Speter    }
4848251881Speter
4849251881Speter  /* If we haven't yet found a node origin ID, we'll go spelunking for one. */
4850251881Speter  if (! origin_id)
4851251881Speter    {
4852251881Speter      svn_fs_root_t *curroot = root;
4853251881Speter      apr_pool_t *subpool = svn_pool_create(pool);
4854251881Speter      apr_pool_t *predidpool = svn_pool_create(pool);
4855251881Speter      svn_stringbuf_t *lastpath =
4856251881Speter        svn_stringbuf_create(path, pool);
4857251881Speter      svn_revnum_t lastrev = SVN_INVALID_REVNUM;
4858251881Speter      const svn_fs_id_t *pred_id;
4859251881Speter
4860251881Speter      /* Walk the closest-copy chain back to the first copy in our history.
4861251881Speter
4862251881Speter         NOTE: We merely *assume* that this is faster than walking the
4863251881Speter         predecessor chain, because we *assume* that copies of parent
4864251881Speter         directories happen less often than modifications to a given item. */
4865251881Speter      while (1)
4866251881Speter        {
4867251881Speter          svn_revnum_t currev;
4868251881Speter          const char *curpath = lastpath->data;
4869251881Speter
4870251881Speter          /* Get a root pointing to LASTREV.  (The first time around,
4871251881Speter             LASTREV is invalid, but that's cool because CURROOT is
4872251881Speter             already initialized.)  */
4873251881Speter          if (SVN_IS_VALID_REVNUM(lastrev))
4874251881Speter            SVN_ERR(svn_fs_base__revision_root(&curroot, fs,
4875251881Speter                                               lastrev, subpool));
4876251881Speter
4877251881Speter          /* Find the previous location using the closest-copy shortcut. */
4878251881Speter          SVN_ERR(prev_location(&curpath, &currev, fs, curroot,
4879251881Speter                                curpath, subpool));
4880251881Speter          if (! curpath)
4881251881Speter            break;
4882251881Speter
4883251881Speter          /* Update our LASTPATH and LASTREV variables (which survive
4884251881Speter             SUBPOOL). */
4885251881Speter          svn_stringbuf_set(lastpath, curpath);
4886251881Speter          lastrev = currev;
4887251881Speter        }
4888251881Speter
4889251881Speter      /* Walk the predecessor links back to origin. */
4890251881Speter      SVN_ERR(base_node_id(&pred_id, curroot, lastpath->data, pool));
4891251881Speter      while (1)
4892251881Speter        {
4893251881Speter          struct txn_pred_id_args pid_args;
4894251881Speter          svn_pool_clear(subpool);
4895251881Speter          pid_args.id = pred_id;
4896251881Speter          pid_args.pred_id = NULL;
4897251881Speter          pid_args.pool = subpool;
4898251881Speter          SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_id, &pid_args,
4899251881Speter                                         FALSE, subpool));
4900251881Speter          if (! pid_args.pred_id)
4901251881Speter            break;
4902251881Speter          svn_pool_clear(predidpool);
4903251881Speter          pred_id = svn_fs_base__id_copy(pid_args.pred_id, predidpool);
4904251881Speter        }
4905251881Speter
4906251881Speter      /* Okay.  PRED_ID should hold our origin ID now.  */
4907251881Speter      origin_id = svn_fs_base__id_copy(pred_id, pool);
4908251881Speter
4909251881Speter      /* If our filesystem version supports it, let's remember this
4910251881Speter         value from now on.  */
4911251881Speter      if (bfd->format >= SVN_FS_BASE__MIN_NODE_ORIGINS_FORMAT)
4912251881Speter        {
4913251881Speter          args.origin_id = origin_id;
4914251881Speter          SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_set_node_origin,
4915251881Speter                                         &args, TRUE, subpool));
4916251881Speter        }
4917251881Speter
4918251881Speter      svn_pool_destroy(predidpool);
4919251881Speter      svn_pool_destroy(subpool);
4920251881Speter    }
4921251881Speter
4922251881Speter  /* Okay.  We have an origin node-revision-ID.  Let's get a created
4923251881Speter     revision from it. */
4924251881Speter  icr_args.id = origin_id;
4925251881Speter  SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_id_created_rev, &icr_args,
4926251881Speter                                 TRUE, pool));
4927251881Speter  *revision = icr_args.revision;
4928251881Speter  return SVN_NO_ERROR;
4929251881Speter}
4930251881Speter
4931251881Speter
4932251881Speter
4933251881Speter/* Mergeinfo Queries */
4934251881Speter
4935251881Speter
4936251881Speter/* Examine directory NODE's immediately children for mergeinfo.
4937251881Speter
4938251881Speter   For those which have explicit mergeinfo, add their mergeinfo to
4939251881Speter   RESULT_CATALOG (allocated in RESULT_CATALOG's pool).
4940251881Speter
4941251881Speter   For those which don't, but sit atop trees which contain mergeinfo
4942251881Speter   somewhere deeper, add them to *CHILDREN_ATOP_MERGEINFO_TREES, a
4943251881Speter   hash mapping dirent names to dag_node_t * objects, allocated
4944251881Speter   from that hash's pool.
4945251881Speter
4946251881Speter   For those which neither have explicit mergeinfo nor sit atop trees
4947251881Speter   which contain mergeinfo, ignore them.
4948251881Speter
4949251881Speter   Use TRAIL->pool for temporary allocations. */
4950251881Speter
4951251881Speterstruct get_mergeinfo_data_and_entries_baton
4952251881Speter{
4953251881Speter  svn_mergeinfo_catalog_t result_catalog;
4954251881Speter  apr_hash_t *children_atop_mergeinfo_trees;
4955251881Speter  dag_node_t *node;
4956251881Speter  const char *node_path;
4957251881Speter};
4958251881Speter
4959251881Speterstatic svn_error_t *
4960251881Spetertxn_body_get_mergeinfo_data_and_entries(void *baton, trail_t *trail)
4961251881Speter{
4962251881Speter  struct get_mergeinfo_data_and_entries_baton *args = baton;
4963251881Speter  dag_node_t *node = args->node;
4964251881Speter  apr_hash_t *entries;
4965251881Speter  apr_hash_index_t *hi;
4966251881Speter  apr_pool_t *iterpool = svn_pool_create(trail->pool);
4967251881Speter  apr_pool_t *result_pool = apr_hash_pool_get(args->result_catalog);
4968251881Speter  apr_pool_t *children_pool =
4969251881Speter    apr_hash_pool_get(args->children_atop_mergeinfo_trees);
4970251881Speter
4971251881Speter  SVN_ERR_ASSERT(svn_fs_base__dag_node_kind(node) == svn_node_dir);
4972251881Speter
4973251881Speter  SVN_ERR(svn_fs_base__dag_dir_entries(&entries, node, trail, trail->pool));
4974251881Speter  for (hi = apr_hash_first(trail->pool, entries); hi; hi = apr_hash_next(hi))
4975251881Speter    {
4976251881Speter      void *val;
4977251881Speter      svn_fs_dirent_t *dirent;
4978251881Speter      const svn_fs_id_t *child_id;
4979251881Speter      dag_node_t *child_node;
4980251881Speter      svn_boolean_t has_mergeinfo;
4981251881Speter      apr_int64_t kid_count;
4982251881Speter
4983251881Speter      svn_pool_clear(iterpool);
4984251881Speter      apr_hash_this(hi, NULL, NULL, &val);
4985251881Speter      dirent = val;
4986251881Speter      child_id = dirent->id;
4987251881Speter
4988251881Speter      /* Get the node for this child. */
4989251881Speter      SVN_ERR(svn_fs_base__dag_get_node(&child_node, trail->fs, child_id,
4990251881Speter                                        trail, iterpool));
4991251881Speter
4992251881Speter      /* Query the child node's mergeinfo stats. */
4993251881Speter      SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(&has_mergeinfo, &kid_count,
4994251881Speter                                                   child_node, trail,
4995251881Speter                                                   iterpool));
4996251881Speter
4997251881Speter      /* If the child has mergeinfo, add it to the result catalog. */
4998251881Speter      if (has_mergeinfo)
4999251881Speter        {
5000251881Speter          apr_hash_t *plist;
5001251881Speter          svn_mergeinfo_t child_mergeinfo;
5002251881Speter          svn_string_t *pval;
5003251881Speter          svn_error_t *err;
5004251881Speter
5005251881Speter          SVN_ERR(svn_fs_base__dag_get_proplist(&plist, child_node,
5006251881Speter                                                trail, iterpool));
5007251881Speter          pval = svn_hash_gets(plist, SVN_PROP_MERGEINFO);
5008251881Speter          if (! pval)
5009251881Speter            {
5010251881Speter              svn_string_t *id_str = svn_fs_base__id_unparse(child_id,
5011251881Speter                                                             iterpool);
5012251881Speter              return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
5013251881Speter                                       _("Node-revision '%s' claims to have "
5014251881Speter                                         "mergeinfo but doesn't"),
5015251881Speter                                       id_str->data);
5016251881Speter            }
5017251881Speter          /* Issue #3896: If syntactically invalid mergeinfo is present on
5018251881Speter             CHILD_NODE then treat it as if no mergeinfo is present rather
5019251881Speter             than raising a parse error. */
5020251881Speter          err = svn_mergeinfo_parse(&child_mergeinfo, pval->data,
5021251881Speter                                    result_pool);
5022251881Speter          if (err)
5023251881Speter            {
5024251881Speter              if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
5025251881Speter                svn_error_clear(err);
5026251881Speter              else
5027251881Speter                return svn_error_trace(err);
5028251881Speter            }
5029251881Speter          else
5030251881Speter            {
5031251881Speter              svn_hash_sets(args->result_catalog,
5032251881Speter                            svn_fspath__join(args->node_path, dirent->name,
5033251881Speter                                             result_pool),
5034251881Speter                            child_mergeinfo);
5035251881Speter            }
5036251881Speter        }
5037251881Speter
5038251881Speter      /* If the child has descendants with mergeinfo -- that is, if
5039251881Speter         the count of descendants beneath it carrying mergeinfo, not
5040251881Speter         including itself, is non-zero -- then add it to the
5041251881Speter         children_atop_mergeinfo_trees hash to be crawled later. */
5042251881Speter      if ((kid_count - (has_mergeinfo ? 1 : 0)) > 0)
5043251881Speter        {
5044251881Speter          if (svn_fs_base__dag_node_kind(child_node) != svn_node_dir)
5045251881Speter            {
5046251881Speter              svn_string_t *id_str = svn_fs_base__id_unparse(child_id,
5047251881Speter                                                             iterpool);
5048251881Speter              return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
5049251881Speter                                       _("Node-revision '%s' claims to sit "
5050251881Speter                                         "atop a tree containing mergeinfo "
5051251881Speter                                         "but is not a directory"),
5052251881Speter                                       id_str->data);
5053251881Speter            }
5054251881Speter          svn_hash_sets(args->children_atop_mergeinfo_trees,
5055251881Speter                        apr_pstrdup(children_pool, dirent->name),
5056251881Speter                        svn_fs_base__dag_dup(child_node, children_pool));
5057251881Speter        }
5058251881Speter    }
5059251881Speter
5060251881Speter  svn_pool_destroy(iterpool);
5061251881Speter  return SVN_NO_ERROR;
5062251881Speter}
5063251881Speter
5064251881Speterstatic svn_error_t *
5065251881Spetercrawl_directory_for_mergeinfo(svn_fs_t *fs,
5066251881Speter                              dag_node_t *node,
5067251881Speter                              const char *node_path,
5068251881Speter                              svn_mergeinfo_catalog_t result_catalog,
5069251881Speter                              apr_pool_t *pool)
5070251881Speter{
5071251881Speter  struct get_mergeinfo_data_and_entries_baton gmdae_args;
5072251881Speter  apr_hash_t *children_atop_mergeinfo_trees = apr_hash_make(pool);
5073251881Speter  apr_hash_index_t *hi;
5074251881Speter  apr_pool_t *iterpool;
5075251881Speter
5076251881Speter  /* Add mergeinfo for immediate children that have it, and fetch
5077251881Speter     immediate children that *don't* have it but sit atop trees that do. */
5078251881Speter  gmdae_args.result_catalog = result_catalog;
5079251881Speter  gmdae_args.children_atop_mergeinfo_trees = children_atop_mergeinfo_trees;
5080251881Speter  gmdae_args.node = node;
5081251881Speter  gmdae_args.node_path = node_path;
5082251881Speter  SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_mergeinfo_data_and_entries,
5083251881Speter                                 &gmdae_args, FALSE, pool));
5084251881Speter
5085251881Speter  /* If no children sit atop trees with mergeinfo, we're done.
5086251881Speter     Otherwise, recurse on those children. */
5087251881Speter
5088251881Speter  if (apr_hash_count(children_atop_mergeinfo_trees) == 0)
5089251881Speter    return SVN_NO_ERROR;
5090251881Speter
5091251881Speter  iterpool = svn_pool_create(pool);
5092251881Speter  for (hi = apr_hash_first(pool, children_atop_mergeinfo_trees);
5093251881Speter       hi;
5094251881Speter       hi = apr_hash_next(hi))
5095251881Speter    {
5096251881Speter      const void *key;
5097251881Speter      void *val;
5098251881Speter      svn_pool_clear(iterpool);
5099251881Speter      apr_hash_this(hi, &key, NULL, &val);
5100251881Speter      SVN_ERR(crawl_directory_for_mergeinfo(fs, val,
5101251881Speter                                            svn_fspath__join(node_path, key,
5102251881Speter                                                             iterpool),
5103251881Speter                                            result_catalog, iterpool));
5104251881Speter    }
5105251881Speter  svn_pool_destroy(iterpool);
5106251881Speter  return SVN_NO_ERROR;
5107251881Speter}
5108251881Speter
5109251881Speter
5110251881Speter/* Calculate the mergeinfo for PATH under revision ROOT using
5111251881Speter   inheritance type INHERIT.  Set *MERGEINFO to the mergeinfo, or to
5112251881Speter   NULL if there is none.  Results are allocated in POOL; TRAIL->pool
5113251881Speter   is used for temporary allocations.  */
5114251881Speter
5115251881Speterstruct get_mergeinfo_for_path_baton
5116251881Speter{
5117251881Speter  svn_mergeinfo_t *mergeinfo;
5118251881Speter  svn_fs_root_t *root;
5119251881Speter  const char *path;
5120251881Speter  svn_mergeinfo_inheritance_t inherit;
5121251881Speter  svn_boolean_t adjust_inherited_mergeinfo;
5122251881Speter  apr_pool_t *pool;
5123251881Speter};
5124251881Speter
5125251881Speterstatic svn_error_t *
5126251881Spetertxn_body_get_mergeinfo_for_path(void *baton, trail_t *trail)
5127251881Speter{
5128251881Speter  struct get_mergeinfo_for_path_baton *args = baton;
5129251881Speter  parent_path_t *parent_path, *nearest_ancestor;
5130251881Speter  apr_hash_t *proplist;
5131251881Speter  svn_string_t *mergeinfo_string;
5132251881Speter  apr_pool_t *iterpool;
5133251881Speter  dag_node_t *node = NULL;
5134251881Speter
5135251881Speter  *(args->mergeinfo) = NULL;
5136251881Speter
5137251881Speter  SVN_ERR(open_path(&parent_path, args->root, args->path, 0,
5138251881Speter                    NULL, trail, trail->pool));
5139251881Speter
5140251881Speter  /* Init the nearest ancestor. */
5141251881Speter  nearest_ancestor = parent_path;
5142251881Speter  if (args->inherit == svn_mergeinfo_nearest_ancestor)
5143251881Speter    {
5144251881Speter      if (! parent_path->parent)
5145251881Speter        return SVN_NO_ERROR;
5146251881Speter      nearest_ancestor = parent_path->parent;
5147251881Speter    }
5148251881Speter
5149251881Speter  iterpool = svn_pool_create(trail->pool);
5150251881Speter  while (TRUE)
5151251881Speter    {
5152251881Speter      svn_boolean_t has_mergeinfo;
5153251881Speter      apr_int64_t count;
5154251881Speter
5155251881Speter      svn_pool_clear(iterpool);
5156251881Speter
5157251881Speter      node = nearest_ancestor->node;
5158251881Speter      SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(&has_mergeinfo, &count,
5159251881Speter                                                   node, trail, iterpool));
5160251881Speter      if (has_mergeinfo)
5161251881Speter        break;
5162251881Speter
5163251881Speter      /* No need to loop if we're looking for explicit mergeinfo. */
5164251881Speter      if (args->inherit == svn_mergeinfo_explicit)
5165251881Speter        {
5166251881Speter          svn_pool_destroy(iterpool);
5167251881Speter          return SVN_NO_ERROR;
5168251881Speter        }
5169251881Speter
5170251881Speter      nearest_ancestor = nearest_ancestor->parent;
5171251881Speter
5172251881Speter      /* Run out?  There's no mergeinfo. */
5173251881Speter      if (! nearest_ancestor)
5174251881Speter        {
5175251881Speter          svn_pool_destroy(iterpool);
5176251881Speter          return SVN_NO_ERROR;
5177251881Speter        }
5178251881Speter    }
5179251881Speter  svn_pool_destroy(iterpool);
5180251881Speter
5181251881Speter  SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, node, trail, trail->pool));
5182251881Speter  mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
5183251881Speter  if (! mergeinfo_string)
5184251881Speter    {
5185251881Speter      svn_string_t *id_str =
5186251881Speter        svn_fs_base__id_unparse(svn_fs_base__dag_get_id(node), trail->pool);
5187251881Speter      return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
5188251881Speter                               _("Node-revision '%s' claims to have "
5189251881Speter                                 "mergeinfo but doesn't"), id_str->data);
5190251881Speter    }
5191251881Speter
5192251881Speter  /* Parse the mergeinfo; store the result in ARGS->MERGEINFO. */
5193251881Speter  {
5194251881Speter    /* Issue #3896: If a node has syntactically invalid mergeinfo, then
5195251881Speter       treat it as if no mergeinfo is present rather than raising a parse
5196251881Speter       error. */
5197251881Speter    svn_error_t *err = svn_mergeinfo_parse(args->mergeinfo,
5198251881Speter                                           mergeinfo_string->data,
5199251881Speter                                           args->pool);
5200251881Speter    if (err)
5201251881Speter      {
5202251881Speter        if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
5203251881Speter          {
5204251881Speter            svn_error_clear(err);
5205251881Speter            err = NULL;
5206251881Speter            args->mergeinfo = NULL;
5207251881Speter          }
5208251881Speter        return svn_error_trace(err);
5209251881Speter      }
5210251881Speter  }
5211251881Speter
5212251881Speter  /* If our nearest ancestor is the very path we inquired about, we
5213251881Speter     can return the mergeinfo results directly.  Otherwise, we're
5214251881Speter     inheriting the mergeinfo, so we need to a) remove non-inheritable
5215251881Speter     ranges and b) telescope the merged-from paths. */
5216251881Speter  if (args->adjust_inherited_mergeinfo && (nearest_ancestor != parent_path))
5217251881Speter    {
5218251881Speter      svn_mergeinfo_t tmp_mergeinfo;
5219251881Speter
5220251881Speter      SVN_ERR(svn_mergeinfo_inheritable2(&tmp_mergeinfo, *args->mergeinfo,
5221251881Speter                                         NULL, SVN_INVALID_REVNUM,
5222251881Speter                                         SVN_INVALID_REVNUM, TRUE,
5223251881Speter                                         trail->pool, trail->pool));
5224251881Speter      SVN_ERR(svn_fs__append_to_merged_froms(args->mergeinfo, tmp_mergeinfo,
5225251881Speter                                             parent_path_relpath(
5226251881Speter                                               parent_path, nearest_ancestor,
5227251881Speter                                               trail->pool),
5228251881Speter                                             args->pool));
5229251881Speter    }
5230251881Speter
5231251881Speter  return SVN_NO_ERROR;
5232251881Speter}
5233251881Speter
5234251881Speter/* Set **NODE to the dag node for PATH in ROOT (allocated in POOL),
5235251881Speter   and query its mergeinfo stats, setting HAS_MERGEINFO and
5236251881Speter   CHILD_MERGEINFO_COUNT appropriately. */
5237251881Speter
5238251881Speterstruct get_node_mergeinfo_stats_baton
5239251881Speter{
5240251881Speter  dag_node_t *node;
5241251881Speter  svn_boolean_t has_mergeinfo;
5242251881Speter  apr_int64_t child_mergeinfo_count;
5243251881Speter  svn_fs_root_t *root;
5244251881Speter  const char *path;
5245251881Speter};
5246251881Speter
5247251881Speterstatic svn_error_t *
5248251881Spetertxn_body_get_node_mergeinfo_stats(void *baton, trail_t *trail)
5249251881Speter{
5250251881Speter  struct get_node_mergeinfo_stats_baton *args = baton;
5251251881Speter
5252251881Speter  SVN_ERR(get_dag(&(args->node), args->root, args->path,
5253251881Speter                  trail, trail->pool));
5254251881Speter  return svn_fs_base__dag_get_mergeinfo_stats(&(args->has_mergeinfo),
5255251881Speter                                              &(args->child_mergeinfo_count),
5256251881Speter                                              args->node, trail,
5257251881Speter                                              trail->pool);
5258251881Speter}
5259251881Speter
5260251881Speter
5261251881Speter/* Get the mergeinfo for a set of paths, returned in
5262251881Speter   *MERGEINFO_CATALOG.  Returned values are allocated in POOL, while
5263251881Speter   temporary values are allocated in a sub-pool. */
5264251881Speterstatic svn_error_t *
5265251881Speterget_mergeinfos_for_paths(svn_fs_root_t *root,
5266251881Speter                         svn_mergeinfo_catalog_t *mergeinfo_catalog,
5267251881Speter                         const apr_array_header_t *paths,
5268251881Speter                         svn_mergeinfo_inheritance_t inherit,
5269251881Speter                         svn_boolean_t include_descendants,
5270251881Speter                         svn_boolean_t adjust_inherited_mergeinfo,
5271251881Speter                         apr_pool_t *result_pool,
5272251881Speter                         apr_pool_t *scratch_pool)
5273251881Speter{
5274251881Speter  svn_mergeinfo_catalog_t result_catalog = apr_hash_make(result_pool);
5275251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5276251881Speter  int i;
5277251881Speter
5278251881Speter  for (i = 0; i < paths->nelts; i++)
5279251881Speter    {
5280251881Speter      svn_mergeinfo_t path_mergeinfo;
5281251881Speter      struct get_mergeinfo_for_path_baton gmfp_args;
5282251881Speter      const char *path = APR_ARRAY_IDX(paths, i, const char *);
5283251881Speter
5284251881Speter      svn_pool_clear(iterpool);
5285251881Speter
5286251881Speter      path = svn_fs__canonicalize_abspath(path, iterpool);
5287251881Speter
5288251881Speter      /* Get the mergeinfo for PATH itself. */
5289251881Speter      gmfp_args.mergeinfo = &path_mergeinfo;
5290251881Speter      gmfp_args.root = root;
5291251881Speter      gmfp_args.path = path;
5292251881Speter      gmfp_args.inherit = inherit;
5293251881Speter      gmfp_args.pool = result_pool;
5294251881Speter      gmfp_args.adjust_inherited_mergeinfo = adjust_inherited_mergeinfo;
5295251881Speter      SVN_ERR(svn_fs_base__retry_txn(root->fs,
5296251881Speter                                     txn_body_get_mergeinfo_for_path,
5297251881Speter                                     &gmfp_args, FALSE, iterpool));
5298251881Speter      if (path_mergeinfo)
5299251881Speter        svn_hash_sets(result_catalog, apr_pstrdup(result_pool, path),
5300251881Speter                      path_mergeinfo);
5301251881Speter
5302251881Speter      /* If we're including descendants, do so. */
5303251881Speter      if (include_descendants)
5304251881Speter        {
5305251881Speter          svn_boolean_t do_crawl;
5306251881Speter          struct get_node_mergeinfo_stats_baton gnms_args;
5307251881Speter
5308251881Speter          /* Query the node and its mergeinfo stats. */
5309251881Speter          gnms_args.root = root;
5310251881Speter          gnms_args.path = path;
5311251881Speter          SVN_ERR(svn_fs_base__retry_txn(root->fs,
5312251881Speter                                         txn_body_get_node_mergeinfo_stats,
5313251881Speter                                         &gnms_args, FALSE, iterpool));
5314251881Speter
5315251881Speter          /* Determine if there's anything worth crawling here. */
5316251881Speter          if (svn_fs_base__dag_node_kind(gnms_args.node) != svn_node_dir)
5317251881Speter            do_crawl = FALSE;
5318251881Speter          else
5319251881Speter            do_crawl = ((gnms_args.child_mergeinfo_count > 1)
5320251881Speter                        || ((gnms_args.child_mergeinfo_count == 1)
5321251881Speter                            && (! gnms_args.has_mergeinfo)));
5322251881Speter
5323251881Speter          /* If it's worth crawling, crawl. */
5324251881Speter          if (do_crawl)
5325251881Speter            SVN_ERR(crawl_directory_for_mergeinfo(root->fs, gnms_args.node,
5326251881Speter                                                  path, result_catalog,
5327251881Speter                                                  iterpool));
5328251881Speter        }
5329251881Speter    }
5330251881Speter  svn_pool_destroy(iterpool);
5331251881Speter
5332251881Speter  *mergeinfo_catalog = result_catalog;
5333251881Speter  return SVN_NO_ERROR;
5334251881Speter}
5335251881Speter
5336251881Speter
5337251881Speter/* Implements svn_fs_get_mergeinfo. */
5338251881Speterstatic svn_error_t *
5339251881Speterbase_get_mergeinfo(svn_mergeinfo_catalog_t *catalog,
5340251881Speter                   svn_fs_root_t *root,
5341251881Speter                   const apr_array_header_t *paths,
5342251881Speter                   svn_mergeinfo_inheritance_t inherit,
5343251881Speter                   svn_boolean_t include_descendants,
5344251881Speter                   svn_boolean_t adjust_inherited_mergeinfo,
5345251881Speter                   apr_pool_t *result_pool,
5346251881Speter                   apr_pool_t *scratch_pool)
5347251881Speter{
5348251881Speter  /* Verify that our filesystem version supports mergeinfo stuff. */
5349251881Speter  SVN_ERR(svn_fs_base__test_required_feature_format
5350251881Speter          (root->fs, "mergeinfo", SVN_FS_BASE__MIN_MERGEINFO_FORMAT));
5351251881Speter
5352251881Speter  /* We require a revision root. */
5353251881Speter  if (root->is_txn_root)
5354251881Speter    return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
5355251881Speter
5356251881Speter  /* Retrieve a path -> mergeinfo mapping. */
5357251881Speter  return get_mergeinfos_for_paths(root, catalog, paths,
5358251881Speter                                  inherit, include_descendants,
5359251881Speter                                  adjust_inherited_mergeinfo,
5360251881Speter                                  result_pool, scratch_pool);
5361251881Speter}
5362251881Speter
5363251881Speter
5364251881Speter
5365251881Speter/* Creating root objects.  */
5366251881Speter
5367251881Speter
5368251881Speterstatic root_vtable_t root_vtable = {
5369251881Speter  base_paths_changed,
5370251881Speter  base_check_path,
5371251881Speter  base_node_history,
5372251881Speter  base_node_id,
5373251881Speter  base_node_created_rev,
5374251881Speter  base_node_origin_rev,
5375251881Speter  base_node_created_path,
5376251881Speter  base_delete_node,
5377251881Speter  base_copied_from,
5378251881Speter  base_closest_copy,
5379251881Speter  base_node_prop,
5380251881Speter  base_node_proplist,
5381251881Speter  base_change_node_prop,
5382251881Speter  base_props_changed,
5383251881Speter  base_dir_entries,
5384251881Speter  base_make_dir,
5385251881Speter  base_copy,
5386251881Speter  base_revision_link,
5387251881Speter  base_file_length,
5388251881Speter  base_file_checksum,
5389251881Speter  base_file_contents,
5390251881Speter  NULL,
5391251881Speter  base_make_file,
5392251881Speter  base_apply_textdelta,
5393251881Speter  base_apply_text,
5394251881Speter  base_contents_changed,
5395251881Speter  base_get_file_delta_stream,
5396251881Speter  base_merge,
5397251881Speter  base_get_mergeinfo,
5398251881Speter};
5399251881Speter
5400251881Speter
5401251881Speter/* Construct a new root object in FS, allocated from POOL.  */
5402251881Speterstatic svn_fs_root_t *
5403251881Spetermake_root(svn_fs_t *fs,
5404251881Speter          apr_pool_t *pool)
5405251881Speter{
5406251881Speter  svn_fs_root_t *root = apr_pcalloc(pool, sizeof(*root));
5407251881Speter  base_root_data_t *brd = apr_palloc(pool, sizeof(*brd));
5408251881Speter
5409251881Speter  root->fs = fs;
5410251881Speter  root->pool = pool;
5411251881Speter
5412251881Speter  /* Init the node ID cache. */
5413251881Speter  brd->node_cache = apr_hash_make(pool);
5414251881Speter  brd->node_cache_idx = 0;
5415251881Speter  root->vtable = &root_vtable;
5416251881Speter  root->fsap_data = brd;
5417251881Speter
5418251881Speter  return root;
5419251881Speter}
5420251881Speter
5421251881Speter
5422251881Speter/* Construct a root object referring to the root of REVISION in FS,
5423251881Speter   whose root directory is ROOT_DIR.  Create the new root in POOL.  */
5424251881Speterstatic svn_fs_root_t *
5425251881Spetermake_revision_root(svn_fs_t *fs,
5426251881Speter                   svn_revnum_t rev,
5427251881Speter                   dag_node_t *root_dir,
5428251881Speter                   apr_pool_t *pool)
5429251881Speter{
5430251881Speter  svn_fs_root_t *root = make_root(fs, pool);
5431251881Speter  base_root_data_t *brd = root->fsap_data;
5432251881Speter
5433251881Speter  root->is_txn_root = FALSE;
5434251881Speter  root->rev = rev;
5435251881Speter  brd->root_dir = root_dir;
5436251881Speter
5437251881Speter  return root;
5438251881Speter}
5439251881Speter
5440251881Speter
5441251881Speter/* Construct a root object referring to the root of the transaction
5442251881Speter   named TXN and based on revision BASE_REV in FS.  FLAGS represents
5443251881Speter   the behavior of the transaction.  Create the new root in POOL.  */
5444251881Speterstatic svn_fs_root_t *
5445251881Spetermake_txn_root(svn_fs_t *fs,
5446251881Speter              const char *txn,
5447251881Speter              svn_revnum_t base_rev,
5448251881Speter              apr_uint32_t flags,
5449251881Speter              apr_pool_t *pool)
5450251881Speter{
5451251881Speter  svn_fs_root_t *root = make_root(fs, pool);
5452251881Speter  root->is_txn_root = TRUE;
5453251881Speter  root->txn = apr_pstrdup(root->pool, txn);
5454251881Speter  root->txn_flags = flags;
5455251881Speter  root->rev = base_rev;
5456251881Speter
5457251881Speter  return root;
5458251881Speter}
5459