~ubuntu-branches/debian/sid/subversion/sid

« back to all changes in this revision

Viewing changes to subversion/libsvn_fs_fs/tree.c

  • Committer: Package Import Robot
  • Author(s): James McCoy, Peter Samuelson, James McCoy
  • Date: 2014-01-12 19:48:33 UTC
  • mfrom: (0.2.10)
  • Revision ID: package-import@ubuntu.com-20140112194833-w3axfwksn296jn5x
Tags: 1.8.5-1
[ Peter Samuelson ]
* New upstream release.  (Closes: #725787) Rediff patches:
  - Remove apr-abi1 (applied upstream), rename apr-abi2 to apr-abi
  - Remove loosen-sqlite-version-check (shouldn't be needed)
  - Remove java-osgi-metadata (applied upstream)
  - svnmucc prompts for a changelog if none is provided. (Closes: #507430)
  - Remove fix-bdb-version-detection, upstream uses "apu-config --dbm-libs"
  - Remove ruby-test-wc (applied upstream)
  - Fix “svn diff -r N file” when file has svn:mime-type set.
    (Closes: #734163)
  - Support specifying an encoding for mod_dav_svn's environment in which
    hooks are run.  (Closes: #601544)
  - Fix ordering of “svnadmin dump” paths with certain APR versions.
    (Closes: #687291)
  - Provide a better error message when authentication fails with an
    svn+ssh:// URL.  (Closes: #273874)
  - Updated Polish translations.  (Closes: #690815)

[ James McCoy ]
* Remove all traces of libneon, replaced by libserf.
* patches/sqlite_3.8.x_workaround: Upstream fix for wc-queries-test test
  failurse.
* Run configure with --with-apache-libexecdir, which allows removing part of
  patches/rpath.
* Re-enable auth-test as upstream has fixed the problem of picking up
  libraries from the environment rather than the build tree.
  (Closes: #654172)
* Point LD_LIBRARY_PATH at the built auth libraries when running the svn
  command during the build.  (Closes: #678224)
* Add a NEWS entry describing how to configure mod_dav_svn to understand
  UTF-8.  (Closes: #566148)
* Remove ancient transitional package, libsvn-ruby.
* Enable compatibility with Sqlite3 versions back to Wheezy.
* Enable hardening flags.  (Closes: #734918)
* patches/build-fixes: Enable verbose build logs.
* Build against the default ruby version.  (Closes: #722393)

Show diffs side-by-side

added added

removed removed

Lines of Context:
41
41
#include <apr_pools.h>
42
42
#include <apr_hash.h>
43
43
 
 
44
#include "svn_hash.h"
44
45
#include "svn_private_config.h"
45
46
#include "svn_pools.h"
46
47
#include "svn_error.h"
47
 
#include "svn_ctype.h"
48
 
#include "svn_dirent_uri.h"
49
48
#include "svn_path.h"
50
49
#include "svn_mergeinfo.h"
51
50
#include "svn_fs.h"
58
57
#include "tree.h"
59
58
#include "fs_fs.h"
60
59
#include "id.h"
 
60
#include "temp_serializer.h"
61
61
 
62
62
#include "private/svn_mergeinfo_private.h"
 
63
#include "private/svn_subr_private.h"
63
64
#include "private/svn_fs_util.h"
64
65
#include "private/svn_fspath.h"
65
66
#include "../libsvn_fs/fs-loader.h"
115
116
 
116
117
typedef struct fs_txn_root_data_t
117
118
{
 
119
  const char *txn_id;
 
120
 
118
121
  /* Cache of txn DAG nodes (without their nested noderevs, because
119
 
   * it's mutable). */
 
122
   * it's mutable). Same keys/values as ffd->rev_node_cache. */
120
123
  svn_cache__t *txn_node_cache;
121
124
} fs_txn_root_data_t;
122
125
 
123
126
/* Declared here to resolve the circular dependencies. */
124
 
static svn_error_t * get_dag(dag_node_t **dag_node_p, svn_fs_root_t *root,
125
 
                             const char *path, apr_pool_t *pool);
 
127
static svn_error_t * get_dag(dag_node_t **dag_node_p,
 
128
                             svn_fs_root_t *root,
 
129
                             const char *path,
 
130
                             svn_boolean_t needs_lock_cache,
 
131
                             apr_pool_t *pool);
126
132
 
127
133
static svn_fs_root_t *make_revision_root(svn_fs_t *fs, svn_revnum_t rev,
128
134
                                         dag_node_t *root_dir,
136
142
 
137
143
/*** Node Caching ***/
138
144
 
 
145
/* 1st level cache */
 
146
 
 
147
/* An entry in the first-level cache.  REVISION and PATH form the key that
 
148
   will ultimately be matched.
 
149
 */
 
150
typedef struct cache_entry_t
 
151
{
 
152
  /* hash value derived from PATH, REVISION.
 
153
     Used to short-circuit failed lookups. */
 
154
  apr_uint32_t hash_value;
 
155
 
 
156
  /* revision to which the NODE belongs */
 
157
  svn_revnum_t revision;
 
158
 
 
159
  /* path of the NODE */
 
160
  char *path;
 
161
 
 
162
  /* cached value of strlen(PATH). */
 
163
  apr_size_t path_len;
 
164
 
 
165
  /* the node allocated in the cache's pool. NULL for empty entries. */
 
166
  dag_node_t *node;
 
167
} cache_entry_t;
 
168
 
 
169
/* Number of entries in the cache.  Keep this low to keep pressure on the
 
170
   CPU caches low as well.  A binary value is most efficient.  If we walk
 
171
   a directory tree, we want enough entries to store nodes for all files
 
172
   without overwriting the nodes for the parent folder.  That way, there
 
173
   will be no unnecessary misses (except for a few random ones caused by
 
174
   hash collision).
 
175
 
 
176
   The actual number of instances may be higher but entries that got
 
177
   overwritten are no longer visible.
 
178
 */
 
179
enum { BUCKET_COUNT = 256 };
 
180
 
 
181
/* Each pool that has received a DAG node, will hold at least on lock on
 
182
   our cache to ensure that the node remains valid despite being allocated
 
183
   in the cache's pool.  This is the structure to represent the lock.
 
184
 */
 
185
typedef struct cache_lock_t
 
186
{
 
187
  /* pool holding the lock */
 
188
  apr_pool_t *pool;
 
189
 
 
190
  /* cache being locked */
 
191
  fs_fs_dag_cache_t *cache;
 
192
 
 
193
  /* next lock. NULL at EOL */
 
194
  struct cache_lock_t *next;
 
195
 
 
196
  /* previous lock. NULL at list head. Only then this==cache->first_lock */
 
197
  struct cache_lock_t *prev;
 
198
} cache_lock_t;
 
199
 
 
200
/* The actual cache structure.  All nodes will be allocated in POOL.
 
201
   When the number of INSERTIONS (i.e. objects created form that pool)
 
202
   exceeds a certain threshold, the pool will be cleared and the cache
 
203
   with it.
 
204
 
 
205
   To ensure that nodes returned from this structure remain valid, the
 
206
   cache will get locked for the lifetime of the _receiving_ pools (i.e.
 
207
   those in which we would allocate the node if there was no cache.).
 
208
   The cache will only be cleared FIRST_LOCK is 0.
 
209
 */
 
210
struct fs_fs_dag_cache_t
 
211
{
 
212
  /* fixed number of (possibly empty) cache entries */
 
213
  cache_entry_t buckets[BUCKET_COUNT];
 
214
 
 
215
  /* pool used for all node allocation */
 
216
  apr_pool_t *pool;
 
217
 
 
218
  /* number of entries created from POOL since the last cleanup */
 
219
  apr_size_t insertions;
 
220
 
 
221
  /* Property lookups etc. have a very high locality (75% re-hit).
 
222
     Thus, remember the last hit location for optimistic lookup. */
 
223
  apr_size_t last_hit;
 
224
 
 
225
  /* List of receiving pools that are still alive. */
 
226
  cache_lock_t *first_lock;
 
227
};
 
228
 
 
229
/* Cleanup function to be called when a receiving pool gets cleared.
 
230
   Unlocks the cache once.
 
231
 */
 
232
static apr_status_t
 
233
unlock_cache(void *baton_void)
 
234
{
 
235
  cache_lock_t *lock = baton_void;
 
236
 
 
237
  /* remove lock from chain. Update the head */
 
238
  if (lock->next)
 
239
    lock->next->prev = lock->prev;
 
240
  if (lock->prev)
 
241
    lock->prev->next = lock->next;
 
242
  else
 
243
    lock->cache->first_lock = lock->next;
 
244
 
 
245
  return APR_SUCCESS;
 
246
}
 
247
 
 
248
/* Cleanup function to be called when the cache itself gets destroyed.
 
249
   In that case, we must unregister all unlock requests.
 
250
 */
 
251
static apr_status_t
 
252
unregister_locks(void *baton_void)
 
253
{
 
254
  fs_fs_dag_cache_t *cache = baton_void;
 
255
  cache_lock_t *lock;
 
256
 
 
257
  for (lock = cache->first_lock; lock; lock = lock->next)
 
258
    apr_pool_cleanup_kill(lock->pool,
 
259
                          lock,
 
260
                          unlock_cache);
 
261
 
 
262
  return APR_SUCCESS;
 
263
}
 
264
 
 
265
fs_fs_dag_cache_t*
 
266
svn_fs_fs__create_dag_cache(apr_pool_t *pool)
 
267
{
 
268
  fs_fs_dag_cache_t *result = apr_pcalloc(pool, sizeof(*result));
 
269
  result->pool = svn_pool_create(pool);
 
270
 
 
271
  apr_pool_cleanup_register(pool,
 
272
                            result,
 
273
                            unregister_locks,
 
274
                            apr_pool_cleanup_null);
 
275
 
 
276
  return result;
 
277
}
 
278
 
 
279
/* Prevent the entries in CACHE from being destroyed, for as long as the
 
280
   POOL lives.
 
281
 */
 
282
static void
 
283
lock_cache(fs_fs_dag_cache_t* cache, apr_pool_t *pool)
 
284
{
 
285
  /* we only need to lock / unlock once per pool.  Since we will often ask
 
286
     for multiple nodes with the same pool, we can reduce the overhead.
 
287
     However, if e.g. pools are being used in an alternating pattern,
 
288
     we may lock the cache more than once for the same pool (and register
 
289
     just as many cleanup actions).
 
290
   */
 
291
  cache_lock_t *lock = cache->first_lock;
 
292
 
 
293
  /* try to find an existing lock for POOL.
 
294
     But limit the time spent on chasing pointers.  */
 
295
  int limiter = 8;
 
296
  while (lock && --limiter)
 
297
      if (lock->pool == pool)
 
298
        return;
 
299
 
 
300
  /* create a new lock and put it at the beginning of the lock chain */
 
301
  lock = apr_palloc(pool, sizeof(*lock));
 
302
  lock->cache = cache;
 
303
  lock->pool = pool;
 
304
  lock->next = cache->first_lock;
 
305
  lock->prev = NULL;
 
306
 
 
307
  if (cache->first_lock)
 
308
    cache->first_lock->prev = lock;
 
309
  cache->first_lock = lock;
 
310
 
 
311
  /* instruct POOL to remove the look upon cleanup */
 
312
  apr_pool_cleanup_register(pool,
 
313
                            lock,
 
314
                            unlock_cache,
 
315
                            apr_pool_cleanup_null);
 
316
}
 
317
 
 
318
/* Clears the CACHE at regular intervals (destroying all cached nodes)
 
319
 */
 
320
static void
 
321
auto_clear_dag_cache(fs_fs_dag_cache_t* cache)
 
322
{
 
323
  if (cache->first_lock == NULL && cache->insertions > BUCKET_COUNT)
 
324
    {
 
325
      svn_pool_clear(cache->pool);
 
326
 
 
327
      memset(cache->buckets, 0, sizeof(cache->buckets));
 
328
      cache->insertions = 0;
 
329
    }
 
330
}
 
331
 
 
332
/* For the given REVISION and PATH, return the respective entry in CACHE.
 
333
   If the entry is empty, its NODE member will be NULL and the caller
 
334
   may then set it to the corresponding DAG node allocated in CACHE->POOL.
 
335
 */
 
336
static cache_entry_t *
 
337
cache_lookup( fs_fs_dag_cache_t *cache
 
338
            , svn_revnum_t revision
 
339
            , const char *path)
 
340
{
 
341
  apr_size_t i, bucket_index;
 
342
  apr_size_t path_len = strlen(path);
 
343
  apr_uint32_t hash_value = (apr_uint32_t)revision;
 
344
 
 
345
#if SVN_UNALIGNED_ACCESS_IS_OK
 
346
  /* "randomizing" / distributing factor used in our hash function */
 
347
  const apr_uint32_t factor = 0xd1f3da69;
 
348
#endif
 
349
 
 
350
  /* optimistic lookup: hit the same bucket again? */
 
351
  cache_entry_t *result = &cache->buckets[cache->last_hit];
 
352
  if (   (result->revision == revision)
 
353
      && (result->path_len == path_len)
 
354
      && !memcmp(result->path, path, path_len))
 
355
    {
 
356
      return result;
 
357
    }
 
358
 
 
359
  /* need to do a full lookup.  Calculate the hash value
 
360
     (HASH_VALUE has been initialized to REVISION). */
 
361
  i = 0;
 
362
#if SVN_UNALIGNED_ACCESS_IS_OK
 
363
  /* We relax the dependency chain between iterations by processing
 
364
     two chunks from the input per hash_value self-multiplication.
 
365
     The HASH_VALUE update latency is now 1 MUL latency + 1 ADD latency
 
366
     per 2 chunks instead of 1 chunk.
 
367
   */
 
368
  for (; i + 8 <= path_len; i += 8)
 
369
    hash_value = hash_value * factor * factor
 
370
               + (  *(const apr_uint32_t*)(path + i) * factor
 
371
                  + *(const apr_uint32_t*)(path + i + 4));
 
372
#endif
 
373
 
 
374
  for (; i < path_len; ++i)
 
375
    /* Help GCC to minimize the HASH_VALUE update latency by splitting the
 
376
       MUL 33 of the naive implementation: h = h * 33 + path[i].  This
 
377
       shortens the dependency chain from 1 shift + 2 ADDs to 1 shift + 1 ADD.
 
378
     */
 
379
    hash_value = hash_value * 32 + (hash_value + (unsigned char)path[i]);
 
380
 
 
381
  bucket_index = hash_value + (hash_value >> 16);
 
382
  bucket_index = (bucket_index + (bucket_index >> 8)) % BUCKET_COUNT;
 
383
 
 
384
  /* access the corresponding bucket and remember its location */
 
385
  result = &cache->buckets[bucket_index];
 
386
  cache->last_hit = bucket_index;
 
387
 
 
388
  /* if it is *NOT* a match,  clear the bucket, expect the caller to fill
 
389
     in the node and count it as an insertion */
 
390
  if (   (result->hash_value != hash_value)
 
391
      || (result->revision != revision)
 
392
      || (result->path_len != path_len)
 
393
      || memcmp(result->path, path, path_len))
 
394
    {
 
395
      result->hash_value = hash_value;
 
396
      result->revision = revision;
 
397
      if (result->path_len < path_len)
 
398
        result->path = apr_palloc(cache->pool, path_len + 1);
 
399
      result->path_len = path_len;
 
400
      memcpy(result->path, path, path_len + 1);
 
401
 
 
402
      result->node = NULL;
 
403
 
 
404
      cache->insertions++;
 
405
    }
 
406
 
 
407
  return result;
 
408
}
 
409
 
 
410
/* 2nd level cache */
 
411
 
139
412
/* Find and return the DAG node cache for ROOT and the key that
140
413
   should be used for PATH. */
141
414
static void
155
428
    {
156
429
      fs_fs_data_t *ffd = root->fs->fsap_data;
157
430
      if (cache) *cache = ffd->rev_node_cache;
158
 
      if (key && path) *key = apr_psprintf(pool, "%ld%s",
159
 
                                           root->rev, path);
 
431
      if (key && path) *key
 
432
        = svn_fs_fs__combine_number_and_string(root->rev, path, pool);
160
433
    }
161
434
}
162
435
 
163
436
/* Return NODE for PATH from ROOT's node cache, or NULL if the node
164
 
   isn't cached; the node is copied into POOL. */
 
437
   isn't cached; read it from the FS. *NODE remains valid until either
 
438
   POOL or the FS gets cleared or destroyed (whichever comes first).
 
439
 
 
440
   Since locking can be expensive and POOL may be long-living, for
 
441
   nodes that will not need to survive the next call to this function,
 
442
   set NEEDS_LOCK_CACHE to FALSE. */
165
443
static svn_error_t *
166
444
dag_node_cache_get(dag_node_t **node_p,
167
445
                   svn_fs_root_t *root,
168
446
                   const char *path,
 
447
                   svn_boolean_t needs_lock_cache,
169
448
                   apr_pool_t *pool)
170
449
{
171
450
  svn_boolean_t found;
172
 
  dag_node_t *node;
 
451
  dag_node_t *node = NULL;
173
452
  svn_cache__t *cache;
174
453
  const char *key;
175
454
 
176
455
  SVN_ERR_ASSERT(*path == '/');
177
456
 
178
 
  locate_cache(&cache, &key, root, path, pool);
179
 
 
180
 
  SVN_ERR(svn_cache__get((void **) &node, &found, cache, key, pool));
181
 
  if (found && node)
 
457
  if (!root->is_txn_root)
182
458
    {
183
 
      /* Patch up the FS, since this might have come from an old FS
184
 
       * object. */
185
 
      svn_fs_fs__dag_set_fs(node, root->fs);
186
 
      *node_p = node;
 
459
      /* immutable DAG node. use the global caches for it */
 
460
 
 
461
      fs_fs_data_t *ffd = root->fs->fsap_data;
 
462
      cache_entry_t *bucket;
 
463
 
 
464
      auto_clear_dag_cache(ffd->dag_node_cache);
 
465
      bucket = cache_lookup(ffd->dag_node_cache, root->rev, path);
 
466
      if (bucket->node == NULL)
 
467
        {
 
468
          locate_cache(&cache, &key, root, path, pool);
 
469
          SVN_ERR(svn_cache__get((void **)&node, &found, cache, key,
 
470
                                 ffd->dag_node_cache->pool));
 
471
          if (found && node)
 
472
            {
 
473
              /* Patch up the FS, since this might have come from an old FS
 
474
              * object. */
 
475
              svn_fs_fs__dag_set_fs(node, root->fs);
 
476
              bucket->node = node;
 
477
            }
 
478
        }
 
479
      else
 
480
        {
 
481
          node = bucket->node;
 
482
        }
 
483
 
 
484
      /* if we found a node, make sure it remains valid at least as long
 
485
         as it would when allocated in POOL. */
 
486
      if (node && needs_lock_cache)
 
487
        lock_cache(ffd->dag_node_cache, pool);
187
488
    }
188
489
  else
189
 
    *node_p = NULL;
 
490
    {
 
491
      /* DAG is mutable / may become invalid. Use the TXN-local cache */
 
492
 
 
493
      locate_cache(&cache, &key, root, path, pool);
 
494
 
 
495
      SVN_ERR(svn_cache__get((void **) &node, &found, cache, key, pool));
 
496
      if (found && node)
 
497
        {
 
498
          /* Patch up the FS, since this might have come from an old FS
 
499
          * object. */
 
500
          svn_fs_fs__dag_set_fs(node, root->fs);
 
501
        }
 
502
    }
 
503
 
 
504
  *node_p = node;
 
505
 
190
506
  return SVN_NO_ERROR;
191
507
}
192
508
 
203
519
 
204
520
  SVN_ERR_ASSERT(*path == '/');
205
521
 
 
522
  /* Do *not* attempt to dup and put the node into L1.
 
523
   * dup() is twice as expensive as an L2 lookup (which will set also L1).
 
524
   */
206
525
  locate_cache(&cache, &key, root, path, pool);
207
526
 
208
527
  return svn_cache__set(cache, key, node, pool);
229
548
  struct fdic_baton *b = baton;
230
549
  const char *item_path = key;
231
550
 
232
 
  if (svn_dirent_is_ancestor(b->path, item_path))
 
551
  if (svn_fspath__skip_ancestor(b->path, item_path))
233
552
    APR_ARRAY_PUSH(b->list, const char *) = apr_pstrdup(b->pool, item_path);
234
553
 
235
554
  return SVN_NO_ERROR;
288
607
  SVN_ERR(svn_fs_fs__txn_proplist(&txnprops, txn, pool));
289
608
  if (txnprops)
290
609
    {
291
 
      if (apr_hash_get(txnprops, SVN_FS__PROP_TXN_CHECK_OOD,
292
 
                       APR_HASH_KEY_STRING))
 
610
      if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD))
293
611
        flags |= SVN_FS_TXN_CHECK_OOD;
294
612
 
295
 
      if (apr_hash_get(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS,
296
 
                       APR_HASH_KEY_STRING))
 
613
      if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS))
297
614
        flags |= SVN_FS_TXN_CHECK_LOCKS;
298
615
    }
299
616
 
359
676
    return svn_fs_fs__dag_clone_root(node_p, root->fs, root->txn, pool);
360
677
  else
361
678
    /* If it's not a transaction root, we can't change its contents.  */
362
 
    return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path, pool);
 
679
    return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path);
363
680
}
364
681
 
365
682
 
503
820
     or if it is a branch point that we are accessing via its original
504
821
     copy destination path. */
505
822
  SVN_ERR(svn_fs_fs__dag_get_copyroot(&copyroot_rev, &copyroot_path,
506
 
                                      child->node,pool));
 
823
                                      child->node));
507
824
  SVN_ERR(svn_fs_fs__revision_root(&copyroot_root, fs, copyroot_rev, pool));
508
 
  SVN_ERR(get_dag(&copyroot_node, copyroot_root, copyroot_path, pool));
 
825
  SVN_ERR(get_dag(&copyroot_node, copyroot_root, copyroot_path, FALSE, pool));
509
826
  copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
510
827
 
511
828
  if (svn_fs_fs__id_compare(copyroot_id, child_id) == -1)
553
870
     directories must exist, as usual.)  If the last component doesn't
554
871
     exist, simply leave the `node' member of the bottom parent_path
555
872
     component zero.  */
556
 
  open_path_last_optional = 1
557
 
 
 
873
  open_path_last_optional = 1,
 
874
 
 
875
  /* When this flag is set, don't bother to lookup the DAG node in
 
876
     our caches because we already tried this.  Ignoring this flag
 
877
     has no functional impact.  */
 
878
  open_path_uncached = 2,
 
879
 
 
880
  /* The caller does not care about the parent node chain but only
 
881
     the final DAG node. */
 
882
  open_path_node_only = 4
558
883
} open_path_flags_t;
559
884
 
560
885
 
561
886
/* Open the node identified by PATH in ROOT, allocating in POOL.  Set
562
887
   *PARENT_PATH_P to a path from the node up to ROOT.  The resulting
563
888
   **PARENT_PATH_P value is guaranteed to contain at least one
564
 
   *element, for the root directory.
 
889
   *element, for the root directory.  PATH must be in canonical form.
565
890
 
566
891
   If resulting *PARENT_PATH_P will eventually be made mutable and
567
892
   modified, or if copy ID inheritance information is otherwise
568
893
   needed, TXN_ID should be the ID of the mutability transaction.  If
569
 
   TXN_ID is NULL, no copy ID in heritance information will be
 
894
   TXN_ID is NULL, no copy ID inheritance information will be
570
895
   calculated for the *PARENT_PATH_P chain.
571
896
 
572
897
   If FLAGS & open_path_last_optional is zero, return the error
577
902
   callers that create new nodes --- we find the parent directory for
578
903
   them, and tell them whether the entry exists already.
579
904
 
 
905
   The remaining bits in FLAGS are hints that allow this function
 
906
   to take shortcuts based on knowledge that the caller provides,
 
907
   such as the caller is not actually being interested in PARENT_PATH_P,
 
908
   but only in (*PARENT_PATH_P)->NODE.
 
909
 
580
910
   NOTE: Public interfaces which only *read* from the filesystem
581
911
   should not call this function directly, but should instead use
582
912
   get_dag().
590
920
          apr_pool_t *pool)
591
921
{
592
922
  svn_fs_t *fs = root->fs;
593
 
  dag_node_t *here; /* The directory we're currently looking at.  */
594
 
  parent_path_t *parent_path; /* The path from HERE up to the root.  */
 
923
  dag_node_t *here = NULL; /* The directory we're currently looking at.  */
 
924
  parent_path_t *parent_path; /* The path from HERE up to the root. */
595
925
  const char *rest; /* The portion of PATH we haven't traversed yet.  */
596
 
  const char *canon_path = svn_fs__canonicalize_abspath(path, pool);
 
926
 
 
927
  /* ensure a canonical path representation */
597
928
  const char *path_so_far = "/";
598
 
 
599
 
  /* Make a parent_path item for the root node, using its own current
600
 
     copy id.  */
601
 
  SVN_ERR(root_node(&here, root, pool));
 
929
  apr_pool_t *iterpool = svn_pool_create(pool);
 
930
 
 
931
  /* callers often traverse the tree in some path-based order.  That means
 
932
     a sibling of PATH has been presently accessed.  Try to start the lookup
 
933
     directly at the parent node, if the caller did not requested the full
 
934
     parent chain. */
 
935
  const char *directory;
 
936
  assert(svn_fs__is_canonical_abspath(path));
 
937
  if (flags & open_path_node_only)
 
938
    {
 
939
      directory = svn_dirent_dirname(path, pool);
 
940
      if (directory[1] != 0) /* root nodes are covered anyway */
 
941
        SVN_ERR(dag_node_cache_get(&here, root, directory, TRUE, pool));
 
942
    }
 
943
 
 
944
  /* did the shortcut work? */
 
945
  if (here)
 
946
    {
 
947
      path_so_far = directory;
 
948
      rest = path + strlen(directory) + 1;
 
949
    }
 
950
  else
 
951
    {
 
952
      /* Make a parent_path item for the root node, using its own current
 
953
         copy id.  */
 
954
      SVN_ERR(root_node(&here, root, pool));
 
955
      rest = path + 1; /* skip the leading '/', it saves in iteration */
 
956
    }
 
957
 
602
958
  parent_path = make_parent_path(here, 0, 0, pool);
603
959
  parent_path->copy_inherit = copy_id_inherit_self;
604
960
 
605
 
  rest = canon_path + 1; /* skip the leading '/', it saves in iteration */
606
 
 
607
961
  /* Whenever we are at the top of this loop:
608
962
     - HERE is our current directory,
609
963
     - ID is the node revision ID of HERE,
615
969
      char *entry;
616
970
      dag_node_t *child;
617
971
 
 
972
      svn_pool_clear(iterpool);
 
973
 
618
974
      /* Parse out the next entry from the path.  */
619
975
      entry = svn_fs__next_entry_name(&next, rest, pool);
620
976
 
634
990
          copy_id_inherit_t inherit;
635
991
          const char *copy_path = NULL;
636
992
          svn_error_t *err = SVN_NO_ERROR;
637
 
          dag_node_t *cached_node;
 
993
          dag_node_t *cached_node = NULL;
638
994
 
639
995
          /* If we found a directory entry, follow it.  First, we
640
996
             check our node cache, and, failing that, we hit the DAG
641
 
             layer. */
642
 
          SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far, pool));
 
997
             layer.  Don't bother to contact the cache for the last
 
998
             element if we already know the lookup to fail for the
 
999
             complete path. */
 
1000
          if (next || !(flags & open_path_uncached))
 
1001
            SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far,
 
1002
                                       TRUE, pool));
643
1003
          if (cached_node)
644
1004
            child = cached_node;
645
1005
          else
646
 
            err = svn_fs_fs__dag_open(&child, here, entry, pool);
 
1006
            err = svn_fs_fs__dag_open(&child, here, entry, pool, iterpool);
647
1007
 
648
1008
          /* "file not found" requires special handling.  */
649
1009
          if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
672
1032
          /* Other errors we return normally.  */
673
1033
          SVN_ERR(err);
674
1034
 
675
 
          /* Now, make a parent_path item for CHILD. */
676
 
          parent_path = make_parent_path(child, entry, parent_path, pool);
677
 
          if (txn_id)
678
 
            {
679
 
              SVN_ERR(get_copy_inheritance(&inherit, &copy_path,
680
 
                                           fs, parent_path, txn_id, pool));
681
 
              parent_path->copy_inherit = inherit;
682
 
              parent_path->copy_src_path = apr_pstrdup(pool, copy_path);
 
1035
          if (flags & open_path_node_only)
 
1036
            {
 
1037
              /* Shortcut: the caller only wan'ts the final DAG node. */
 
1038
              parent_path->node = child;
 
1039
            }
 
1040
          else
 
1041
            {
 
1042
              /* Now, make a parent_path item for CHILD. */
 
1043
              parent_path = make_parent_path(child, entry, parent_path, pool);
 
1044
              if (txn_id)
 
1045
                {
 
1046
                  SVN_ERR(get_copy_inheritance(&inherit, &copy_path, fs,
 
1047
                                               parent_path, txn_id, iterpool));
 
1048
                  parent_path->copy_inherit = inherit;
 
1049
                  parent_path->copy_src_path = apr_pstrdup(pool, copy_path);
 
1050
                }
683
1051
            }
684
1052
 
685
1053
          /* Cache the node we found (if it wasn't already cached). */
686
1054
          if (! cached_node)
687
 
            SVN_ERR(dag_node_cache_set(root, path_so_far, child, pool));
 
1055
            SVN_ERR(dag_node_cache_set(root, path_so_far, child, iterpool));
688
1056
        }
689
1057
 
690
1058
      /* Are we finished traversing the path?  */
693
1061
 
694
1062
      /* The path isn't finished yet; we'd better be in a directory.  */
695
1063
      if (svn_fs_fs__dag_node_kind(child) != svn_node_dir)
696
 
        SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far, pool),
697
 
                  apr_psprintf(pool, _("Failure opening '%s'"), path));
 
1064
        SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far),
 
1065
                  apr_psprintf(iterpool, _("Failure opening '%s'"), path));
698
1066
 
699
1067
      rest = next;
700
1068
      here = child;
701
1069
    }
702
1070
 
 
1071
  svn_pool_destroy(iterpool);
703
1072
  *parent_path_p = parent_path;
704
1073
  return SVN_NO_ERROR;
705
1074
}
764
1133
 
765
1134
      /* Determine what copyroot our new child node should use. */
766
1135
      SVN_ERR(svn_fs_fs__dag_get_copyroot(&copyroot_rev, &copyroot_path,
767
 
                                          parent_path->node, pool));
 
1136
                                          parent_path->node));
768
1137
      SVN_ERR(svn_fs_fs__revision_root(&copyroot_root, root->fs,
769
1138
                                       copyroot_rev, pool));
770
 
      SVN_ERR(get_dag(&copyroot_node, copyroot_root, copyroot_path, pool));
 
1139
      SVN_ERR(get_dag(&copyroot_node, copyroot_root, copyroot_path,
 
1140
                      FALSE, pool));
771
1141
 
772
1142
      child_id = svn_fs_fs__dag_get_id(parent_path->node);
773
1143
      copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
804
1174
 
805
1175
/* Open the node identified by PATH in ROOT.  Set DAG_NODE_P to the
806
1176
   node we find, allocated in POOL.  Return the error
807
 
   SVN_ERR_FS_NOT_FOUND if this node doesn't exist. */
 
1177
   SVN_ERR_FS_NOT_FOUND if this node doesn't exist.
 
1178
 
 
1179
   Since locking can be expensive and POOL may be long-living, for
 
1180
   nodes that will not need to survive the next call to this function,
 
1181
   set NEEDS_LOCK_CACHE to FALSE. */
808
1182
static svn_error_t *
809
1183
get_dag(dag_node_t **dag_node_p,
810
1184
        svn_fs_root_t *root,
811
1185
        const char *path,
 
1186
        svn_boolean_t needs_lock_cache,
812
1187
        apr_pool_t *pool)
813
1188
{
814
1189
  parent_path_t *parent_path;
815
 
  dag_node_t *node;
816
 
 
817
 
  /* Canonicalize the input PATH. */
818
 
  path = svn_fs__canonicalize_abspath(path, pool);
819
 
 
820
 
  /* First we look for the DAG in our cache. */
821
 
  SVN_ERR(dag_node_cache_get(&node, root, path, pool));
 
1190
  dag_node_t *node = NULL;
 
1191
 
 
1192
  /* First we look for the DAG in our cache
 
1193
     (if the path may be canonical). */
 
1194
  if (*path == '/')
 
1195
    SVN_ERR(dag_node_cache_get(&node, root, path, needs_lock_cache, pool));
 
1196
 
822
1197
  if (! node)
823
1198
    {
824
 
      /* Call open_path with no flags, as we want this to return an error
825
 
         if the node for which we are searching doesn't exist. */
826
 
      SVN_ERR(open_path(&parent_path, root, path, 0, NULL, pool));
827
 
      node = parent_path->node;
828
 
 
829
 
      /* No need to cache our find -- open_path() will do that for us. */
 
1199
      /* Canonicalize the input PATH. */
 
1200
      if (! svn_fs__is_canonical_abspath(path))
 
1201
        {
 
1202
          path = svn_fs__canonicalize_abspath(path, pool);
 
1203
 
 
1204
          /* Try again with the corrected path. */
 
1205
          SVN_ERR(dag_node_cache_get(&node, root, path, needs_lock_cache,
 
1206
                                     pool));
 
1207
        }
 
1208
 
 
1209
      if (! node)
 
1210
        {
 
1211
          /* Call open_path with no flags, as we want this to return an
 
1212
           * error if the node for which we are searching doesn't exist. */
 
1213
          SVN_ERR(open_path(&parent_path, root, path,
 
1214
                            open_path_uncached | open_path_node_only,
 
1215
                            NULL, pool));
 
1216
          node = parent_path->node;
 
1217
 
 
1218
          /* No need to cache our find -- open_path() will do that for us. */
 
1219
        }
830
1220
    }
831
1221
 
832
1222
  *dag_node_p = node;
891
1281
    {
892
1282
      dag_node_t *node;
893
1283
 
894
 
      SVN_ERR(get_dag(&node, root, path, pool));
 
1284
      SVN_ERR(get_dag(&node, root, path, FALSE, pool));
895
1285
      *id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(node), pool);
896
1286
    }
897
1287
  return SVN_NO_ERROR;
906
1296
{
907
1297
  dag_node_t *node;
908
1298
 
909
 
  SVN_ERR(get_dag(&node, root, path, pool));
 
1299
  SVN_ERR(get_dag(&node, root, path, FALSE, pool));
910
1300
  return svn_fs_fs__dag_get_revision(revision, node, pool);
911
1301
}
912
1302
 
921
1311
{
922
1312
  dag_node_t *node;
923
1313
 
924
 
  SVN_ERR(get_dag(&node, root, path, pool));
 
1314
  SVN_ERR(get_dag(&node, root, path, TRUE, pool));
925
1315
  *created_path = svn_fs_fs__dag_get_created_path(node);
926
1316
 
927
1317
  return SVN_NO_ERROR;
985
1375
  dag_node_t *node;
986
1376
  apr_hash_t *proplist;
987
1377
 
988
 
  SVN_ERR(get_dag(&node, root, path, pool));
 
1378
  SVN_ERR(get_dag(&node, root, path, FALSE, pool));
989
1379
  SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, node, pool));
990
1380
  *value_p = NULL;
991
1381
  if (proplist)
992
 
    *value_p = apr_hash_get(proplist, propname, APR_HASH_KEY_STRING);
 
1382
    *value_p = svn_hash_gets(proplist, propname);
993
1383
 
994
1384
  return SVN_NO_ERROR;
995
1385
}
1008
1398
  apr_hash_t *table;
1009
1399
  dag_node_t *node;
1010
1400
 
1011
 
  SVN_ERR(get_dag(&node, root, path, pool));
 
1401
  SVN_ERR(get_dag(&node, root, path, FALSE, pool));
1012
1402
  SVN_ERR(svn_fs_fs__dag_get_proplist(&table, node, pool));
1013
1403
  *table_p = table ? table : apr_hash_make(pool);
1014
1404
 
1049
1439
    return SVN_FS__NOT_TXN(root);
1050
1440
  txn_id = root->txn;
1051
1441
 
 
1442
  path = svn_fs__canonicalize_abspath(path, pool);
1052
1443
  SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, pool));
1053
1444
 
1054
1445
  /* Check (non-recursively) to see if path is locked; if so, check
1073
1464
    {
1074
1465
      apr_int64_t increment = 0;
1075
1466
      svn_boolean_t had_mergeinfo;
1076
 
      SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&had_mergeinfo, parent_path->node,
1077
 
                                           pool));
 
1467
      SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&had_mergeinfo, parent_path->node));
1078
1468
 
1079
1469
      if (value && !had_mergeinfo)
1080
1470
        increment = 1;
1090
1480
    }
1091
1481
 
1092
1482
  /* Set the property. */
1093
 
  apr_hash_set(proplist, name, APR_HASH_KEY_STRING, value);
 
1483
  svn_hash_sets(proplist, name, value);
1094
1484
 
1095
1485
  /* Overwrite the node's proplist. */
1096
1486
  SVN_ERR(svn_fs_fs__dag_set_proplist(parent_path->node, proplist,
1125
1515
      (SVN_ERR_FS_GENERAL, NULL,
1126
1516
       _("Cannot compare property value between two different filesystems"));
1127
1517
 
1128
 
  SVN_ERR(get_dag(&node1, root1, path1, pool));
1129
 
  SVN_ERR(get_dag(&node2, root2, path2, pool));
 
1518
  SVN_ERR(get_dag(&node1, root1, path1, TRUE, pool));
 
1519
  SVN_ERR(get_dag(&node2, root2, path2, TRUE, pool));
1130
1520
  return svn_fs_fs__dag_things_different(changed_p, NULL,
1131
 
                                         node1, node2, pool);
 
1521
                                         node1, node2);
1132
1522
}
1133
1523
 
1134
1524
 
1139
1529
static svn_error_t *
1140
1530
get_root(dag_node_t **node, svn_fs_root_t *root, apr_pool_t *pool)
1141
1531
{
1142
 
  return get_dag(node, root, "", pool);
 
1532
  return get_dag(node, root, "/", TRUE, pool);
1143
1533
}
1144
1534
 
1145
1535
 
1195
1585
  svn_fs_t *fs;
1196
1586
  apr_pool_t *iterpool;
1197
1587
  apr_int64_t mergeinfo_increment = 0;
 
1588
  svn_boolean_t fs_supports_mergeinfo;
1198
1589
 
1199
1590
  /* Make sure everyone comes from the same filesystem. */
1200
1591
  fs = svn_fs_fs__dag_get_fs(ancestor);
1338
1729
  /* ### todo: it would be more efficient to simply check for a NULL
1339
1730
     entries hash where necessary below than to allocate an empty hash
1340
1731
     here, but another day, another day... */
1341
 
  SVN_ERR(svn_fs_fs__dag_dir_entries(&s_entries, source, pool, pool));
1342
 
  SVN_ERR(svn_fs_fs__dag_dir_entries(&t_entries, target, pool, pool));
1343
 
  SVN_ERR(svn_fs_fs__dag_dir_entries(&a_entries, ancestor, pool, pool));
 
1732
  SVN_ERR(svn_fs_fs__dag_dir_entries(&s_entries, source, pool));
 
1733
  SVN_ERR(svn_fs_fs__dag_dir_entries(&t_entries, target, pool));
 
1734
  SVN_ERR(svn_fs_fs__dag_dir_entries(&a_entries, ancestor, pool));
 
1735
 
 
1736
  fs_supports_mergeinfo = svn_fs_fs__fs_supports_mergeinfo(fs);
1344
1737
 
1345
1738
  /* for each entry E in a_entries... */
1346
1739
  iterpool = svn_pool_create(pool);
1373
1766
          dag_node_t *t_ent_node;
1374
1767
          SVN_ERR(svn_fs_fs__dag_get_node(&t_ent_node, fs,
1375
1768
                                          t_entry->id, iterpool));
1376
 
          if (svn_fs_fs__fs_supports_mergeinfo(fs))
 
1769
          if (fs_supports_mergeinfo)
1377
1770
            {
1378
1771
              apr_int64_t mergeinfo_start;
1379
1772
              SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start,
1380
 
                                                         t_ent_node,
1381
 
                                                         iterpool));
 
1773
                                                         t_ent_node));
1382
1774
              mergeinfo_increment -= mergeinfo_start;
1383
1775
            }
1384
1776
 
1388
1780
              SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
1389
1781
                                              s_entry->id, iterpool));
1390
1782
 
1391
 
              if (svn_fs_fs__fs_supports_mergeinfo(fs))
 
1783
              if (fs_supports_mergeinfo)
1392
1784
                {
1393
1785
                  apr_int64_t mergeinfo_end;
1394
1786
                  SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end,
1395
 
                                                             s_ent_node,
1396
 
                                                             iterpool));
 
1787
                                                             s_ent_node));
1397
1788
                  mergeinfo_increment += mergeinfo_end;
1398
1789
                }
1399
1790
 
1466
1857
                        txn_id,
1467
1858
                        &sub_mergeinfo_increment,
1468
1859
                        iterpool));
1469
 
          if (svn_fs_fs__fs_supports_mergeinfo(fs))
 
1860
          if (fs_supports_mergeinfo)
1470
1861
            mergeinfo_increment += sub_mergeinfo_increment;
1471
1862
        }
1472
1863
 
1502
1893
 
1503
1894
      SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
1504
1895
                                      s_entry->id, iterpool));
1505
 
      if (svn_fs_fs__fs_supports_mergeinfo(fs))
 
1896
      if (fs_supports_mergeinfo)
1506
1897
        {
1507
1898
          apr_int64_t mergeinfo_s;
1508
1899
          SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_s,
1509
 
                                                     s_ent_node,
1510
 
                                                     iterpool));
 
1900
                                                     s_ent_node));
1511
1901
          mergeinfo_increment += mergeinfo_s;
1512
1902
        }
1513
1903
 
1519
1909
 
1520
1910
  SVN_ERR(svn_fs_fs__dag_update_ancestry(target, source, pool));
1521
1911
 
1522
 
  if (svn_fs_fs__fs_supports_mergeinfo(fs))
 
1912
  if (fs_supports_mergeinfo)
1523
1913
    SVN_ERR(svn_fs_fs__dag_increment_mergeinfo_count(target,
1524
1914
                                                     mergeinfo_increment,
1525
1915
                                                     pool));
1622
2012
   */
1623
2013
 
1624
2014
  svn_error_t *err = SVN_NO_ERROR;
1625
 
  svn_stringbuf_t *conflict = svn_stringbuf_create("", pool);
 
2015
  svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool);
1626
2016
  svn_fs_t *fs = txn->fs;
1627
2017
 
1628
2018
  /* Limit memory usage when the repository has a high commit rate and
1730
2120
  dag_node_t *source, *ancestor;
1731
2121
  svn_fs_txn_t *txn;
1732
2122
  svn_error_t *err;
1733
 
  svn_stringbuf_t *conflict = svn_stringbuf_create("", pool);
 
2123
  svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool);
1734
2124
 
1735
2125
  if (! target_root->is_txn_root)
1736
2126
    return SVN_FS__NOT_TXN(target_root);
1803
2193
  dag_node_t *node;
1804
2194
 
1805
2195
  /* Get the entries for this path in the caller's pool. */
1806
 
  SVN_ERR(get_dag(&node, root, path, pool));
1807
 
  return svn_fs_fs__dag_dir_entries(table_p, node, pool, pool);
1808
 
}
1809
 
 
1810
 
/* Return a copy of PATH, allocated from POOL, for which control
1811
 
   characters have been escaped using the form \NNN (where NNN is the
1812
 
   octal representation of the byte's ordinal value).  */
1813
 
static const char *
1814
 
illegal_path_escape(const char *path, apr_pool_t *pool)
1815
 
{
1816
 
  svn_stringbuf_t *retstr;
1817
 
  apr_size_t i, copied = 0;
1818
 
  int c;
1819
 
 
1820
 
  /* At least one control character:
1821
 
      strlen - 1 (control) + \ + N + N + N + null . */
1822
 
  retstr = svn_stringbuf_create_ensure(strlen(path) + 4, pool);
1823
 
  for (i = 0; path[i]; i++)
1824
 
    {
1825
 
      c = (unsigned char)path[i];
1826
 
      if (! svn_ctype_iscntrl(c))
1827
 
        continue;
1828
 
 
1829
 
      /* If we got here, we're looking at a character that isn't
1830
 
         supported by the (or at least, our) URI encoding scheme.  We
1831
 
         need to escape this character.  */
1832
 
 
1833
 
      /* First things first, copy all the good stuff that we haven't
1834
 
         yet copied into our output buffer. */
1835
 
      if (i - copied)
1836
 
        svn_stringbuf_appendbytes(retstr, path + copied,
1837
 
                                  i - copied);
1838
 
 
1839
 
      /* Make sure buffer is big enough for '\' 'N' 'N' 'N' (and NUL) */
1840
 
      svn_stringbuf_ensure(retstr, retstr->len + 5);
1841
 
      /*### The backslash separator doesn't work too great with Windows,
1842
 
         but it's what we'll use for consistency with invalid utf8
1843
 
         formatting (until someone has a better idea) */
1844
 
      apr_snprintf(retstr->data + retstr->len, 5, "\\%03o", (unsigned char)c);
1845
 
      retstr->len += 4;
1846
 
 
1847
 
      /* Finally, update our copy counter. */
1848
 
      copied = i + 1;
1849
 
    }
1850
 
 
1851
 
  /* If we didn't encode anything, we don't need to duplicate the string. */
1852
 
  if (retstr->len == 0)
1853
 
    return path;
1854
 
 
1855
 
  /* Anything left to copy? */
1856
 
  if (i - copied)
1857
 
    svn_stringbuf_appendbytes(retstr, path + copied, i - copied);
1858
 
 
1859
 
  /* retstr is null-terminated either by apr_snprintf or the svn_stringbuf
1860
 
     functions. */
1861
 
 
1862
 
  return retstr->data;
 
2196
  SVN_ERR(get_dag(&node, root, path, FALSE, pool));
 
2197
  return svn_fs_fs__dag_dir_entries(table_p, node, pool);
1863
2198
}
1864
2199
 
1865
2200
/* Raise an error if PATH contains a newline because FSFS cannot handle
1867
2202
static svn_error_t *
1868
2203
check_newline(const char *path, apr_pool_t *pool)
1869
2204
{
1870
 
  const char *c;
 
2205
  char *c = strchr(path, '\n');
1871
2206
 
1872
 
  for (c = path; *c; c++)
1873
 
    {
1874
 
      if (*c == '\n')
1875
 
        return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
1876
 
           _("Invalid control character '0x%02x' in path '%s'"),
1877
 
           (unsigned char)*c, illegal_path_escape(path, pool));
1878
 
    }
 
2207
  if (c)
 
2208
    return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
 
2209
       _("Invalid control character '0x%02x' in path '%s'"),
 
2210
       (unsigned char)*c, svn_path_illegal_path_escape(path, pool));
1879
2211
 
1880
2212
  return SVN_NO_ERROR;
1881
2213
}
1895
2227
 
1896
2228
  SVN_ERR(check_newline(path, pool));
1897
2229
 
 
2230
  path = svn_fs__canonicalize_abspath(path, pool);
1898
2231
  SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
1899
2232
                    txn_id, pool));
1900
2233
 
1908
2241
  /* If there's already a sub-directory by that name, complain.  This
1909
2242
     also catches the case of trying to make a subdirectory named `/'.  */
1910
2243
  if (parent_path->node)
1911
 
    return SVN_FS__ALREADY_EXISTS(root, path, pool);
 
2244
    return SVN_FS__ALREADY_EXISTS(root, path);
1912
2245
 
1913
2246
  /* Create the subdirectory.  */
1914
2247
  SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
1940
2273
{
1941
2274
  parent_path_t *parent_path;
1942
2275
  const char *txn_id = root->txn;
1943
 
  apr_int64_t mergeinfo_count;
 
2276
  apr_int64_t mergeinfo_count = 0;
1944
2277
  svn_node_kind_t kind;
1945
2278
 
1946
2279
  if (! root->is_txn_root)
1947
2280
    return SVN_FS__NOT_TXN(root);
1948
2281
 
 
2282
  path = svn_fs__canonicalize_abspath(path, pool);
1949
2283
  SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, pool));
1950
2284
  kind = svn_fs_fs__dag_node_kind(parent_path->node);
1951
2285
 
1964
2298
  SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
1965
2299
  if (svn_fs_fs__fs_supports_mergeinfo(root->fs))
1966
2300
    SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_count,
1967
 
                                               parent_path->node,
1968
 
                                               pool));
 
