1 /* verify.c --- verification of FSX 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 * ====================================================================
26 #include "private/svn_subr_private.h"
28 #include "cached_data.h"
29 #include "rep-cache.h"
33 #include "../libsvn_fs/fs-loader.h"
35 #include "svn_private_config.h"
40 /* Baton type expected by verify_walker(). The purpose is to limit the
41 * number of notifications sent.
43 typedef struct verify_walker_baton_t
45 /* number of calls to verify_walker() since the last clean */
48 /* progress notification callback to invoke periodically (may be NULL) */
49 svn_fs_progress_notify_func_t notify_func;
51 /* baton to use with NOTIFY_FUNC */
54 /* remember the last revision for which we called notify_func */
55 svn_revnum_t last_notified_revision;
56 } verify_walker_baton_t;
58 /* Used by svn_fs_x__verify().
59 Implements svn_fs_x__walk_rep_reference().walker. */
61 verify_walker(svn_fs_x__representation_t *rep,
64 apr_pool_t *scratch_pool)
66 verify_walker_baton_t *walker_baton = baton;
68 /* notify and free resources periodically */
69 if (walker_baton->iteration_count > 1000)
71 svn_revnum_t revision = svn_fs_x__get_revnum(rep->id.change_set);
72 if ( walker_baton->notify_func
73 && revision != walker_baton->last_notified_revision)
75 walker_baton->notify_func(revision,
76 walker_baton->notify_baton,
78 walker_baton->last_notified_revision = revision;
81 walker_baton->iteration_count = 0;
84 /* access the repo data */
85 SVN_ERR(svn_fs_x__check_rep(rep, fs, scratch_pool));
87 /* update resource usage counters */
88 walker_baton->iteration_count++;
93 /* Verify the rep cache DB's consistency with our rev / pack data.
94 * The function signature is similar to svn_fs_x__verify.
95 * The values of START and END have already been auto-selected and
99 verify_rep_cache(svn_fs_t *fs,
102 svn_fs_progress_notify_func_t notify_func,
104 svn_cancel_func_t cancel_func,
106 apr_pool_t *scratch_pool)
108 svn_boolean_t exists;
110 /* rep-cache verification. */
111 SVN_ERR(svn_fs_x__exists_rep_cache(&exists, fs, scratch_pool));
114 /* provide a baton to allow the reuse of open file handles between
115 iterations (saves 2/3 of OS level file operations). */
116 verify_walker_baton_t *baton
117 = apr_pcalloc(scratch_pool, sizeof(*baton));
119 baton->last_notified_revision = SVN_INVALID_REVNUM;
120 baton->notify_func = notify_func;
121 baton->notify_baton = notify_baton;
123 /* tell the user that we are now ready to do *something* */
125 notify_func(SVN_INVALID_REVNUM, notify_baton, scratch_pool);
127 /* Do not attempt to walk the rep-cache database if its file does
128 not exist, since doing so would create it --- which may confuse
129 the administrator. Don't take any lock. */
130 SVN_ERR(svn_fs_x__walk_rep_reference(fs, start, end,
131 verify_walker, baton,
132 cancel_func, cancel_baton,
139 /* Verify that the MD5 checksum of the data between offsets START and END
140 * in FILE matches the EXPECTED checksum. If there is a mismatch use the
141 * indedx NAME in the error message. Supports cancellation with CANCEL_FUNC
142 * and CANCEL_BATON. SCRATCH_POOL is for temporary allocations. */
144 verify_index_checksum(apr_file_t *file,
148 svn_checksum_t *expected,
149 svn_cancel_func_t cancel_func,
151 apr_pool_t *scratch_pool)
153 unsigned char buffer[SVN__STREAM_CHUNK_SIZE];
154 apr_off_t size = end - start;
155 svn_checksum_t *actual;
156 svn_checksum_ctx_t *context
157 = svn_checksum_ctx_create(svn_checksum_md5, scratch_pool);
159 /* Calculate the index checksum. */
160 SVN_ERR(svn_io_file_seek(file, APR_SET, &start, scratch_pool));
163 apr_size_t to_read = size > sizeof(buffer)
166 SVN_ERR(svn_io_file_read_full2(file, buffer, to_read, NULL, NULL,
168 SVN_ERR(svn_checksum_update(context, buffer, to_read));
172 SVN_ERR(cancel_func(cancel_baton));
175 SVN_ERR(svn_checksum_final(&actual, context, scratch_pool));
177 /* Verify that it matches the expected checksum. */
178 if (!svn_checksum_match(expected, actual))
180 const char *file_name;
182 SVN_ERR(svn_io_file_name_get(&file_name, file, scratch_pool));
183 SVN_ERR(svn_checksum_mismatch_err(expected, actual, scratch_pool,
184 _("%s checksum mismatch in file %s"),
191 /* Verify the MD5 checksums of the index data in the rev / pack file
192 * containing revision START in FS. If given, invoke CANCEL_FUNC with
193 * CANCEL_BATON at regular intervals. Use SCRATCH_POOL for temporary
197 verify_index_checksums(svn_fs_t *fs,
199 svn_cancel_func_t cancel_func,
201 apr_pool_t *scratch_pool)
203 svn_fs_x__revision_file_t *rev_file;
205 /* Open the rev / pack file and read the footer */
206 SVN_ERR(svn_fs_x__open_pack_or_rev_file(&rev_file, fs, start,
207 scratch_pool, scratch_pool));
208 SVN_ERR(svn_fs_x__auto_read_footer(rev_file));
210 /* Verify the index contents against the checksum from the footer. */
211 SVN_ERR(verify_index_checksum(rev_file->file, "L2P index",
212 rev_file->l2p_offset, rev_file->p2l_offset,
213 rev_file->l2p_checksum,
214 cancel_func, cancel_baton, scratch_pool));
215 SVN_ERR(verify_index_checksum(rev_file->file, "P2L index",
216 rev_file->p2l_offset, rev_file->footer_offset,
217 rev_file->p2l_checksum,
218 cancel_func, cancel_baton, scratch_pool));
221 SVN_ERR(svn_fs_x__close_revision_file(rev_file));
226 /* Verify that for all log-to-phys index entries for revisions START to
227 * START + COUNT-1 in FS there is a consistent entry in the phys-to-log
228 * index. If given, invoke CANCEL_FUNC with CANCEL_BATON at regular
229 * intervals. Use SCRATCH_POOL for temporary allocations.
232 compare_l2p_to_p2l_index(svn_fs_t *fs,
235 svn_cancel_func_t cancel_func,
237 apr_pool_t *scratch_pool)
240 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
241 apr_array_header_t *max_ids;
243 /* common file access structure */
244 svn_fs_x__revision_file_t *rev_file;
245 SVN_ERR(svn_fs_x__open_pack_or_rev_file(&rev_file, fs, start, scratch_pool,
248 /* determine the range of items to check for each revision */
249 SVN_ERR(svn_fs_x__l2p_get_max_ids(&max_ids, fs, start, count, scratch_pool,
252 /* check all items in all revisions if the given range */
253 for (i = 0; i < max_ids->nelts; ++i)
256 apr_uint64_t max_id = APR_ARRAY_IDX(max_ids, i, apr_uint64_t);
257 svn_revnum_t revision = start + i;
259 for (k = 0; k < max_id; ++k)
262 apr_uint32_t sub_item;
263 svn_fs_x__id_t l2p_item;
264 svn_fs_x__id_t *p2l_item;
266 l2p_item.change_set = svn_fs_x__change_set_by_rev(revision);
269 /* get L2P entry. Ignore unused entries. */
270 SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, rev_file,
271 &l2p_item, iterpool));
275 /* find the corresponding P2L entry */
276 SVN_ERR(svn_fs_x__p2l_item_lookup(&p2l_item, fs, rev_file,
277 revision, offset, sub_item,
278 iterpool, iterpool));
280 if (p2l_item == NULL)
281 return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT,
283 _("p2l index entry not found for "
284 "PHYS o%s:s%ld returned by "
285 "l2p index for LOG r%ld:i%ld"),
286 apr_off_t_toa(scratch_pool, offset),
287 (long)sub_item, revision, (long)k);
289 if (!svn_fs_x__id_eq(&l2p_item, p2l_item))
290 return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT,
292 _("p2l index info LOG r%ld:i%ld"
294 "l2p index for LOG r%ld:i%ld"),
295 svn_fs_x__get_revnum(p2l_item->change_set),
296 (long)p2l_item->number, revision,
299 svn_pool_clear(iterpool);
303 SVN_ERR(cancel_func(cancel_baton));
306 svn_pool_destroy(iterpool);
308 SVN_ERR(svn_fs_x__close_revision_file(rev_file));
313 /* Verify that for all phys-to-log index entries for revisions START to
314 * START + COUNT-1 in FS there is a consistent entry in the log-to-phys
315 * index. If given, invoke CANCEL_FUNC with CANCEL_BATON at regular
316 * intervals. Use SCRATCH_POOL for temporary allocations.
318 * Please note that we can only check on pack / rev file granularity and
319 * must only be called for a single rev / pack file.
322 compare_p2l_to_l2p_index(svn_fs_t *fs,
325 svn_cancel_func_t cancel_func,
327 apr_pool_t *scratch_pool)
329 svn_fs_x__data_t *ffd = fs->fsap_data;
330 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
331 apr_pool_t *iterpool2 = svn_pool_create(scratch_pool);
332 apr_off_t max_offset;
333 apr_off_t offset = 0;
335 /* common file access structure */
336 svn_fs_x__revision_file_t *rev_file;
337 SVN_ERR(svn_fs_x__open_pack_or_rev_file(&rev_file, fs, start, scratch_pool,
340 /* get the size of the rev / pack file as covered by the P2L index */
341 SVN_ERR(svn_fs_x__p2l_get_max_offset(&max_offset, fs, rev_file, start,
344 /* for all offsets in the file, get the P2L index entries and check
345 them against the L2P index */
346 for (offset = 0; offset < max_offset; )
348 apr_array_header_t *entries;
349 svn_fs_x__p2l_entry_t *last_entry;
352 svn_pool_clear(iterpool);
354 /* get all entries for the current block */
355 SVN_ERR(svn_fs_x__p2l_index_lookup(&entries, fs, rev_file, start,
356 offset, ffd->p2l_page_size,
357 iterpool, iterpool));
358 if (entries->nelts == 0)
359 return svn_error_createf(SVN_ERR_FS_INDEX_CORRUPTION,
361 _("p2l does not cover offset %s"
362 " for revision %ld"),
363 apr_off_t_toa(scratch_pool, offset), start);
365 /* process all entries (and later continue with the next block) */
367 = &APR_ARRAY_IDX(entries, entries->nelts-1, svn_fs_x__p2l_entry_t);
368 offset = last_entry->offset + last_entry->size;
370 for (i = 0; i < entries->nelts; ++i)
373 svn_fs_x__p2l_entry_t *entry
374 = &APR_ARRAY_IDX(entries, i, svn_fs_x__p2l_entry_t);
376 /* check all sub-items for consist entries in the L2P index */
377 for (k = 0; k < entry->item_count; ++k)
379 apr_off_t l2p_offset;
380 apr_uint32_t sub_item;
381 svn_fs_x__id_t *p2l_item = &entry->items[k];
382 svn_revnum_t revision
383 = svn_fs_x__get_revnum(p2l_item->change_set);
385 svn_pool_clear(iterpool2);
386 SVN_ERR(svn_fs_x__item_offset(&l2p_offset, &sub_item, fs,
387 rev_file, p2l_item, iterpool2));
389 if (sub_item != k || l2p_offset != entry->offset)
390 return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT,
392 _("l2p index entry PHYS o%s:s%ld "
393 "does not match p2l index value "
394 "LOG r%ld:i%ld for PHYS o%s:s%ld"),
395 apr_off_t_toa(scratch_pool,
399 (long)p2l_item->number,
400 apr_off_t_toa(scratch_pool,
407 SVN_ERR(cancel_func(cancel_baton));
410 svn_pool_destroy(iterpool2);
411 svn_pool_destroy(iterpool);
413 SVN_ERR(svn_fs_x__close_revision_file(rev_file));
418 /* Items smaller than this can be read at once into a buffer and directly
419 * be checksummed. Larger items require stream processing.
420 * Must be a multiple of 8. */
421 #define STREAM_THRESHOLD 4096
423 /* Verify that the next SIZE bytes read from FILE are NUL. SIZE must not
424 * exceed STREAM_THRESHOLD. Use SCRATCH_POOL for temporary allocations.
427 expect_buffer_nul(apr_file_t *file,
429 apr_pool_t *scratch_pool)
433 unsigned char buffer[STREAM_THRESHOLD];
434 apr_uint64_t chunks[STREAM_THRESHOLD / sizeof(apr_uint64_t)];
438 SVN_ERR_ASSERT(size <= STREAM_THRESHOLD);
440 /* read the whole data block; error out on failure */
441 data.chunks[(size - 1)/ sizeof(apr_uint64_t)] = 0;
442 SVN_ERR(svn_io_file_read_full2(file, data.buffer, size, NULL, NULL,
446 for (i = 0; i < size / sizeof(apr_uint64_t); ++i)
447 if (data.chunks[i] != 0)
450 /* byte-wise check upon mismatch or at the end of the block */
451 for (i *= sizeof(apr_uint64_t); i < size; ++i)
452 if (data.buffer[i] != 0)
454 const char *file_name;
457 SVN_ERR(svn_io_file_name_get(&file_name, file, scratch_pool));
458 SVN_ERR(svn_fs_x__get_file_offset(&offset, file, scratch_pool));
461 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
462 _("Empty section in file %s contains "
463 "non-NUL data at offset %s"),
465 apr_off_t_toa(scratch_pool, offset));
471 /* Verify that the next SIZE bytes read from FILE are NUL.
472 * Use SCRATCH_POOL for temporary allocations.
475 read_all_nul(apr_file_t *file,
477 apr_pool_t *scratch_pool)
479 for (; size >= STREAM_THRESHOLD; size -= STREAM_THRESHOLD)
480 SVN_ERR(expect_buffer_nul(file, STREAM_THRESHOLD, scratch_pool));
483 SVN_ERR(expect_buffer_nul(file, size, scratch_pool));
488 /* Compare the ACTUAL checksum with the one expected by ENTRY.
489 * Return an error in case of mismatch. Use the name of FILE
490 * in error message. Allocate temporary data in SCRATCH_POOL.
493 expected_checksum(apr_file_t *file,
494 svn_fs_x__p2l_entry_t *entry,
496 apr_pool_t *scratch_pool)
498 if (actual != entry->fnv1_checksum)
500 const char *file_name;
502 SVN_ERR(svn_io_file_name_get(&file_name, file, scratch_pool));
503 SVN_ERR(svn_io_file_name_get(&file_name, file, scratch_pool));
504 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
505 _("Checksum mismatch in item at offset %s of "
506 "length %s bytes in file %s"),
507 apr_off_t_toa(scratch_pool, entry->offset),
508 apr_off_t_toa(scratch_pool, entry->size),
515 /* Verify that the FNV checksum over the next ENTRY->SIZE bytes read
516 * from FILE will match ENTRY's expected checksum. SIZE must not
517 * exceed STREAM_THRESHOLD. Use SCRATCH_POOL for temporary allocations.
520 expected_buffered_checksum(apr_file_t *file,
521 svn_fs_x__p2l_entry_t *entry,
522 apr_pool_t *scratch_pool)
524 unsigned char buffer[STREAM_THRESHOLD];
525 SVN_ERR_ASSERT(entry->size <= STREAM_THRESHOLD);
527 SVN_ERR(svn_io_file_read_full2(file, buffer, (apr_size_t)entry->size,
528 NULL, NULL, scratch_pool));
529 SVN_ERR(expected_checksum(file, entry,
530 svn__fnv1a_32x4(buffer, (apr_size_t)entry->size),
536 /* Verify that the FNV checksum over the next ENTRY->SIZE bytes read from
537 * FILE will match ENTRY's expected checksum.
538 * Use SCRATCH_POOL for temporary allocations.
541 expected_streamed_checksum(apr_file_t *file,
542 svn_fs_x__p2l_entry_t *entry,
543 apr_pool_t *scratch_pool)
545 unsigned char buffer[STREAM_THRESHOLD];
546 svn_checksum_t *checksum;
547 svn_checksum_ctx_t *context
548 = svn_checksum_ctx_create(svn_checksum_fnv1a_32x4, scratch_pool);
549 apr_off_t size = entry->size;
553 apr_size_t to_read = size > sizeof(buffer)
556 SVN_ERR(svn_io_file_read_full2(file, buffer, to_read, NULL, NULL,
558 SVN_ERR(svn_checksum_update(context, buffer, to_read));
562 SVN_ERR(svn_checksum_final(&checksum, context, scratch_pool));
563 SVN_ERR(expected_checksum(file, entry,
564 ntohl(*(const apr_uint32_t *)checksum->digest),
570 /* Verify that for all phys-to-log index entries for revisions START to
571 * START + COUNT-1 in FS match the actual pack / rev file contents.
572 * If given, invoke CANCEL_FUNC with CANCEL_BATON at regular intervals.
573 * Use SCRATCH_POOL for temporary allocations.
575 * Please note that we can only check on pack / rev file granularity and
576 * must only be called for a single rev / pack file.
579 compare_p2l_to_rev(svn_fs_t *fs,
582 svn_cancel_func_t cancel_func,
584 apr_pool_t *scratch_pool)
586 svn_fs_x__data_t *ffd = fs->fsap_data;
587 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
588 apr_off_t max_offset;
589 apr_off_t offset = 0;
590 svn_fs_x__revision_file_t *rev_file;
592 /* open the pack / rev file that is covered by the p2l index */
593 SVN_ERR(svn_fs_x__open_pack_or_rev_file(&rev_file, fs, start, scratch_pool,
596 /* check file size vs. range covered by index */
597 SVN_ERR(svn_fs_x__auto_read_footer(rev_file));
598 SVN_ERR(svn_fs_x__p2l_get_max_offset(&max_offset, fs, rev_file, start,
601 if (rev_file->l2p_offset != max_offset)
602 return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT, NULL,
603 _("File size of %s for revision r%ld does "
604 "not match p2l index size of %s"),
605 apr_off_t_toa(scratch_pool,
606 rev_file->l2p_offset),
608 apr_off_t_toa(scratch_pool,
611 SVN_ERR(svn_io_file_aligned_seek(rev_file->file, ffd->block_size, NULL, 0,
614 /* for all offsets in the file, get the P2L index entries and check
615 them against the L2P index */
616 for (offset = 0; offset < max_offset; )
618 apr_array_header_t *entries;
621 svn_pool_clear(iterpool);
623 /* get all entries for the current block */
624 SVN_ERR(svn_fs_x__p2l_index_lookup(&entries, fs, rev_file, start,
625 offset, ffd->p2l_page_size,
626 iterpool, iterpool));
628 /* The above might have moved the file pointer.
629 * Ensure we actually start reading at OFFSET. */
630 SVN_ERR(svn_io_file_aligned_seek(rev_file->file, ffd->block_size,
631 NULL, offset, iterpool));
633 /* process all entries (and later continue with the next block) */
634 for (i = 0; i < entries->nelts; ++i)
636 svn_fs_x__p2l_entry_t *entry
637 = &APR_ARRAY_IDX(entries, i, svn_fs_x__p2l_entry_t);
639 /* skip bits we previously checked */
640 if (i == 0 && entry->offset < offset)
643 /* skip zero-sized entries */
644 if (entry->size == 0)
647 /* p2l index must cover all rev / pack file offsets exactly once */
648 if (entry->offset != offset)
649 return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT,
651 _("p2l index entry for revision r%ld"
652 " is non-contiguous between offsets "
655 apr_off_t_toa(scratch_pool, offset),
656 apr_off_t_toa(scratch_pool,
659 /* empty sections must contain NUL bytes only */
660 if (entry->type == SVN_FS_X__ITEM_TYPE_UNUSED)
662 /* skip filler entry at the end of the p2l index */
663 if (entry->offset != max_offset)
664 SVN_ERR(read_all_nul(rev_file->file, entry->size, iterpool));
668 if (entry->size < STREAM_THRESHOLD)
669 SVN_ERR(expected_buffered_checksum(rev_file->file, entry,
672 SVN_ERR(expected_streamed_checksum(rev_file->file, entry,
677 offset += entry->size;
681 SVN_ERR(cancel_func(cancel_baton));
684 svn_pool_destroy(iterpool);
689 /* Verify that the revprops of the revisions START to END in FS can be
690 * accessed. Invoke CANCEL_FUNC with CANCEL_BATON at regular intervals.
692 * The values of START and END have already been auto-selected and
696 verify_revprops(svn_fs_t *fs,
699 svn_cancel_func_t cancel_func,
701 apr_pool_t *scratch_pool)
703 svn_revnum_t revision;
704 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
706 for (revision = start; revision < end; ++revision)
711 svn_pool_clear(iterpool);
713 /* Access the svn:date revprop.
714 * This implies parsing all revprops for that revision. */
715 SVN_ERR(svn_fs_x__revision_prop(&date, fs, revision,
716 SVN_PROP_REVISION_DATE,
717 iterpool, iterpool));
719 /* The time stamp is the only revprop that, if given, needs to
720 * have a valid content. */
722 SVN_ERR(svn_time_from_cstring(&timetemp, date->data, iterpool));
725 SVN_ERR(cancel_func(cancel_baton));
728 svn_pool_destroy(iterpool);
733 /* Verify that on-disk representation has not been tempered with (in a way
734 * that leaves the repository in a corrupted state). This compares log-to-
735 * phys with phys-to-log indexes, verifies the low-level checksums and
736 * checks that all revprops are available. The function signature is
737 * similar to svn_fs_x__verify.
739 * The values of START and END have already been auto-selected and
743 verify_metadata_consistency(svn_fs_t *fs,
746 svn_fs_progress_notify_func_t notify_func,
748 svn_cancel_func_t cancel_func,
750 apr_pool_t *scratch_pool)
753 svn_fs_x__data_t *ffd = fs->fsap_data;
754 svn_revnum_t revision, next_revision;
755 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
757 for (revision = start; revision <= end; revision = next_revision)
759 svn_revnum_t count = svn_fs_x__packed_base_rev(fs, revision);
760 svn_revnum_t pack_start = count;
761 svn_revnum_t pack_end = pack_start + svn_fs_x__pack_size(fs, revision);
763 svn_pool_clear(iterpool);
765 if (notify_func && (pack_start % ffd->max_files_per_dir == 0))
766 notify_func(pack_start, notify_baton, iterpool);
768 /* Check for external corruption to the indexes. */
769 err = verify_index_checksums(fs, pack_start, cancel_func,
770 cancel_baton, iterpool);
772 /* two-way index check */
774 err = compare_l2p_to_p2l_index(fs, pack_start, pack_end - pack_start,
775 cancel_func, cancel_baton, iterpool);
777 err = compare_p2l_to_l2p_index(fs, pack_start, pack_end - pack_start,
778 cancel_func, cancel_baton, iterpool);
780 /* verify in-index checksums and types vs. actual rev / pack files */
782 err = compare_p2l_to_rev(fs, pack_start, pack_end - pack_start,
783 cancel_func, cancel_baton, iterpool);
785 /* ensure that revprops are available and accessible */
787 err = verify_revprops(fs, pack_start, pack_end,
788 cancel_func, cancel_baton, iterpool);
790 /* concurrent packing is one of the reasons why verification may fail.
791 Make sure, we operate on up-to-date information. */
793 SVN_ERR(svn_fs_x__read_min_unpacked_rev(&ffd->min_unpacked_rev,
796 /* retry the whole shard if it got packed in the meantime */
797 if (err && count != svn_fs_x__pack_size(fs, revision))
799 svn_error_clear(err);
801 /* We could simply assign revision here but the code below is
802 more intuitive to maintainers. */
803 next_revision = svn_fs_x__packed_base_rev(fs, revision);
808 next_revision = pack_end;
812 svn_pool_destroy(iterpool);
818 svn_fs_x__verify(svn_fs_t *fs,
821 svn_fs_progress_notify_func_t notify_func,
823 svn_cancel_func_t cancel_func,
825 apr_pool_t *scratch_pool)
827 svn_fs_x__data_t *ffd = fs->fsap_data;
828 svn_revnum_t youngest = ffd->youngest_rev_cache; /* cache is current */
830 /* Input validation. */
831 if (! SVN_IS_VALID_REVNUM(start))
833 if (! SVN_IS_VALID_REVNUM(end))
835 SVN_ERR(svn_fs_x__ensure_revision_exists(start, fs, scratch_pool));
836 SVN_ERR(svn_fs_x__ensure_revision_exists(end, fs, scratch_pool));
838 /* log/phys index consistency. We need to check them first to make
839 sure we can access the rev / pack files in format7. */
840 SVN_ERR(verify_metadata_consistency(fs, start, end,
841 notify_func, notify_baton,
842 cancel_func, cancel_baton,
845 /* rep cache consistency */
846 SVN_ERR(verify_rep_cache(fs, start, end, notify_func, notify_baton,
847 cancel_func, cancel_baton, scratch_pool));