1 /* verify.c --- verification of FSFS filesystems
3 * ====================================================================
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
20 * ====================================================================
23 #include "svn_sorts.h"
24 #include "svn_checksum.h"
26 #include "private/svn_subr_private.h"
31 #include "cached_data.h"
32 #include "rep-cache.h"
36 #include "../libsvn_fs/fs-loader.h"
38 #include "svn_private_config.h"
43 /* Baton type expected by verify_walker(). The purpose is to reuse open
44 * rev / pack file handles between calls. Its contents need to be cleaned
45 * periodically to limit resource usage.
47 typedef struct verify_walker_baton_t
49 /* number of calls to verify_walker() since the last clean */
52 /* number of files opened since the last clean */
55 /* progress notification callback to invoke periodically (may be NULL) */
56 svn_fs_progress_notify_func_t notify_func;
58 /* baton to use with NOTIFY_FUNC */
61 /* remember the last revision for which we called notify_func */
62 svn_revnum_t last_notified_revision;
64 /* cached hint for successive calls to svn_fs_fs__check_rep() */
67 /* pool to use for the file handles etc. */
69 } verify_walker_baton_t;
71 /* Used by svn_fs_fs__verify().
72 Implements svn_fs_fs__walk_rep_reference().walker. */
74 verify_walker(representation_t *rep,
77 apr_pool_t *scratch_pool)
79 verify_walker_baton_t *walker_baton = baton;
82 /* notify and free resources periodically */
83 if ( walker_baton->iteration_count > 1000
84 || walker_baton->file_count > 16)
86 if ( walker_baton->notify_func
87 && rep->revision != walker_baton->last_notified_revision)
89 walker_baton->notify_func(rep->revision,
90 walker_baton->notify_baton,
92 walker_baton->last_notified_revision = rep->revision;
95 svn_pool_clear(walker_baton->pool);
97 walker_baton->iteration_count = 0;
98 walker_baton->file_count = 0;
99 walker_baton->hint = NULL;
102 /* access the repo data */
103 previous_hint = walker_baton->hint;
104 SVN_ERR(svn_fs_fs__check_rep(rep, fs, &walker_baton->hint,
105 walker_baton->pool));
107 /* update resource usage counters */
108 walker_baton->iteration_count++;
109 if (previous_hint != walker_baton->hint)
110 walker_baton->file_count++;
115 /* Verify the rep cache DB's consistency with our rev / pack data.
116 * The function signature is similar to svn_fs_fs__verify.
117 * The values of START and END have already been auto-selected and
121 verify_rep_cache(svn_fs_t *fs,
124 svn_fs_progress_notify_func_t notify_func,
126 svn_cancel_func_t cancel_func,
130 svn_boolean_t exists;
132 /* rep-cache verification. */
133 SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, fs, pool));
136 /* provide a baton to allow the reuse of open file handles between
137 iterations (saves 2/3 of OS level file operations). */
138 verify_walker_baton_t *baton = apr_pcalloc(pool, sizeof(*baton));
139 baton->pool = svn_pool_create(pool);
140 baton->last_notified_revision = SVN_INVALID_REVNUM;
141 baton->notify_func = notify_func;
142 baton->notify_baton = notify_baton;
144 /* tell the user that we are now ready to do *something* */
146 notify_func(SVN_INVALID_REVNUM, notify_baton, baton->pool);
148 /* Do not attempt to walk the rep-cache database if its file does
149 not exist, since doing so would create it --- which may confuse
150 the administrator. Don't take any lock. */
151 SVN_ERR(svn_fs_fs__walk_rep_reference(fs, start, end,
152 verify_walker, baton,
153 cancel_func, cancel_baton,
156 /* walker resource cleanup */
157 svn_pool_destroy(baton->pool);
163 /* Verify that the MD5 checksum of the data between offsets START and END
164 * in FILE matches the EXPECTED checksum. If there is a mismatch use the
165 * indedx NAME in the error message. Supports cancellation with CANCEL_FUNC
166 * and CANCEL_BATON. SCRATCH_POOL is for temporary allocations. */
168 verify_index_checksum(apr_file_t *file,
172 svn_checksum_t *expected,
173 svn_cancel_func_t cancel_func,
175 apr_pool_t *scratch_pool)
177 unsigned char buffer[SVN__STREAM_CHUNK_SIZE];
178 apr_off_t size = end - start;
179 svn_checksum_t *actual;
180 svn_checksum_ctx_t *context
181 = svn_checksum_ctx_create(svn_checksum_md5, scratch_pool);
183 /* Calculate the index checksum. */
184 SVN_ERR(svn_io_file_seek(file, APR_SET, &start, scratch_pool));
187 apr_size_t to_read = size > sizeof(buffer)
190 SVN_ERR(svn_io_file_read_full2(file, buffer, to_read, NULL, NULL,
192 SVN_ERR(svn_checksum_update(context, buffer, to_read));
196 SVN_ERR(cancel_func(cancel_baton));
199 SVN_ERR(svn_checksum_final(&actual, context, scratch_pool));
201 /* Verify that it matches the expected checksum. */
202 if (!svn_checksum_match(expected, actual))
204 const char *file_name;
206 SVN_ERR(svn_io_file_name_get(&file_name, file, scratch_pool));
207 SVN_ERR(svn_checksum_mismatch_err(expected, actual, scratch_pool,
208 _("%s checksum mismatch in file %s"),
215 /* Verify the MD5 checksums of the index data in the rev / pack file
216 * containing revision START in FS. If given, invoke CANCEL_FUNC with
217 * CANCEL_BATON at regular intervals. Use SCRATCH_POOL for temporary
221 verify_index_checksums(svn_fs_t *fs,
223 svn_cancel_func_t cancel_func,
225 apr_pool_t *scratch_pool)
227 svn_fs_fs__revision_file_t *rev_file;
229 /* Open the rev / pack file and read the footer */
230 SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, fs, start,
231 scratch_pool, scratch_pool));
232 SVN_ERR(svn_fs_fs__auto_read_footer(rev_file));
234 /* Verify the index contents against the checksum from the footer. */
235 SVN_ERR(verify_index_checksum(rev_file->file, "L2P index",
236 rev_file->l2p_offset, rev_file->p2l_offset,
237 rev_file->l2p_checksum,
238 cancel_func, cancel_baton, scratch_pool));
239 SVN_ERR(verify_index_checksum(rev_file->file, "P2L index",
240 rev_file->p2l_offset, rev_file->footer_offset,
241 rev_file->p2l_checksum,
242 cancel_func, cancel_baton, scratch_pool));
245 SVN_ERR(svn_fs_fs__close_revision_file(rev_file));
250 /* Verify that for all log-to-phys index entries for revisions START to
251 * START + COUNT-1 in FS there is a consistent entry in the phys-to-log
252 * index. If given, invoke CANCEL_FUNC with CANCEL_BATON at regular
253 * intervals. Use POOL for allocations.
256 compare_l2p_to_p2l_index(svn_fs_t *fs,
259 svn_cancel_func_t cancel_func,
264 apr_pool_t *iterpool = svn_pool_create(pool);
265 apr_array_header_t *max_ids;
267 /* common file access structure */
268 svn_fs_fs__revision_file_t *rev_file;
269 SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, fs, start, pool,
272 /* determine the range of items to check for each revision */
273 SVN_ERR(svn_fs_fs__l2p_get_max_ids(&max_ids, fs, start, count, pool,
276 /* check all items in all revisions if the given range */
277 for (i = 0; i < max_ids->nelts; ++i)
280 apr_uint64_t max_id = APR_ARRAY_IDX(max_ids, i, apr_uint64_t);
281 svn_revnum_t revision = start + i;
283 for (k = 0; k < max_id; ++k)
286 svn_fs_fs__p2l_entry_t *p2l_entry;
287 svn_pool_clear(iterpool);
289 /* get L2P entry. Ignore unused entries. */
290 SVN_ERR(svn_fs_fs__item_offset(&offset, fs, rev_file, revision,
295 /* find the corresponding P2L entry */
296 SVN_ERR(svn_fs_fs__p2l_entry_lookup(&p2l_entry, fs, rev_file,
297 revision, offset, iterpool,
300 if (p2l_entry == NULL)
301 return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT,
303 _("p2l index entry not found for "
304 "PHYS %s returned by "
305 "l2p index for LOG r%ld:i%ld"),
306 apr_off_t_toa(pool, offset),
309 if ( p2l_entry->item.number != k
310 || p2l_entry->item.revision != revision)
311 return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT,
313 _("p2l index info LOG r%ld:i%ld"
315 "l2p index for LOG r%ld:i%ld"),
316 p2l_entry->item.revision,
317 (long)p2l_entry->item.number,
322 SVN_ERR(cancel_func(cancel_baton));
325 svn_pool_destroy(iterpool);
327 SVN_ERR(svn_fs_fs__close_revision_file(rev_file));
332 /* Verify that for all phys-to-log index entries for revisions START to
333 * START + COUNT-1 in FS there is a consistent entry in the log-to-phys
334 * index. If given, invoke CANCEL_FUNC with CANCEL_BATON at regular
335 * intervals. Use POOL for allocations.
337 * Please note that we can only check on pack / rev file granularity and
338 * must only be called for a single rev / pack file.
341 compare_p2l_to_l2p_index(svn_fs_t *fs,
344 svn_cancel_func_t cancel_func,
348 fs_fs_data_t *ffd = fs->fsap_data;
349 apr_pool_t *iterpool = svn_pool_create(pool);
350 apr_off_t max_offset;
351 apr_off_t offset = 0;
353 /* common file access structure */
354 svn_fs_fs__revision_file_t *rev_file;
355 SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, fs, start, pool,
358 /* get the size of the rev / pack file as covered by the P2L index */
359 SVN_ERR(svn_fs_fs__p2l_get_max_offset(&max_offset, fs, rev_file, start,
362 /* for all offsets in the file, get the P2L index entries and check
363 them against the L2P index */
364 for (offset = 0; offset < max_offset; )
366 apr_array_header_t *entries;
367 svn_fs_fs__p2l_entry_t *last_entry;
370 svn_pool_clear(iterpool);
372 /* get all entries for the current block */
373 SVN_ERR(svn_fs_fs__p2l_index_lookup(&entries, fs, rev_file, start,
374 offset, ffd->p2l_page_size,
375 iterpool, iterpool));
376 if (entries->nelts == 0)
377 return svn_error_createf(SVN_ERR_FS_INDEX_CORRUPTION,
379 _("p2l does not cover offset %s"
380 " for revision %ld"),
381 apr_off_t_toa(pool, offset), start);
383 /* process all entries (and later continue with the next block) */
385 = &APR_ARRAY_IDX(entries, entries->nelts-1, svn_fs_fs__p2l_entry_t);
386 offset = last_entry->offset + last_entry->size;
388 for (i = 0; i < entries->nelts; ++i)
390 svn_fs_fs__p2l_entry_t *entry
391 = &APR_ARRAY_IDX(entries, i, svn_fs_fs__p2l_entry_t);
393 /* check all sub-items for consist entries in the L2P index */
394 if (entry->type == SVN_FS_FS__ITEM_TYPE_UNUSED)
396 /* There is no L2P entry for unused rev file sections.
397 * And its P2L index data is hardly ever used. But we
398 * should still check whether someone tempered with it. */
399 if ( entry->item.revision != SVN_INVALID_REVNUM
400 && ( entry->item.revision < start
401 || entry->item.revision >= start + count))
402 return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT,
404 _("Empty P2L entry for PHYS %s "
405 "refers to revision %ld outside "
406 "the rev / pack file (%ld-%ld)"),
407 apr_off_t_toa(pool, entry->offset),
408 entry->item.revision,
409 start, start + count - 1);
413 apr_off_t l2p_offset;
414 SVN_ERR(svn_fs_fs__item_offset(&l2p_offset, fs, rev_file,
415 entry->item.revision, NULL,
416 entry->item.number, iterpool));
418 if (l2p_offset != entry->offset)
419 return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT,
421 _("l2p index entry PHYS %s"
422 "does not match p2l index value "
423 "LOG r%ld:i%ld for PHYS %s"),
424 apr_off_t_toa(pool, l2p_offset),
425 entry->item.revision,
426 (long)entry->item.number,
427 apr_off_t_toa(pool, entry->offset));
432 SVN_ERR(cancel_func(cancel_baton));
435 svn_pool_destroy(iterpool);
437 SVN_ERR(svn_fs_fs__close_revision_file(rev_file));
442 /* Items smaller than this can be read at once into a buffer and directly
443 * be checksummed. Larger items require stream processing.
444 * Must be a multiple of 8. */
445 #define STREAM_THRESHOLD 4096
447 /* Verify that the next SIZE bytes read from FILE are NUL.
448 * SIZE must not exceed STREAM_THRESHOLD. Use POOL for allocations.
451 expect_buffer_nul(apr_file_t *file,
457 unsigned char buffer[STREAM_THRESHOLD];
458 apr_uint64_t chunks[STREAM_THRESHOLD / sizeof(apr_uint64_t)];
462 SVN_ERR_ASSERT(size <= STREAM_THRESHOLD);
464 /* read the whole data block; error out on failure */
465 data.chunks[(size - 1)/ sizeof(apr_uint64_t)] = 0;
466 SVN_ERR(svn_io_file_read_full2(file, data.buffer, size, NULL, NULL, pool));
469 for (i = 0; i < size / sizeof(apr_uint64_t); ++i)
470 if (data.chunks[i] != 0)
473 /* byte-wise check upon mismatch or at the end of the block */
474 for (i *= sizeof(apr_uint64_t); i < size; ++i)
475 if (data.buffer[i] != 0)
477 const char *file_name;
480 SVN_ERR(svn_io_file_name_get(&file_name, file, pool));
481 SVN_ERR(svn_fs_fs__get_file_offset(&offset, file, pool));
484 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
485 _("Empty section in file %s contains "
486 "non-NUL data at offset %s"),
487 file_name, apr_off_t_toa(pool, offset));
493 /* Verify that the next SIZE bytes read from FILE are NUL.
494 * Use POOL for allocations.
497 read_all_nul(apr_file_t *file,
501 for (; size >= STREAM_THRESHOLD; size -= STREAM_THRESHOLD)
502 SVN_ERR(expect_buffer_nul(file, STREAM_THRESHOLD, pool));
505 SVN_ERR(expect_buffer_nul(file, size, pool));
510 /* Compare the ACTUAL checksum with the one expected by ENTRY.
511 * Return an error in case of mismatch. Use the name of FILE
512 * in error message. Allocate data in POOL.
515 expected_checksum(apr_file_t *file,
516 svn_fs_fs__p2l_entry_t *entry,
520 if (actual != entry->fnv1_checksum)
522 const char *file_name;
524 SVN_ERR(svn_io_file_name_get(&file_name, file, pool));
525 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
526 _("Checksum mismatch in item at offset %s of "
527 "length %s bytes in file %s"),
528 apr_off_t_toa(pool, entry->offset),
529 apr_off_t_toa(pool, entry->size), file_name);
535 /* Verify that the FNV checksum over the next ENTRY->SIZE bytes read
536 * from FILE will match ENTRY's expected checksum. SIZE must not
537 * exceed STREAM_THRESHOLD. Use POOL for allocations.
540 expected_buffered_checksum(apr_file_t *file,
541 svn_fs_fs__p2l_entry_t *entry,
544 unsigned char buffer[STREAM_THRESHOLD];
545 SVN_ERR_ASSERT(entry->size <= STREAM_THRESHOLD);
547 SVN_ERR(svn_io_file_read_full2(file, buffer, (apr_size_t)entry->size,
549 SVN_ERR(expected_checksum(file, entry,
550 svn__fnv1a_32x4(buffer, (apr_size_t)entry->size),
556 /* Verify that the FNV checksum over the next ENTRY->SIZE bytes read from
557 * FILE will match ENTRY's expected checksum. Use POOL for allocations.
560 expected_streamed_checksum(apr_file_t *file,
561 svn_fs_fs__p2l_entry_t *entry,
564 unsigned char buffer[STREAM_THRESHOLD];
565 svn_checksum_t *checksum;
566 svn_checksum_ctx_t *context
567 = svn_checksum_ctx_create(svn_checksum_fnv1a_32x4, pool);
568 apr_off_t size = entry->size;
572 apr_size_t to_read = size > sizeof(buffer)
575 SVN_ERR(svn_io_file_read_full2(file, buffer, to_read, NULL, NULL,
577 SVN_ERR(svn_checksum_update(context, buffer, to_read));
581 SVN_ERR(svn_checksum_final(&checksum, context, pool));
582 SVN_ERR(expected_checksum(file, entry,
583 ntohl(*(const apr_uint32_t *)checksum->digest),
589 /* Verify that for all phys-to-log index entries for revisions START to
590 * START + COUNT-1 in FS match the actual pack / rev file contents.
591 * If given, invoke CANCEL_FUNC with CANCEL_BATON at regular intervals.
592 * Use POOL for allocations.
594 * Please note that we can only check on pack / rev file granularity and
595 * must only be called for a single rev / pack file.
598 compare_p2l_to_rev(svn_fs_t *fs,
601 svn_cancel_func_t cancel_func,
605 fs_fs_data_t *ffd = fs->fsap_data;
606 apr_pool_t *iterpool = svn_pool_create(pool);
607 apr_off_t max_offset;
608 apr_off_t offset = 0;
609 svn_fs_fs__revision_file_t *rev_file;
611 /* open the pack / rev file that is covered by the p2l index */
612 SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, fs, start, pool,
615 /* check file size vs. range covered by index */
616 SVN_ERR(svn_fs_fs__auto_read_footer(rev_file));
617 SVN_ERR(svn_fs_fs__p2l_get_max_offset(&max_offset, fs, rev_file, start,
620 if (rev_file->l2p_offset != max_offset)
621 return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT, NULL,
622 _("File size of %s for revision r%ld does "
623 "not match p2l index size of %s"),
624 apr_off_t_toa(pool, rev_file->l2p_offset), start,
625 apr_off_t_toa(pool, max_offset));
627 SVN_ERR(svn_io_file_aligned_seek(rev_file->file, ffd->block_size, NULL, 0,
630 /* for all offsets in the file, get the P2L index entries and check
631 them against the L2P index */
632 for (offset = 0; offset < max_offset; )
634 apr_array_header_t *entries;
637 svn_pool_clear(iterpool);
639 /* get all entries for the current block */
640 SVN_ERR(svn_fs_fs__p2l_index_lookup(&entries, fs, rev_file, start,
641 offset, ffd->p2l_page_size,
642 iterpool, iterpool));
644 /* The above might have moved the file pointer.
645 * Ensure we actually start reading at OFFSET. */
646 SVN_ERR(svn_io_file_aligned_seek(rev_file->file, ffd->block_size,
647 NULL, offset, iterpool));
649 /* process all entries (and later continue with the next block) */
650 for (i = 0; i < entries->nelts; ++i)
652 svn_fs_fs__p2l_entry_t *entry
653 = &APR_ARRAY_IDX(entries, i, svn_fs_fs__p2l_entry_t);
655 /* skip bits we previously checked */
656 if (i == 0 && entry->offset < offset)
659 /* skip zero-sized entries */
660 if (entry->size == 0)
663 /* p2l index must cover all rev / pack file offsets exactly once */
664 if (entry->offset != offset)
665 return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT,
667 _("p2l index entry for revision r%ld"
668 " is non-contiguous between offsets "
671 apr_off_t_toa(pool, offset),
672 apr_off_t_toa(pool, entry->offset));
674 /* empty sections must contain NUL bytes only */
675 if (entry->type == SVN_FS_FS__ITEM_TYPE_UNUSED)
677 /* skip filler entry at the end of the p2l index */
678 if (entry->offset != max_offset)
679 SVN_ERR(read_all_nul(rev_file->file, entry->size, pool));
683 if (entry->size < STREAM_THRESHOLD)
684 SVN_ERR(expected_buffered_checksum(rev_file->file, entry,
687 SVN_ERR(expected_streamed_checksum(rev_file->file, entry,
692 offset += entry->size;
696 SVN_ERR(cancel_func(cancel_baton));
699 svn_pool_destroy(iterpool);
701 SVN_ERR(svn_fs_fs__close_revision_file(rev_file));
706 /* Verify that the revprops of the revisions START to END in FS can be
707 * accessed. Invoke CANCEL_FUNC with CANCEL_BATON at regular intervals.
709 * The values of START and END have already been auto-selected and
713 verify_revprops(svn_fs_t *fs,
716 svn_cancel_func_t cancel_func,
720 svn_revnum_t revision;
721 apr_pool_t *iterpool = svn_pool_create(pool);
723 for (revision = start; revision < end; ++revision)
728 svn_pool_clear(iterpool);
730 /* Access the svn:date revprop.
731 * This implies parsing all revprops for that revision. */
732 SVN_ERR(svn_fs_fs__revision_prop(&date, fs, revision,
733 SVN_PROP_REVISION_DATE, iterpool));
735 /* The time stamp is the only revprop that, if given, needs to
736 * have a valid content. */
738 SVN_ERR(svn_time_from_cstring(&timetemp, date->data, iterpool));
741 SVN_ERR(cancel_func(cancel_baton));
744 svn_pool_destroy(iterpool);
750 pack_size(svn_fs_t *fs, svn_revnum_t rev)
752 fs_fs_data_t *ffd = fs->fsap_data;
754 return rev < ffd->min_unpacked_rev ? ffd->max_files_per_dir : 1;
757 /* Verify that on-disk representation has not been tempered with (in a way
758 * that leaves the repository in a corrupted state). This compares log-to-
759 * phys with phys-to-log indexes, verifies the low-level checksums and
760 * checks that all revprops are available. The function signature is
761 * similar to svn_fs_fs__verify.
763 * The values of START and END have already been auto-selected and
764 * verified. You may call this for format7 or higher repos.
767 verify_f7_metadata_consistency(svn_fs_t *fs,
770 svn_fs_progress_notify_func_t notify_func,
772 svn_cancel_func_t cancel_func,
776 fs_fs_data_t *ffd = fs->fsap_data;
777 svn_revnum_t revision, next_revision;
778 apr_pool_t *iterpool = svn_pool_create(pool);
780 for (revision = start; revision <= end; revision = next_revision)
782 svn_error_t *err = SVN_NO_ERROR;
784 svn_revnum_t count = pack_size(fs, revision);
785 svn_revnum_t pack_start = svn_fs_fs__packed_base_rev(fs, revision);
786 svn_revnum_t pack_end = pack_start + count;
788 svn_pool_clear(iterpool);
790 if (notify_func && (pack_start % ffd->max_files_per_dir == 0))
791 notify_func(pack_start, notify_baton, iterpool);
793 /* Check for external corruption to the indexes. */
794 err = verify_index_checksums(fs, pack_start, cancel_func,
795 cancel_baton, iterpool);
797 /* two-way index check */
799 err = compare_l2p_to_p2l_index(fs, pack_start, pack_end - pack_start,
800 cancel_func, cancel_baton, iterpool);
802 err = compare_p2l_to_l2p_index(fs, pack_start, pack_end - pack_start,
803 cancel_func, cancel_baton, iterpool);
805 /* verify in-index checksums and types vs. actual rev / pack files */
807 err = compare_p2l_to_rev(fs, pack_start, pack_end - pack_start,
808 cancel_func, cancel_baton, iterpool);
810 /* ensure that revprops are available and accessible */
812 err = verify_revprops(fs, pack_start, pack_end,
813 cancel_func, cancel_baton, iterpool);
815 /* concurrent packing is one of the reasons why verification may fail.
816 Make sure, we operate on up-to-date information. */
820 = svn_fs_fs__read_min_unpacked_rev(&ffd->min_unpacked_rev,
823 /* Be careful to not leak ERR. */
825 return svn_error_trace(svn_error_compose_create(err, err2));
828 /* retry the whole shard if it got packed in the meantime */
829 if (err && count != pack_size(fs, revision))
831 svn_error_clear(err);
833 /* We could simply assign revision here but the code below is
834 more intuitive to maintainers. */
835 next_revision = svn_fs_fs__packed_base_rev(fs, revision);
840 next_revision = pack_end;
844 svn_pool_destroy(iterpool);
850 svn_fs_fs__verify(svn_fs_t *fs,
853 svn_fs_progress_notify_func_t notify_func,
855 svn_cancel_func_t cancel_func,
859 fs_fs_data_t *ffd = fs->fsap_data;
860 svn_revnum_t youngest = ffd->youngest_rev_cache; /* cache is current */
862 /* Input validation. */
863 if (! SVN_IS_VALID_REVNUM(start))
865 if (! SVN_IS_VALID_REVNUM(end))
867 SVN_ERR(svn_fs_fs__ensure_revision_exists(start, fs, pool));
868 SVN_ERR(svn_fs_fs__ensure_revision_exists(end, fs, pool));
870 /* log/phys index consistency. We need to check them first to make
871 sure we can access the rev / pack files in format7. */
872 if (svn_fs_fs__use_log_addressing(fs))
873 SVN_ERR(verify_f7_metadata_consistency(fs, start, end,
874 notify_func, notify_baton,
875 cancel_func, cancel_baton, pool));
877 /* rep cache consistency */
878 if (ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT)
879 SVN_ERR(verify_rep_cache(fs, start, end, notify_func, notify_baton,
880 cancel_func, cancel_baton, pool));