2301
                                               parent_path->node));
1969
2302
  SVN_ERR(svn_fs_fs__dag_delete(parent_path->parent->node,
1970
2303
                                parent_path->entry,
1971
2304
                                txn_id, pool));
1975
2308
                                    pool));
1976
2309
 
1977
2310
  /* Update mergeinfo counts for parents */
1978
 
  if (svn_fs_fs__fs_supports_mergeinfo(root->fs) && mergeinfo_count > 0)
 
2311
  if (mergeinfo_count > 0)
1979
2312
    SVN_ERR(increment_mergeinfo_up_tree(parent_path->parent,
1980
2313
                                        -mergeinfo_count,
1981
2314
                                        pool));
1997
2330
          svn_fs_t *fs2,
1998
2331
          apr_pool_t *pool)
1999
2332
{
2000
 
  const char *uuid1;
2001
 
  const char *uuid2;
2002
 
 
2003
 
  /* Random thought: if fetching UUIDs to compare filesystems is too
2004
 
     expensive, one solution would be to cache the UUID in each fs
2005
 
     object (copying the UUID into fs->pool, of course). */
2006
 
 
2007
 
  SVN_ERR(fs1->vtable->get_uuid(fs1, &uuid1, pool));
2008
 
  SVN_ERR(fs2->vtable->get_uuid(fs2, &uuid2, pool));
2009
 
 
2010
 
  *same_p = ! strcmp(uuid1, uuid2);
 
2333
  *same_p = ! strcmp(fs1->uuid, fs2->uuid);
2011
2334
  return SVN_NO_ERROR;
2012
2335
}
2013
2336
 
