137
143
/*** Node Caching ***/
145
/* 1st level cache */
147
/* An entry in the first-level cache. REVISION and PATH form the key that
148
will ultimately be matched.
150
typedef struct cache_entry_t
152
/* hash value derived from PATH, REVISION.
153
Used to short-circuit failed lookups. */
154
apr_uint32_t hash_value;
156
/* revision to which the NODE belongs */
157
svn_revnum_t revision;
159
/* path of the NODE */
162
/* cached value of strlen(PATH). */
165
/* the node allocated in the cache's pool. NULL for empty entries. */
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
176
The actual number of instances may be higher but entries that got
177
overwritten are no longer visible.
179
enum { BUCKET_COUNT = 256 };
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.
185
typedef struct cache_lock_t
187
/* pool holding the lock */
190
/* cache being locked */
191
fs_fs_dag_cache_t *cache;
193
/* next lock. NULL at EOL */
194
struct cache_lock_t *next;
196
/* previous lock. NULL at list head. Only then this==cache->first_lock */
197
struct cache_lock_t *prev;
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
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.
210
struct fs_fs_dag_cache_t
212
/* fixed number of (possibly empty) cache entries */
213
cache_entry_t buckets[BUCKET_COUNT];
215
/* pool used for all node allocation */
218
/* number of entries created from POOL since the last cleanup */
219
apr_size_t insertions;
221
/* Property lookups etc. have a very high locality (75% re-hit).
222
Thus, remember the last hit location for optimistic lookup. */
225
/* List of receiving pools that are still alive. */
226
cache_lock_t *first_lock;
229
/* Cleanup function to be called when a receiving pool gets cleared.
230
Unlocks the cache once.
233
unlock_cache(void *baton_void)
235
cache_lock_t *lock = baton_void;
237
/* remove lock from chain. Update the head */
239
lock->next->prev = lock->prev;
241
lock->prev->next = lock->next;
243
lock->cache->first_lock = lock->next;
248
/* Cleanup function to be called when the cache itself gets destroyed.
249
In that case, we must unregister all unlock requests.
252
unregister_locks(void *baton_void)
254
fs_fs_dag_cache_t *cache = baton_void;
257
for (lock = cache->first_lock; lock; lock = lock->next)
258
apr_pool_cleanup_kill(lock->pool,
266
svn_fs_fs__create_dag_cache(apr_pool_t *pool)
268
fs_fs_dag_cache_t *result = apr_pcalloc(pool, sizeof(*result));
269
result->pool = svn_pool_create(pool);
271
apr_pool_cleanup_register(pool,
274
apr_pool_cleanup_null);
279
/* Prevent the entries in CACHE from being destroyed, for as long as the
283
lock_cache(fs_fs_dag_cache_t* cache, apr_pool_t *pool)
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).
291
cache_lock_t *lock = cache->first_lock;
293
/* try to find an existing lock for POOL.
294
But limit the time spent on chasing pointers. */
296
while (lock && --limiter)
297
if (lock->pool == pool)
300
/* create a new lock and put it at the beginning of the lock chain */
301
lock = apr_palloc(pool, sizeof(*lock));
304
lock->next = cache->first_lock;
307
if (cache->first_lock)
308
cache->first_lock->prev = lock;
309
cache->first_lock = lock;
311
/* instruct POOL to remove the look upon cleanup */
312
apr_pool_cleanup_register(pool,
315
apr_pool_cleanup_null);
318
/* Clears the CACHE at regular intervals (destroying all cached nodes)
321
auto_clear_dag_cache(fs_fs_dag_cache_t* cache)
323
if (cache->first_lock == NULL && cache->insertions > BUCKET_COUNT)
325
svn_pool_clear(cache->pool);
327
memset(cache->buckets, 0, sizeof(cache->buckets));
328
cache->insertions = 0;
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.
336
static cache_entry_t *
337
cache_lookup( fs_fs_dag_cache_t *cache
338
, svn_revnum_t revision
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;
345
#if SVN_UNALIGNED_ACCESS_IS_OK
346
/* "randomizing" / distributing factor used in our hash function */
347
const apr_uint32_t factor = 0xd1f3da69;
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))
359
/* need to do a full lookup. Calculate the hash value
360
(HASH_VALUE has been initialized to REVISION). */
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.
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));
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.
379
hash_value = hash_value * 32 + (hash_value + (unsigned char)path[i]);
381
bucket_index = hash_value + (hash_value >> 16);
382
bucket_index = (bucket_index + (bucket_index >> 8)) % BUCKET_COUNT;
384
/* access the corresponding bucket and remember its location */
385
result = &cache->buckets[bucket_index];
386
cache->last_hit = bucket_index;
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))
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);
410
/* 2nd level cache */
139
412
/* Find and return the DAG node cache for ROOT and the key that
140
413
should be used for PATH. */
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",
431
if (key && path) *key
432
= svn_fs_fs__combine_number_and_string(root->rev, path, pool);
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).
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)
171
450
svn_boolean_t found;
451
dag_node_t *node = NULL;
173
452
svn_cache__t *cache;
176
455
SVN_ERR_ASSERT(*path == '/');
178
locate_cache(&cache, &key, root, path, pool);
180
SVN_ERR(svn_cache__get((void **) &node, &found, cache, key, pool));
457
if (!root->is_txn_root)
183
/* Patch up the FS, since this might have come from an old FS
185
svn_fs_fs__dag_set_fs(node, root->fs);
459
/* immutable DAG node. use the global caches for it */
461
fs_fs_data_t *ffd = root->fs->fsap_data;
462
cache_entry_t *bucket;
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)
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));
473
/* Patch up the FS, since this might have come from an old FS
475
svn_fs_fs__dag_set_fs(node, root->fs);
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);
491
/* DAG is mutable / may become invalid. Use the TXN-local cache */
493
locate_cache(&cache, &key, root, path, pool);
495
SVN_ERR(svn_cache__get((void **) &node, &found, cache, key, pool));
498
/* Patch up the FS, since this might have come from an old FS
500
svn_fs_fs__dag_set_fs(node, root->fs);
190
506
return SVN_NO_ERROR;
590
920
apr_pool_t *pool)
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);
927
/* ensure a canonical path representation */
597
928
const char *path_so_far = "/";
599
/* Make a parent_path item for the root node, using its own current
601
SVN_ERR(root_node(&here, root, pool));
929
apr_pool_t *iterpool = svn_pool_create(pool);
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
935
const char *directory;
936
assert(svn_fs__is_canonical_abspath(path));
937
if (flags & open_path_node_only)
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));
944
/* did the shortcut work? */
947
path_so_far = directory;
948
rest = path + strlen(directory) + 1;
952
/* Make a parent_path item for the root node, using its own current
954
SVN_ERR(root_node(&here, root, pool));
955
rest = path + 1; /* skip the leading '/', it saves in iteration */
602
958
parent_path = make_parent_path(here, 0, 0, pool);
603
959
parent_path->copy_inherit = copy_id_inherit_self;
605
rest = canon_path + 1; /* skip the leading '/', it saves in iteration */
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,
672
1032
/* Other errors we return normally. */
675
/* Now, make a parent_path item for CHILD. */
676
parent_path = make_parent_path(child, entry, parent_path, pool);
679
SVN_ERR(get_copy_inheritance(&inherit, ©_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)
1037
/* Shortcut: the caller only wan'ts the final DAG node. */
1038
parent_path->node = child;
1042
/* Now, make a parent_path item for CHILD. */
1043
parent_path = make_parent_path(child, entry, parent_path, pool);
1046
SVN_ERR(get_copy_inheritance(&inherit, ©_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);
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));
690
1058
/* Are we finished traversing the path? */
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.
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)
814
1189
parent_path_t *parent_path;
817
/* Canonicalize the input PATH. */
818
path = svn_fs__canonicalize_abspath(path, pool);
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;
1192
/* First we look for the DAG in our cache
1193
(if the path may be canonical). */
1195
SVN_ERR(dag_node_cache_get(&node, root, path, needs_lock_cache, pool));
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;
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))
1202
path = svn_fs__canonicalize_abspath(path, pool);
1204
/* Try again with the corrected path. */
1205
SVN_ERR(dag_node_cache_get(&node, root, path, needs_lock_cache,
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,
1216
node = parent_path->node;
1218
/* No need to cache our find -- open_path() will do that for us. */
832
1222
*dag_node_p = node;
1803
2193
dag_node_t *node;
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);
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). */
1814
illegal_path_escape(const char *path, apr_pool_t *pool)
1816
svn_stringbuf_t *retstr;
1817
apr_size_t i, copied = 0;
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++)
1825
c = (unsigned char)path[i];
1826
if (! svn_ctype_iscntrl(c))
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. */
1833
/* First things first, copy all the good stuff that we haven't
1834
yet copied into our output buffer. */
1836
svn_stringbuf_appendbytes(retstr, path + copied,
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);
1847
/* Finally, update our copy counter. */
1851
/* If we didn't encode anything, we don't need to duplicate the string. */
1852
if (retstr->len == 0)
1855
/* Anything left to copy? */
1857
svn_stringbuf_appendbytes(retstr, path + copied, i - copied);
1859
/* retstr is null-terminated either by apr_snprintf or the svn_stringbuf
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);
1865
2200
/* Raise an error if PATH contains a newline because FSFS cannot handle
3618
3966
return SVN_NO_ERROR;
3969
/* Caching wrapper around get_mergeinfo_for_path_internal().
3971
static svn_error_t *
3972
get_mergeinfo_for_path(svn_mergeinfo_t *mergeinfo,
3973
svn_fs_root_t *rev_root,
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)
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;
3987
cache_key = mergeinfo_cache_key(path, rev_root, inherit,
3988
adjust_inherited_mergeinfo, scratch_pool);
3989
if (ffd->mergeinfo_existence_cache)
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));
4002
SVN_ERR(get_mergeinfo_for_path_internal(mergeinfo, rev_root, path,
4004
adjust_inherited_mergeinfo,
4005
result_pool, scratch_pool));
4006
if (ffd->mergeinfo_existence_cache)
4008
mergeinfo_exists = svn_stringbuf_create(*mergeinfo ? "1" : "0",
4010
SVN_ERR(svn_cache__set(ffd->mergeinfo_existence_cache,
4011
cache_key, mergeinfo_exists, scratch_pool));
4013
SVN_ERR(svn_cache__set(ffd->mergeinfo_cache,
4014
cache_key, *mergeinfo, scratch_pool));
4018
return SVN_NO_ERROR;
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. */
3842
4251
*root_p = root;
3843
4252
return SVN_NO_ERROR;
4258
static APR_INLINE const char *
4259
stringify_node(dag_node_t *node,
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;
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,
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);
4279
svn_node_kind_t kind;
4280
apr_pool_t *iterpool = svn_pool_create(pool);
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);
4290
if (mergeinfo_count < 0)
4291
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4292
"Negative mergeinfo-count %" APR_INT64_T_FMT
4294
mergeinfo_count, stringify_node(node, iterpool));
4296
/* Issue #4129. (This check will explicitly catch non-root instances too.) */
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),
4312
/* Kind-dependent verifications. */
4313
if (kind == svn_node_none)
4315
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4316
"Node '%s' has kind 'none'",
4317
stringify_node(node, iterpool));
4319
if (kind == svn_node_file)
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);
4329
if (kind == svn_node_dir)
4331
apr_hash_t *entries;
4332
apr_hash_index_t *hi;
4333
apr_int64_t children_mergeinfo = 0;
4335
SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool));
4337
/* Compute CHILDREN_MERGEINFO. */
4338
for (hi = apr_hash_first(pool, entries);
4340
hi = apr_hash_next(hi))
4342
svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
4344
svn_revnum_t child_rev;
4345
apr_int64_t child_mergeinfo;
4347
svn_pool_clear(iterpool);
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));
4353
if (child_rev == rev)
4354
SVN_ERR(verify_node(child, rev, iterpool));
4356
SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&child_mergeinfo, child));
4357
children_mergeinfo += child_mergeinfo;
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);
4371
svn_pool_destroy(iterpool);
4372
return SVN_NO_ERROR;
4376
svn_fs_fs__verify_root(svn_fs_root_t *root,
4379
svn_fs_t *fs = root->fs;
4380
dag_node_t *root_dir;
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(). */
4386
/* Callers should disable caches by setting SVN_FS_CONFIG_FSFS_CACHE_NS;
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. */
4393
if (root->is_txn_root)
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));
4400
fs_rev_root_data_t *frd = root->fsap_data;
4401
root_dir = frd->root_dir;
4404
/* Recursively verify ROOT_DIR. */
4405
SVN_ERR(verify_node(root_dir, root->rev, pool));
4407
/* Verify explicitly the predecessor of the root. */
4409
const svn_fs_id_t *pred_id;
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'",
4419
? svn_fs_fs__id_unparse(pred_id, pool)->data
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",
4427
/* Check the predecessor's revision. */
4430
svn_revnum_t pred_rev = svn_fs_fs__id_rev(pred_id);
4431
if (! root->is_txn_root && pred_rev+1 != root->rev)
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"
4441
" but should be r%ld",
4442
root->txn, pred_rev, root->rev);
4446
return SVN_NO_ERROR;