2042
2365
       _("Copy from mutable tree not currently supported"));
2043
2366
 
2044
2367
  /* Get the NODE for FROM_PATH in FROM_ROOT.*/
2045
 
  SVN_ERR(get_dag(&from_node, from_root, from_path, pool));
 
2368
  SVN_ERR(get_dag(&from_node, from_root, from_path, TRUE, pool));
2046
2369
 
2047
2370
  /* Build up the parent path from TO_PATH in TO_ROOT.  If the last
2048
2371
     component does not exist, it's not that big a deal.  We'll just
2080
2403
          kind = svn_fs_path_change_replace;
2081
2404
          if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs))
2082
2405
            SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start,
2083
 
                                                       to_parent_path->node,
2084
 
                                                       pool));
 
2406
                                                       to_parent_path->node));
2085
2407
        }
2086
2408
      else
2087
2409
        {
2091
2413
 
2092
2414
      if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs))
2093
2415
        SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end,
2094
 
                                                   from_node, pool));
 
2416
                                                   from_node));
2095
2417
 
2096
2418
      /* Make sure the target node's parents are mutable.  */
2097
2419
      SVN_ERR(make_path_mutable(to_root, to_parent_path->parent,
2120
2442
                                            pool));
2121
2443
 
2122
2444
      /* Make a record of this modification in the changes table. */
2123
 
      SVN_ERR(get_dag(&new_node, to_root, to_path, pool));
 
2445
      SVN_ERR(get_dag(&new_node, to_root, to_path, TRUE, pool));
2124
2446
      SVN_ERR(add_change(to_root->fs, txn_id, to_path,
2125
2447
                         svn_fs_fs__dag_get_id(new_node), kind, FALSE, FALSE,
2126
2448
                         svn_fs_fs__dag_node_kind(from_node),
2159
2481
{
2160
2482
  SVN_ERR(check_newline(to_path, pool));
2161
2483
 
2162
 
  return svn_error_trace(copy_helper(from_root, from_path, to_root, to_path,
 
2484
  return svn_error_trace(copy_helper(from_root,
 
2485
                                     svn_fs__canonicalize_abspath(from_path,
 
2486
                                                                  pool),
 
2487
                                     to_root,
 
2488
                                     svn_fs__canonicalize_abspath(to_path,
 
2489
                                                                  pool),
2163
2490
                                     TRUE, pool));
2164
2491
}
2165
2492
 
2176
2503
  if (! to_root->is_txn_root)
2177
2504
    return SVN_FS__NOT_TXN(to_root);
2178
2505
 
 
2506
  path = svn_fs__canonicalize_abspath(path, pool);
2179
2507
  return svn_error_trace(copy_helper(from_root, path, to_root, path,
2180
2508
                                     FALSE, pool));
2181
2509
}
2194
2522
  dag_node_t *node;
2195
2523
  const char *copyfrom_path, *copyfrom_str = NULL;
2196
2524
  svn_revnum_t copyfrom_rev;
2197
 
  char *str, *last_str, *buf;
 
2525
  char *str, *buf;
2198
2526
 
2199
2527
  /* Check to see if there is a cached version of this copyfrom
2200
2528
     entry. */
2201
2529
  if (! root->is_txn_root) {
2202
2530
    fs_rev_root_data_t *frd = root->fsap_data;
2203
 
    copyfrom_str = apr_hash_get(frd->copyfrom_cache, path, APR_HASH_KEY_STRING);
 
2531
    copyfrom_str = svn_hash_gets(frd->copyfrom_cache, path);
2204
2532
  }
2205
2533
 
2206
2534
  if (copyfrom_str)
2216
2544
        {
2217
2545
          /* Parse the copyfrom string for our cached entry. */
2218
2546
          buf = apr_pstrdup(pool, copyfrom_str);
2219
 
          str = apr_strtok(buf, " ", &last_str);
 
2547
          str = svn_cstring_tokenize(" ", &buf);
2220
2548
          copyfrom_rev = SVN_STR_TO_REV(str);
2221
 
          copyfrom_path = last_str;
 
2549
          copyfrom_path = buf;
2222
2550
        }
2223
2551
    }
2224
2552
  else
2225
2553
    {
2226
2554
      /* There is no cached entry, look it up the old-fashioned
2227
2555
         way. */
2228
 
      SVN_ERR(get_dag(&node, root, path, pool));
2229
 
      SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(&copyfrom_rev, node, pool));
2230
 
      SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(&copyfrom_path, node, pool));
 
2556
      SVN_ERR(get_dag(&node, root, path, TRUE, pool));
 
2557
      SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(&copyfrom_rev, node));
 
2558
      SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(&copyfrom_path, node));
2231
2559
    }
2232
2560
 
2233
2561
  *rev_p  = copyfrom_rev;
2253
2581
 
2254
2582
  SVN_ERR(check_newline(path, pool));
2255
2583
 
 
2584
  path = svn_fs__canonicalize_abspath(path, pool);
2256
2585
  SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
2257
 
                    txn_id, pool));
 
2586
                   txn_id, pool));
2258
2587
 
2259
2588
  /* If there's already a file by that name, complain.
2260
2589
     This also catches the case of trying to make a file named `/'.  */
2261
2590
  if (parent_path->node)
2262
 
    return SVN_FS__ALREADY_EXISTS(root, path, pool);
 
2591
    return SVN_FS__ALREADY_EXISTS(root, path);
2263
2592
 
2264
2593
  /* Check (non-recursively) to see if path is locked;  if so, check
2265
2594
     that we can use it. */
2299
2628
  dag_node_t *file;
2300
2629
 
2301
2630
  /* First create a dag_node_t from the root/path pair. */
2302
 
  SVN_ERR(get_dag(&file, root, path, pool));
 
2631
  SVN_ERR(get_dag(&file, root, path, FALSE, pool));
2303
2632
 
2304
2633
  /* Now fetch its length */
2305
2634
  return svn_fs_fs__dag_file_length(length_p, file, pool);
2318
2647
{
2319
2648
  dag_node_t *file;
2320
2649
 
2321
 
  SVN_ERR(get_dag(&file, root, path, pool));
 
2650
  SVN_ERR(get_dag(&file, root, path, FALSE, pool));
2322
2651
  return svn_fs_fs__dag_file_checksum(checksum, file, kind, pool);
2323
2652
}
2324
2653
 
2337
2666
  svn_stream_t *file_stream;
2338
2667
 
2339
2668
  /* First create a dag_node_t from the root/path pair. */
2340
 
  SVN_ERR(get_dag(&node, root, path, pool));
 
2669
  SVN_ERR(get_dag(&node, root, path, FALSE, pool));
2341
2670
 
2342
2671
  /* Then create a readable stream from the dag_node_t. */
2343
2672
  SVN_ERR(svn_fs_fs__dag_get_contents(&file_stream, node, pool));
2349
2678
/* --- End machinery for svn_fs_file_contents() ---  */
2350
2679
 
2351
2680
 
 
2681
/* --- Machinery for svn_fs_try_process_file_contents() ---  */
 
2682
 
 
2683
static svn_error_t *
 
2684
fs_try_process_file_contents(svn_boolean_t *success,
 
2685
                             svn_fs_root_t *root,
 
2686
                             const char *path,
 
2687
                             svn_fs_process_contents_func_t processor,
 
2688
                             void* baton,
 
2689
                             apr_pool_t *pool)
 
2690
{
 
2691
  dag_node_t *node;
 
2692
  SVN_ERR(get_dag(&node, root, path, FALSE, pool));
 
2693
 
 
2694
  return svn_fs_fs__dag_try_process_file_contents(success, node,
 
2695
                                                  processor, baton, pool);
 
2696
}
 
2697
 
 
2698
/* --- End machinery for svn_fs_try_process_file_contents() ---  */
 
2699
 
2352
2700
 
2353
2701
/* --- Machinery for svn_fs_apply_textdelta() ---  */
2354
2702
 
2440
2788
      SVN_ERR(svn_stream_write(tb->target_stream,
2441
2789
                               tb->target_string->data,
2442
2790
                               &len));
2443
 
      svn_stringbuf_set(tb->target_string, "");
 
2791
      svn_stringbuf_setempty(tb->target_string);
2444
2792
    }
2445
2793
 
2446
2794
  /* Is the window NULL?  If so, we're done. */
2507
2855
 
2508
2856
  /* Make a writable "string" stream which writes data to
2509
2857
     tb->target_string. */
2510
 
  tb->target_string = svn_stringbuf_create("", tb->pool);
 
2858
  tb->target_string = svn_stringbuf_create_empty(tb->pool);
2511
2859
  tb->string_stream = svn_stream_create(tb, tb->pool);
2512
2860
  svn_stream_set_write(tb->string_stream, write_to_string);
2513
2861
 
2543
2891
  txdelta_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
2544
2892
 
2545
2893
  tb->root = root;
2546
 
  tb->path = path;
 
2894
  tb->path = svn_fs__canonicalize_abspath(path, pool);
2547
2895
  tb->pool = pool;
2548
 
 
2549
 
  if (base_checksum)
2550
 
    tb->base_checksum = svn_checksum_dup(base_checksum, pool);
2551
 
  else
2552
 
    tb->base_checksum = NULL;
2553
 
 
2554
 
  if (result_checksum)
2555
 
    tb->result_checksum = svn_checksum_dup(result_checksum, pool);
2556
 
  else
2557
 
    tb->result_checksum = NULL;
2558
 
 
 
2896
  tb->base_checksum = svn_checksum_dup(base_checksum, pool);
 
2897
  tb->result_checksum = svn_checksum_dup(result_checksum, pool);
2559
2898
 
2560
2899
  SVN_ERR(apply_textdelta(tb, pool));
2561
2900
 
2685
3024
  struct text_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
2686
3025
 
2687
3026
  tb->root = root;
2688
 
  tb->path = path;
 
3027
  tb->path = svn_fs__canonicalize_abspath(path, pool);
2689
3028
  tb->pool = pool;
2690
 
 
2691
 
  if (result_checksum)
2692
 
    tb->result_checksum = svn_checksum_dup(result_checksum, pool);
2693
 
  else
2694
 
    tb->result_checksum = NULL;
 
3029
  tb->result_checksum = svn_checksum_dup(result_checksum, pool);
2695
3030
 
2696
3031
  SVN_ERR(apply_text(tb, pool));
2697
3032
 
2736
3071
        (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path2);
2737
3072
  }
2738
3073
 
2739
 
  SVN_ERR(get_dag(&node1, root1, path1, pool));
2740
 
  SVN_ERR(get_dag(&node2, root2, path2, pool));
 
3074
  SVN_ERR(get_dag(&node1, root1, path1, TRUE, pool));
 
3075
  SVN_ERR(get_dag(&node2, root2, path2, TRUE, pool));
2741
3076
  return svn_fs_fs__dag_things_different(NULL, changed_p,
2742
 
                                         node1, node2, pool);
 
3077
                                         node1, node2);
2743
3078
}
2744
3079
 
2745
3080
 
2757
3092
  dag_node_t *source_node, *target_node;
2758
3093
 
2759
3094
  if (source_root && source_path)
2760
 
    SVN_ERR(get_dag(&source_node, source_root, source_path, pool));
 
3095
    SVN_ERR(get_dag(&source_node, source_root, source_path, TRUE, pool));
2761
3096
  else
2762
3097
    source_node = NULL;
2763
 
  SVN_ERR(get_dag(&target_node, target_root, target_path, pool));
 
3098
  SVN_ERR(get_dag(&target_node, target_root, target_path, TRUE, pool));
2764
3099
 
2765
3100
  /* Create a delta stream that turns the source into the target.  */
2766
3101
  return svn_fs_fs__dag_get_file_delta_stream(stream_p, source_node,
2859
3194
                       parent_path_t *parent_path,
2860
3195
                       apr_pool_t *pool)
2861
3196
{
2862
 
  svn_revnum_t rev_mine, rev_parent = -1;
2863
 
  const char *path_mine, *path_parent;
 
3197
  svn_revnum_t rev_mine;
 
3198
  svn_revnum_t rev_parent = SVN_INVALID_REVNUM;
 
3199
  const char *path_mine;
 
3200
  const char *path_parent = NULL;
2864
3201
 
2865
3202
  /* First find our parent's youngest copyroot. */
2866
3203
  if (parent_path->parent)
2869
3206
 
2870
3207
  /* Find our copyroot. */
2871
3208
  SVN_ERR(svn_fs_fs__dag_get_copyroot(&rev_mine, &path_mine,
2872
 
                                      parent_path->node, pool));
 
3209
                                      parent_path->node));
2873
3210
 
2874
3211
  /* If a parent and child were copied to in the same revision, prefer
2875
3212
     the child copy target, since it is the copy relevant to the
2907
3244
  *root_p = NULL;
2908
3245
  *path_p = NULL;
2909
3246
 
 
3247
  path = svn_fs__canonicalize_abspath(path, pool);
2910
3248
  SVN_ERR(open_path(&parent_path, root, path, 0, NULL, pool));
2911
3249
 
2912
3250
  /* Find the youngest copyroot in the path of this node-rev, which
2925
3263
  if (kind == svn_node_none)
2926
3264
    return SVN_NO_ERROR;
2927
3265
  SVN_ERR(open_path(&copy_dst_parent_path, copy_dst_root, path,
2928
 
                    0, NULL, pool));
 
3266
                    open_path_node_only, NULL, pool));
2929
3267
  copy_dst_node = copy_dst_parent_path->node;
2930
3268
  if (! svn_fs_fs__id_check_related(svn_fs_fs__dag_get_id(copy_dst_node),
2931
3269
                                    svn_fs_fs__dag_get_id(parent_path->node)))
2949
3287
  if (created_rev == copy_dst_rev)
2950
3288
    {
2951
3289
      const svn_fs_id_t *pred;
2952
 
      SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred, copy_dst_node, pool));
 
3290
      SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred, copy_dst_node));
2953
3291
      if (! pred)
2954
3292
        return SVN_NO_ERROR;
2955
3293
    }
2977
3315
              const char *path,
2978
3316
              apr_pool_t *pool)
2979
3317
{
2980
 
  const char *copy_path, *copy_src_path, *remainder = "";
 
3318
  const char *copy_path, *copy_src_path, *remainder_path;
2981
3319
  svn_fs_root_t *copy_root;
2982
3320
  svn_revnum_t copy_src_rev;
2983
3321
 
3006
3344
  */
3007
3345
  SVN_ERR(fs_copied_from(&copy_src_rev, &copy_src_path,
3008
3346
                         copy_root, copy_path, pool));
3009
 
  if (strcmp(copy_path, path) != 0)
3010
 
    remainder = svn_relpath__is_child(copy_path, path, pool);
3011
 
  *prev_path = svn_fspath__join(copy_src_path, remainder, pool);
 
3347
  remainder_path = svn_fspath__skip_ancestor(copy_path, path);
 
3348
  *prev_path = svn_fspath__join(copy_src_path, remainder_path, pool);
3012
3349
  *prev_rev = copy_src_rev;
3013
3350
  return SVN_NO_ERROR;
3014
3351
}
3046
3383
      return SVN_NO_ERROR;
3047
3384
    }
3048
3385
 
 
3386
  /* The root node always has ID 0, created in revision 0 and will never
 
3387
     use the new-style ID format. */
 
3388
  if (strcmp(node_id, "0") == 0)
 
3389
    {
 
3390
      *revision = 0;
 
3391
      return SVN_NO_ERROR;
 
3392
    }
 
3393
 
3049
3394
  /* OK, it's an old-style ID?  Maybe it's cached. */
3050
3395
  SVN_ERR(svn_fs_fs__get_node_origin(&cached_origin_id,
3051
3396
                                     fs,
3100
3445
 
3101
3446
    /* Walk the predecessor links back to origin. */
3102
3447
    SVN_ERR(svn_fs_fs__node_id(&pred_id, curroot, lastpath->data, predidpool));
3103
 
    while (pred_id)
 
3448
    do
3104
3449
      {
3105
3450
        svn_pool_clear(subpool);
3106
3451
        SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, subpool));
3111
3456
           value cached in the node (which is allocated in
3112
3457
           SUBPOOL... maybe). */
3113
3458
        svn_pool_clear(predidpool);
3114
 
        SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node, subpool));
 
3459
        SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
3115
3460
        pred_id = pred_id ? svn_fs_fs__id_copy(pred_id, predidpool) : NULL;
3116
3461
      }
 
3462
    while (pred_id);
3117
3463
 
3118
3464
    /* When we get here, NODE should be the first node-revision in our
3119
3465
       chain. */
3212
3558
             no predecessor, in which case we're all done!). */
3213
3559
          const svn_fs_id_t *pred_id;
3214
3560
 
3215
 
          SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node, pool));
 
3561
          SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
3216
3562
          if (! pred_id)
3217
3563
            return SVN_NO_ERROR;
3218
3564
 
3236
3582
 
3237
3583
  if (copyroot_rev > commit_rev)
3238
3584
    {
3239
 
      const char *remainder;
 
3585
      const char *remainder_path;
3240
3586
      const char *copy_dst, *copy_src;
3241
3587
      svn_fs_root_t *copyroot_root;
3242
3588
 
3243
3589
      SVN_ERR(svn_fs_fs__revision_root(&copyroot_root, fs, copyroot_rev,
3244
3590
                                       pool));
3245
 
      SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, pool));
 
3591
      SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, FALSE, pool));
3246
3592
      copy_dst = svn_fs_fs__dag_get_created_path(node);
3247
3593
 
3248
3594
      /* If our current path was the very destination of the copy,
3253
3599
         the copy source.  Finally, if our current path doesn't meet
3254
3600
         one of these other criteria ... ### for now just fallback to
3255
3601
         the old copy hunt algorithm. */
3256
 
      if (strcmp(path, copy_dst) == 0)
3257
 
        remainder = "";
3258
 
      else
3259
 
        remainder = svn_relpath__is_child(copy_dst, path, pool);
 
3602
      remainder_path = svn_fspath__skip_ancestor(copy_dst, path);
3260
3603
 
3261
 
      if (remainder)
 
3604
      if (remainder_path)
3262
3605
        {
3263
3606
          /* If we get here, then our current path is the destination
3264
3607
             of, or the child of the destination of, a copy.  Fill
3265
3608
             in the return values and get outta here.  */
3266
 
          SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(&src_rev, node, pool));
3267
 
          SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(&copy_src, node, pool));
 
3609
          SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(&src_rev, node));
 
3610
          SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(&copy_src, node));
3268
3611
 
3269
3612
          dst_rev = copyroot_rev;
3270
 
          src_path = svn_fspath__join(copy_src, remainder, pool);
 
3613
          src_path = svn_fspath__join(copy_src, remainder_path, pool);
3271
3614
        }
3272
3615
    }
3273
3616
 
3388
3731
{
3389
3732
  svn_fs_history_t *history = apr_pcalloc(pool, sizeof(*history));
3390
3733
  fs_history_data_t *fhd = apr_pcalloc(pool, sizeof(*fhd));
3391
 
  fhd->path = path;
 
3734
  fhd->path = svn_fs__canonicalize_abspath(path, pool);
3392
3735
  fhd->revision = revision;
3393
3736
  fhd->is_interesting = is_interesting;
3394
3737
  fhd->path_hint = path_hint;
3428
3771
  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3429
3772
 
3430
3773
  SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, dir_dag,
3431
 
                                     scratch_pool, scratch_pool));
 
3774
                                     scratch_pool));
3432
3775
 
3433
3776
  for (hi = apr_hash_first(scratch_pool, entries);
3434
3777
       hi;
3442
3785
      svn_pool_clear(iterpool);
3443
3786
 
3444
3787
      kid_path = svn_fspath__join(this_path, dirent->name, iterpool);
3445
 
      SVN_ERR(get_dag(&kid_dag, root, kid_path, iterpool));
 
3788
      SVN_ERR(get_dag(&kid_dag, root, kid_path, TRUE, iterpool));
3446
3789
 
3447
 
      SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, kid_dag, iterpool));
3448
 
      SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down, kid_dag,
3449
 
                                                            iterpool));
 
3790
      SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, kid_dag));
 
3791
      SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down, kid_dag));
3450
3792
 
3451
3793
      if (has_mergeinfo)
3452
3794
        {
3457
3799
          svn_error_t *err;
3458
3800
 
3459
3801
          SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, kid_dag, iterpool));
3460
 
          mergeinfo_string = apr_hash_get(proplist, SVN_PROP_MERGEINFO,
3461
 
                                          APR_HASH_KEY_STRING);
 
3802
          mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
3462
3803
          if (!mergeinfo_string)
3463
3804
            {
3464
3805
              svn_string_t *idstr = svn_fs_fs__id_unparse(dirent->id, iterpool);
3483
3824
              }
3484
3825
          else
3485
3826
            {
3486
 
              apr_hash_set(result_catalog,
3487
 
                           apr_pstrdup(result_pool, kid_path),
3488
 
                           APR_HASH_KEY_STRING,
3489
 
                           kid_mergeinfo);
 
3827
              svn_hash_sets(result_catalog, apr_pstrdup(result_pool, kid_path),
 
3828
                            kid_mergeinfo);
3490
3829
            }
3491
3830
        }
3492
3831
 
3503
3842
  return SVN_NO_ERROR;
3504
3843
}
3505
3844
 
 
3845
/* Return the cache key as a combination of REV_ROOT->REV, the inheritance
 
3846
   flags INHERIT and ADJUST_INHERITED_MERGEINFO, and the PATH.  The result
 
3847
   will be allocated in POOL..
 
3848
 */
 
3849
static const char *
 
3850
mergeinfo_cache_key(const char *path,
 
3851
                    svn_fs_root_t *rev_root,
 
3852
                    svn_mergeinfo_inheritance_t inherit,
 
3853
                    svn_boolean_t adjust_inherited_mergeinfo,
 
3854
                    apr_pool_t *pool)
 
3855
{
 
3856
  apr_int64_t number = rev_root->rev;
 
3857
  number = number * 4
 
3858
         + (inherit == svn_mergeinfo_nearest_ancestor ? 2 : 0)
 
3859
         + (adjust_inherited_mergeinfo ? 1 : 0);
 
3860
 
 
3861
  return svn_fs_fs__combine_number_and_string(number, path, pool);
 
3862
}
3506
3863
 
3507
3864
/* Calculates the mergeinfo for PATH under REV_ROOT using inheritance
3508
3865
   type INHERIT.  Returns it in *MERGEINFO, or NULL if there is none.
3510
3867
   used for temporary allocations.
3511
3868
 */
3512
3869
static svn_error_t *
3513
 
get_mergeinfo_for_path(svn_mergeinfo_t *mergeinfo,
3514
 
                       svn_fs_root_t *rev_root,
3515
 
                       const char *path,
3516
 
                       svn_mergeinfo_inheritance_t inherit,
3517
 
                       apr_pool_t *result_pool,
3518
 
                       apr_pool_t *scratch_pool)
 
3870
get_mergeinfo_for_path_internal(svn_mergeinfo_t *mergeinfo,
 
3871
                                svn_fs_root_t *rev_root,
 
3872
                                const char *path,
 
3873
                                svn_mergeinfo_inheritance_t inherit,
 
3874
                                svn_boolean_t adjust_inherited_mergeinfo,
 
3875
                                apr_pool_t *result_pool,
 
3876
                                apr_pool_t *scratch_pool)
3519
3877
{
3520
3878
  parent_path_t *parent_path, *nearest_ancestor;
3521
3879
  apr_hash_t *proplist;
3522
3880
  svn_string_t *mergeinfo_string;
3523
 
  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3524
 
 
3525
 
  *mergeinfo = NULL;
3526
3881
 
3527
3882
  path = svn_fs__canonicalize_abspath(path, scratch_pool);
3528
3883
 
3540
3895
    {
3541
3896
      svn_boolean_t has_mergeinfo;
3542
3897
 
3543
 
      svn_pool_clear(iterpool);
3544
 
 
3545
3898
      SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo,
3546
 
                                           nearest_ancestor->node, iterpool));
 
3899
                                           nearest_ancestor->node));
3547
3900
      if (has_mergeinfo)
3548
3901
        break;
3549
3902
 
3550
3903
      /* No need to loop if we're looking for explicit mergeinfo. */
3551
3904
      if (inherit == svn_mergeinfo_explicit)
3552
3905
        {
3553
 
          svn_pool_destroy(iterpool);
3554
3906
          return SVN_NO_ERROR;
3555
3907
        }
3556
3908
 
3559
3911
      /* Run out?  There's no mergeinfo. */
3560
3912
      if (!nearest_ancestor)
3561
3913
        {
3562
 
          svn_pool_destroy(iterpool);
3563
3914
          return SVN_NO_ERROR;
3564
3915
        }
3565
3916
    }
3566
3917
 
3567
 
  svn_pool_destroy(iterpool);
3568
 
 
3569
3918
  SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, nearest_ancestor->node,
3570
3919
                                      scratch_pool));
3571
 
  mergeinfo_string = apr_hash_get(proplist, SVN_PROP_MERGEINFO,
3572
 
                                  APR_HASH_KEY_STRING);
 
3920
  mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
3573
3921
  if (!mergeinfo_string)
3574
3922
    return svn_error_createf
3575
3923
      (SVN_ERR_FS_CORRUPT, NULL,
3600
3948
     can return the mergeinfo results directly.  Otherwise, we're
3601
3949
     inheriting the mergeinfo, so we need to a) remove non-inheritable
3602
3950
     ranges and b) telescope the merged-from paths. */
3603
 
  if (nearest_ancestor != parent_path)
 
3951
  if (adjust_inherited_mergeinfo && (nearest_ancestor != parent_path))
3604
3952
    {
3605
3953
      svn_mergeinfo_t tmp_mergeinfo;
3606
3954
 
3618
3966
  return SVN_NO_ERROR;
3619
3967
}
3620
3968
 
 
3969
/* Caching wrapper around get_mergeinfo_for_path_internal().
 
3970
 */
 
3971
static svn_error_t *
 
3972
get_mergeinfo_for_path(svn_mergeinfo_t *mergeinfo,
 
3973
                       svn_fs_root_t *rev_root,
 
3974
                       const char *path,
 
3975
                       svn_mergeinfo_inheritance_t inherit,
 
3976
                       svn_boolean_t adjust_inherited_mergeinfo,
 
3977
                       apr_pool_t *result_pool,
 
3978
                       apr_pool_t *scratch_pool)
 
3979
{
 
3980
  fs_fs_data_t *ffd = rev_root->fs->fsap_data;
 
3981
  const char *cache_key;
 
3982
  svn_boolean_t found = FALSE;
 
3983
  svn_stringbuf_t *mergeinfo_exists;
 
3984
 
 
3985
  *mergeinfo = NULL;
 
3986
 
 
3987
  cache_key = mergeinfo_cache_key(path, rev_root, inherit,
 
3988
                                  adjust_inherited_mergeinfo, scratch_pool);
 
3989
  if (ffd->mergeinfo_existence_cache)
 
3990
    {
 
3991
      SVN_ERR(svn_cache__get((void **)&mergeinfo_exists, &found,
 
3992
                             ffd->mergeinfo_existence_cache,
 
3993
                             cache_key, result_pool));
 
3994
      if (found && mergeinfo_exists->data[0] == '1')
 
3995
        SVN_ERR(svn_cache__get((void **)mergeinfo, &found,
 
3996
                              ffd->mergeinfo_cache,
 
3997
                              cache_key, result_pool));
 
3998
    }
 
3999
 
 
4000
  if (! found)
 
4001
    {
 
4002
      SVN_ERR(get_mergeinfo_for_path_internal(mergeinfo, rev_root, path,
 
4003
                                              inherit,
 
4004
                                              adjust_inherited_mergeinfo,
 
4005
                                              result_pool, scratch_pool));
 
4006
      if (ffd->mergeinfo_existence_cache)
 
4007
        {
 
4008
          mergeinfo_exists = svn_stringbuf_create(*mergeinfo ? "1" : "0",
 
4009
                                                  scratch_pool);
 
4010
          SVN_ERR(svn_cache__set(ffd->mergeinfo_existence_cache,
 
4011
                                 cache_key, mergeinfo_exists, scratch_pool));
 
4012
          if (*mergeinfo)
 
4013
            SVN_ERR(svn_cache__set(ffd->mergeinfo_cache,
 
4014
                                  cache_key, *mergeinfo, scratch_pool));
 
4015
        }
 
4016
    }
 
4017
 
 
4018
  return SVN_NO_ERROR;
 
4019
}
 
4020
 
3621
4021
/* Adds mergeinfo for each descendant of PATH (but not PATH itself)
3622
4022
   under ROOT to RESULT_CATALOG.  Returned values are allocated in
3623
4023
   RESULT_POOL; temporary values in POOL. */
3631
4031
  dag_node_t *this_dag;
3632
4032
  svn_boolean_t go_down;
3633
4033
 
3634
 
  SVN_ERR(get_dag(&this_dag, root, path, scratch_pool));
 
4034
  SVN_ERR(get_dag(&this_dag, root, path, TRUE, scratch_pool));
3635
4035
  SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down,
3636
 
                                                        this_dag,
3637
 
                                                        scratch_pool));
 
4036
                                                        this_dag));
3638
4037
  if (go_down)
3639
4038
    SVN_ERR(crawl_directory_dag_for_mergeinfo(root,
3640
4039
                                              path,
3655
4054
                         const apr_array_header_t *paths,
3656
4055
                         svn_mergeinfo_inheritance_t inherit,
3657
4056
                         svn_boolean_t include_descendants,
3658
 
                         apr_pool_t *pool)
 
4057
                         svn_boolean_t adjust_inherited_mergeinfo,
 
4058
                         apr_pool_t *result_pool,
 
4059
                         apr_pool_t *scratch_pool)
3659
4060
{
3660
 
  svn_mergeinfo_catalog_t result_catalog = apr_hash_make(pool);
3661
 
  apr_pool_t *iterpool = svn_pool_create(pool);
 
4061
  svn_mergeinfo_catalog_t result_catalog = svn_hash__make(result_pool);
 
4062
  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3662
4063
  int i;
3663
4064
 
3664
4065
  for (i = 0; i < paths->nelts; i++)
3670
4071
      svn_pool_clear(iterpool);
3671
4072
 
3672
4073
      err = get_mergeinfo_for_path(&path_mergeinfo, root, path,
3673
 
                                   inherit, pool, iterpool);
 
4074
                                   inherit, adjust_inherited_mergeinfo,
 
4075
                                   result_pool, iterpool);
3674
4076
      if (err)
3675
4077
        {
3676
4078
          if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
3686
4088
        }
3687
4089
 
3688
4090
      if (path_mergeinfo)
3689
 
        apr_hash_set(result_catalog, path, APR_HASH_KEY_STRING,
3690
 
                     path_mergeinfo);
 
4091
        svn_hash_sets(result_catalog, path, path_mergeinfo);
3691
4092
      if (include_descendants)
3692
 
        SVN_ERR(add_descendant_mergeinfo(result_catalog, root, path, pool,
3693
 
                                         iterpool));
 
4093
        SVN_ERR(add_descendant_mergeinfo(result_catalog, root, path,
 
4094
                                         result_pool, scratch_pool));
3694
4095
    }
3695
4096
  svn_pool_destroy(iterpool);
3696
4097
 
3706
4107
                 const apr_array_header_t *paths,
3707
4108
                 svn_mergeinfo_inheritance_t inherit,
3708
4109
                 svn_boolean_t include_descendants,
3709
 
                 apr_pool_t *pool)
 
4110
                 svn_boolean_t adjust_inherited_mergeinfo,
 
4111
                 apr_pool_t *result_pool,
 
4112
                 apr_pool_t *scratch_pool)
3710
4113
{
3711
4114
  fs_fs_data_t *ffd = root->fs->fsap_data;
3712
4115
 
3725
4128
  /* Retrieve a path -> mergeinfo hash mapping. */
3726
4129
  return get_mergeinfos_for_paths(root, catalog, paths,
3727
4130
                                  inherit,
3728
 
                                  include_descendants, pool);
 
4131
                                  include_descendants,
 
4132
                                  adjust_inherited_mergeinfo,
 
4133
                                  result_pool, scratch_pool);
3729
4134
}
3730
4135
 
3731
4136
 
3752
4157
  fs_file_length,
3753
4158
  fs_file_checksum,
3754
4159
  fs_file_contents,
 
4160
  fs_try_process_file_contents,
3755
4161
  fs_make_file,
3756
4162
  fs_apply_textdelta,
3757
4163
  fs_apply_text,
3791
4197
  root->rev = rev;
3792
4198
 
3793
4199
  frd->root_dir = root_dir;
3794
 
  frd->copyfrom_cache = apr_hash_make(root->pool);
 
4200
  frd->copyfrom_cache = svn_hash__make(root->pool);
3795
4201
 
3796
4202
  root->fsap_data = frd;
3797
4203
 
3818
4224
  root->txn_flags = flags;
3819
4225
  root->rev = base_rev;
3820
4226
 
 
4227
  frd->txn_id = txn;
 
4228
 
3821
4229
  /* Because this cache actually tries to invalidate elements, keep
3822
4230
     the number of elements per page down.
3823
4231
 
3828
4236
                                      svn_fs_fs__dag_deserialize,
3829
4237
                                      APR_HASH_KEY_STRING,
3830
4238
                                      32, 20, FALSE,
3831
 
                                      apr_pstrcat(pool, txn, ":TXN", (char *)NULL),
 
4239
                                      apr_pstrcat(pool, txn, ":TXN",
 
4240
                                                  (char *)NULL),
3832
4241
                                      root->pool));
3833
4242
 
3834
4243
  /* Initialize transaction-local caches in FS.
3842
4251
  *root_p = root;
3843
4252
  return SVN_NO_ERROR;
3844
4253
}
 
4254
 
 
4255
 
 
4256
 
 
4257
/* Verify. */
 
4258
static APR_INLINE const char *
 
4259
stringify_node(dag_node_t *node,
 
4260
               apr_pool_t *pool)
 
4261
{
 
4262
  /* ### TODO: print some PATH@REV to it, too. */
 
4263
  return svn_fs_fs__id_unparse(svn_fs_fs__dag_get_id(node), pool)->data;
 
4264
}
 
4265
 
 
4266
/* Check metadata sanity on NODE, and on its children.  Manually verify
 
4267
   information for DAG nodes in revision REV, and trust the metadata
 
4268
   accuracy for nodes belonging to older revisions. */
 
4269
static svn_error_t *
 
4270
verify_node(dag_node_t *node,
 
4271
            svn_revnum_t rev,
 
4272
            apr_pool_t *pool)
 
4273
{
 
4274
  svn_boolean_t has_mergeinfo;
 
4275
  apr_int64_t mergeinfo_count;
 
4276
  const svn_fs_id_t *pred_id;
 
4277
  svn_fs_t *fs = svn_fs_fs__dag_get_fs(node);
 
4278
  int pred_count;
 
4279
  svn_node_kind_t kind;
 
4280
  apr_pool_t *iterpool = svn_pool_create(pool);
 
4281
 
 
4282
  /* Fetch some data. */
 
4283
  SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, node));
 
4284
  SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_count, node));
 
4285
  SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
 
4286
  SVN_ERR(svn_fs_fs__dag_get_predecessor_count(&pred_count, node));
 
4287
  kind = svn_fs_fs__dag_node_kind(node);
 
4288
 
 
4289
  /* Sanity check. */
 
4290
  if (mergeinfo_count < 0)
 
4291
    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
4292
                             "Negative mergeinfo-count %" APR_INT64_T_FMT
 
4293
                             " on node '%s'",
 
4294
                             mergeinfo_count, stringify_node(node, iterpool));
 
4295
 
 
4296
  /* Issue #4129. (This check will explicitly catch non-root instances too.) */
 
4297
  if (pred_id)
 
4298
    {
 
4299
      dag_node_t *pred;
 
4300
      int pred_pred_count;
 
4301
      SVN_ERR(svn_fs_fs__dag_get_node(&pred, fs, pred_id, iterpool));
 
4302
      SVN_ERR(svn_fs_fs__dag_get_predecessor_count(&pred_pred_count, pred));
 
4303
      if (pred_pred_count+1 != pred_count)
 
4304
        return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
4305
                                 "Predecessor count mismatch: "
 
4306
                                 "%s has %d, but %s has %d",
 
4307
                                 stringify_node(node, iterpool), pred_count,
 
4308
                                 stringify_node(pred, iterpool),
 
4309
                                 pred_pred_count);
 
4310
    }
 
4311
 
 
4312
  /* Kind-dependent verifications. */
 
4313
  if (kind == svn_node_none)
 
4314
    {
 
4315
      return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
4316
                               "Node '%s' has kind 'none'",
 
4317
                               stringify_node(node, iterpool));
 
4318
    }
 
4319
  if (kind == svn_node_file)
 
4320
    {
 
4321
      if (has_mergeinfo != mergeinfo_count) /* comparing int to bool */
 
4322
        return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
4323
                                 "File node '%s' has inconsistent mergeinfo: "
 
4324
                                 "has_mergeinfo=%d, "
 
4325
                                 "mergeinfo_count=%" APR_INT64_T_FMT,
 
4326
                                 stringify_node(node, iterpool),
 
4327
                                 has_mergeinfo, mergeinfo_count);
 
4328
    }
 
4329
  if (kind == svn_node_dir)
 
4330
    {
 
4331
      apr_hash_t *entries;
 
4332
      apr_hash_index_t *hi;
 
4333
      apr_int64_t children_mergeinfo = 0;
 
4334
 
 
4335
      SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool));
 
4336
 
 
4337
      /* Compute CHILDREN_MERGEINFO. */
 
4338
      for (hi = apr_hash_first(pool, entries);
 
4339
           hi;
 
4340
           hi = apr_hash_next(hi))
 
4341
        {
 
4342
          svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
 
4343
          dag_node_t *child;
 
4344
          svn_revnum_t child_rev;
 
4345
          apr_int64_t child_mergeinfo;
 
4346
 
 
4347
          svn_pool_clear(iterpool);
 
4348
 
 
4349
          /* Compute CHILD_REV. */
 
4350
          SVN_ERR(svn_fs_fs__dag_get_node(&child, fs, dirent->id, iterpool));
 
4351
          SVN_ERR(svn_fs_fs__dag_get_revision(&child_rev, child, iterpool));
 
4352
 
 
4353
          if (child_rev == rev)
 
4354
            SVN_ERR(verify_node(child, rev, iterpool));
 
4355
 
 
4356
          SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&child_mergeinfo, child));
 
4357
          children_mergeinfo += child_mergeinfo;
 
4358
        }
 
4359
 
 
4360
      /* Side-effect of issue #4129. */
 
4361
      if (children_mergeinfo+has_mergeinfo != mergeinfo_count)
 
4362
        return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
4363
                                 "Mergeinfo-count discrepancy on '%s': "
 
4364
                                 "expected %" APR_INT64_T_FMT "+%d, "
 
4365
                                 "counted %" APR_INT64_T_FMT,
 
4366
                                 stringify_node(node, iterpool),
 
4367
                                 mergeinfo_count, has_mergeinfo,
 
4368
                                 children_mergeinfo);
 
4369
    }
 
4370
 
 
4371
  svn_pool_destroy(iterpool);
 
4372
  return SVN_NO_ERROR;
 
4373
}
 
4374
 
 
4375
svn_error_t *
 
4376
svn_fs_fs__verify_root(svn_fs_root_t *root,
 
4377
                       apr_pool_t *pool)
 
4378
{
 
4379
  svn_fs_t *fs = root->fs;
 
4380
  dag_node_t *root_dir;
 
4381
 
 
4382
  /* Issue #4129: bogus pred-counts and minfo-cnt's on the root node-rev
 
4383
     (and elsewhere).  This code makes more thorough checks than the
 
4384
     commit-time checks in validate_root_noderev(). */
 
4385
 
 
4386
  /* Callers should disable caches by setting SVN_FS_CONFIG_FSFS_CACHE_NS;
 
4387
     see r1462436.
 
4388
 
 
4389
     When this code is called in the library, we want to ensure we
 
4390
     use the on-disk data --- rather than some data that was read
 
4391
     in the possibly-distance past and cached since. */
 
4392
 
 
4393
  if (root->is_txn_root)
 
4394
    {
 
4395
      fs_txn_root_data_t *frd = root->fsap_data;
 
4396
      SVN_ERR(svn_fs_fs__dag_txn_root(&root_dir, fs, frd->txn_id, pool));
 
4397
    }
 
4398
  else
 
4399
    {
 
4400
      fs_rev_root_data_t *frd = root->fsap_data;
 
4401
      root_dir = frd->root_dir;
 
4402
    }
 
4403
 
 
4404
  /* Recursively verify ROOT_DIR. */
 
4405
  SVN_ERR(verify_node(root_dir, root->rev, pool));
 
4406
 
 
4407
  /* Verify explicitly the predecessor of the root. */
 
4408
  {
 
4409
    const svn_fs_id_t *pred_id;
 
4410
 
 
4411
    /* Only r0 should have no predecessor. */
 
4412
    SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, root_dir));
 
4413
    if (! root->is_txn_root && !!pred_id != !!root->rev)
 
4414
      return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
4415
                               "r%ld's root node's predecessor is "
 
4416
                               "unexpectedly '%s'",
 
4417
                               root->rev,
 
4418
                               (pred_id
 
4419
                                ? svn_fs_fs__id_unparse(pred_id, pool)->data
 
4420
                                : "(null)"));
 
4421
    if (root->is_txn_root && !pred_id)
 
4422
      return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
4423
                               "Transaction '%s''s root node's predecessor is "
 
4424
                               "unexpectedly NULL",
 
4425
                               root->txn);
 
4426
 
 
4427
    /* Check the predecessor's revision. */
 
4428
    if (pred_id)
 
4429
      {
 
4430
        svn_revnum_t pred_rev = svn_fs_fs__id_rev(pred_id);
 
4431
        if (! root->is_txn_root && pred_rev+1 != root->rev)
 
4432
          /* Issue #4129. */
 
4433
          return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
4434
                                   "r%ld's root node's predecessor is r%ld"
 
4435
                                   " but should be r%ld",
 
4436
                                   root->rev, pred_rev, root->rev - 1);
 
4437
        if (root->is_txn_root && pred_rev != root->rev)
 
4438
          return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
4439
                                   "Transaction '%s''s root node's predecessor"
 
4440
                                   " is r%ld"
 
4441
                                   " but should be r%ld",
 
4442
                                   root->txn, pred_rev, root->rev);
 
4443
      }
 
4444
  }
 
4445
 
 
4446
  return SVN_NO_ERROR;
 
4447
}