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"
34 #include "../libsvn_fs/fs-loader.h"
36 #include "svn_private_config.h"
41 /* Baton type expected by verify_walker(). The purpose is to limit the
42 * number of notifications sent.
44 typedef struct verify_walker_baton_t
46 /* number of calls to verify_walker() since the last clean */
49 /* progress notification callback to invoke periodically (may be NULL) */
50 svn_fs_progress_notify_func_t notify_func
;
52 /* baton to use with NOTIFY_FUNC */
55 /* remember the last revision for which we called notify_func */
56 svn_revnum_t last_notified_revision
;
57 } verify_walker_baton_t
;
59 /* Used by svn_fs_x__verify().
60 Implements svn_fs_x__walk_rep_reference().walker. */
62 verify_walker(svn_fs_x__representation_t
*rep
,
65 apr_pool_t
*scratch_pool
)
67 verify_walker_baton_t
*walker_baton
= baton
;
69 /* notify and free resources periodically */
70 if (walker_baton
->iteration_count
> 1000)
72 svn_revnum_t revision
= svn_fs_x__get_revnum(rep
->id
.change_set
);
73 if ( walker_baton
->notify_func
74 && revision
!= walker_baton
->last_notified_revision
)
76 walker_baton
->notify_func(revision
,
77 walker_baton
->notify_baton
,
79 walker_baton
->last_notified_revision
= revision
;
82 walker_baton
->iteration_count
= 0;
85 /* access the repo data */
86 SVN_ERR(svn_fs_x__check_rep(rep
, fs
, scratch_pool
));
88 /* update resource usage counters */
89 walker_baton
->iteration_count
++;
94 /* Verify the rep cache DB's consistency with our rev / pack data.
95 * The function signature is similar to svn_fs_x__verify.
96 * The values of START and END have already been auto-selected and
100 verify_rep_cache(svn_fs_t
*fs
,
103 svn_fs_progress_notify_func_t notify_func
,
105 svn_cancel_func_t cancel_func
,
107 apr_pool_t
*scratch_pool
)
109 svn_boolean_t exists
;
111 /* rep-cache verification. */
112 SVN_ERR(svn_fs_x__exists_rep_cache(&exists
, fs
, scratch_pool
));
115 /* provide a baton to allow the reuse of open file handles between
116 iterations (saves 2/3 of OS level file operations). */
117 verify_walker_baton_t
*baton
118 = apr_pcalloc(scratch_pool
, sizeof(*baton
));
120 baton
->last_notified_revision
= SVN_INVALID_REVNUM
;
121 baton
->notify_func
= notify_func
;
122 baton
->notify_baton
= notify_baton
;
124 /* tell the user that we are now ready to do *something* */
126 notify_func(SVN_INVALID_REVNUM
, notify_baton
, scratch_pool
);
128 /* Do not attempt to walk the rep-cache database if its file does
129 not exist, since doing so would create it --- which may confuse
130 the administrator. Don't take any lock. */
131 SVN_ERR(svn_fs_x__walk_rep_reference(fs
, start
, end
,
132 verify_walker
, baton
,
133 cancel_func
, cancel_baton
,
140 /* Verify that the MD5 checksum of the data between offsets START and END
141 * in FILE matches the EXPECTED checksum. If there is a mismatch use the
142 * indedx NAME in the error message. Supports cancellation with CANCEL_FUNC
143 * and CANCEL_BATON. SCRATCH_POOL is for temporary allocations. */
145 verify_index_checksum(svn_fs_x__revision_file_t
*file
,
147 svn_fs_x__index_info_t
*index_info
,
148 svn_cancel_func_t cancel_func
,
150 apr_pool_t
*scratch_pool
)
152 unsigned char buffer
[SVN__STREAM_CHUNK_SIZE
];
153 apr_off_t size
= index_info
->end
- index_info
->start
;
154 svn_checksum_t
*actual
;
155 svn_checksum_ctx_t
*context
156 = svn_checksum_ctx_create(svn_checksum_md5
, scratch_pool
);
158 /* Calculate the index checksum. */
159 SVN_ERR(svn_fs_x__rev_file_seek(file
, NULL
, index_info
->start
));
162 apr_size_t to_read
= size
> sizeof(buffer
)
165 SVN_ERR(svn_fs_x__rev_file_read(file
, buffer
, to_read
));
166 SVN_ERR(svn_checksum_update(context
, buffer
, to_read
));
170 SVN_ERR(cancel_func(cancel_baton
));
173 SVN_ERR(svn_checksum_final(&actual
, context
, scratch_pool
));
175 /* Verify that it matches the expected checksum. */
176 if (!svn_checksum_match(index_info
->checksum
, actual
))
178 const char *file_name
;
180 SVN_ERR(svn_fs_x__rev_file_name(&file_name
, file
, scratch_pool
));
181 SVN_ERR(svn_checksum_mismatch_err(index_info
->checksum
, actual
,
183 _("%s checksum mismatch in file %s"),
190 /* Verify the MD5 checksums of the index data in the rev / pack file
191 * containing revision START in FS. If given, invoke CANCEL_FUNC with
192 * CANCEL_BATON at regular intervals. Use SCRATCH_POOL for temporary
196 verify_index_checksums(svn_fs_t
*fs
,
198 svn_cancel_func_t cancel_func
,
200 apr_pool_t
*scratch_pool
)
202 svn_fs_x__revision_file_t
*rev_file
;
203 svn_fs_x__index_info_t l2p_index_info
;
204 svn_fs_x__index_info_t p2l_index_info
;
206 /* Open the rev / pack file and read the footer */
207 SVN_ERR(svn_fs_x__rev_file_init(&rev_file
, fs
, start
, scratch_pool
));
208 SVN_ERR(svn_fs_x__rev_file_l2p_info(&l2p_index_info
, rev_file
));
209 SVN_ERR(svn_fs_x__rev_file_p2l_info(&p2l_index_info
, rev_file
));
211 /* Verify the index contents against the checksum from the footer. */
212 SVN_ERR(verify_index_checksum(rev_file
, "L2P index", &l2p_index_info
,
213 cancel_func
, cancel_baton
, scratch_pool
));
214 SVN_ERR(verify_index_checksum(rev_file
, "P2L index", &p2l_index_info
,
215 cancel_func
, cancel_baton
, scratch_pool
));
218 SVN_ERR(svn_fs_x__close_revision_file(rev_file
));
223 /* Verify that for all log-to-phys index entries for revisions START to
224 * START + COUNT-1 in FS there is a consistent entry in the phys-to-log
225 * index. If given, invoke CANCEL_FUNC with CANCEL_BATON at regular
226 * intervals. Use SCRATCH_POOL for temporary allocations.
229 compare_l2p_to_p2l_index(svn_fs_t
*fs
,
232 svn_cancel_func_t cancel_func
,
234 apr_pool_t
*scratch_pool
)
237 apr_pool_t
*iterpool
= svn_pool_create(scratch_pool
);
238 apr_array_header_t
*max_ids
;
240 /* common file access structure */
241 svn_fs_x__revision_file_t
*rev_file
;
242 SVN_ERR(svn_fs_x__rev_file_init(&rev_file
, fs
, start
, scratch_pool
));
244 /* determine the range of items to check for each revision */
245 SVN_ERR(svn_fs_x__l2p_get_max_ids(&max_ids
, fs
, start
, count
, scratch_pool
,
248 /* check all items in all revisions if the given range */
249 for (i
= 0; i
< max_ids
->nelts
; ++i
)
252 apr_uint64_t max_id
= APR_ARRAY_IDX(max_ids
, i
, apr_uint64_t
);
253 svn_revnum_t revision
= start
+ i
;
255 for (k
= 0; k
< max_id
; ++k
)
258 apr_uint32_t sub_item
;
259 svn_fs_x__id_t l2p_item
;
260 svn_fs_x__id_t
*p2l_item
;
262 l2p_item
.change_set
= svn_fs_x__change_set_by_rev(revision
);
265 /* get L2P entry. Ignore unused entries. */
266 SVN_ERR(svn_fs_x__item_offset(&offset
, &sub_item
, fs
, rev_file
,
267 &l2p_item
, iterpool
));
271 /* find the corresponding P2L entry */
272 SVN_ERR(svn_fs_x__p2l_item_lookup(&p2l_item
, fs
, rev_file
,
273 revision
, offset
, sub_item
,
274 iterpool
, iterpool
));
276 if (p2l_item
== NULL
)
277 return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT
,
279 _("p2l index entry not found for "
280 "PHYS o%s:s%ld returned by "
281 "l2p index for LOG r%ld:i%ld"),
282 apr_off_t_toa(scratch_pool
, offset
),
283 (long)sub_item
, revision
, (long)k
);
285 if (!svn_fs_x__id_eq(&l2p_item
, p2l_item
))
286 return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT
,
288 _("p2l index info LOG r%ld:i%ld"
290 "l2p index for LOG r%ld:i%ld"),
291 svn_fs_x__get_revnum(p2l_item
->change_set
),
292 (long)p2l_item
->number
, revision
,
295 svn_pool_clear(iterpool
);
299 SVN_ERR(cancel_func(cancel_baton
));
302 svn_pool_destroy(iterpool
);
304 SVN_ERR(svn_fs_x__close_revision_file(rev_file
));
309 /* Verify that for all phys-to-log index entries for revisions START to
310 * START + COUNT-1 in FS there is a consistent entry in the log-to-phys
311 * index. If given, invoke CANCEL_FUNC with CANCEL_BATON at regular
312 * intervals. Use SCRATCH_POOL for temporary allocations.
314 * Please note that we can only check on pack / rev file granularity and
315 * must only be called for a single rev / pack file.
318 compare_p2l_to_l2p_index(svn_fs_t
*fs
,
321 svn_cancel_func_t cancel_func
,
323 apr_pool_t
*scratch_pool
)
325 svn_fs_x__data_t
*ffd
= fs
->fsap_data
;
326 apr_pool_t
*iterpool
= svn_pool_create(scratch_pool
);
327 apr_pool_t
*iterpool2
= svn_pool_create(scratch_pool
);
328 apr_off_t max_offset
;
329 apr_off_t offset
= 0;
331 /* common file access structure */
332 svn_fs_x__revision_file_t
*rev_file
;
333 SVN_ERR(svn_fs_x__rev_file_init(&rev_file
, fs
, start
, scratch_pool
));
335 /* get the size of the rev / pack file as covered by the P2L index */
336 SVN_ERR(svn_fs_x__p2l_get_max_offset(&max_offset
, fs
, rev_file
, start
,
339 /* for all offsets in the file, get the P2L index entries and check
340 them against the L2P index */
341 for (offset
= 0; offset
< max_offset
; )
343 apr_array_header_t
*entries
;
344 svn_fs_x__p2l_entry_t
*last_entry
;
347 svn_pool_clear(iterpool
);
349 /* get all entries for the current block */
350 SVN_ERR(svn_fs_x__p2l_index_lookup(&entries
, fs
, rev_file
, start
,
351 offset
, ffd
->p2l_page_size
,
352 iterpool
, iterpool
));
353 if (entries
->nelts
== 0)
354 return svn_error_createf(SVN_ERR_FS_INDEX_CORRUPTION
,
356 _("p2l does not cover offset %s"
357 " for revision %ld"),
358 apr_off_t_toa(scratch_pool
, offset
), start
);
360 /* process all entries (and later continue with the next block) */
362 = &APR_ARRAY_IDX(entries
, entries
->nelts
-1, svn_fs_x__p2l_entry_t
);
363 offset
= last_entry
->offset
+ last_entry
->size
;
365 for (i
= 0; i
< entries
->nelts
; ++i
)
368 svn_fs_x__p2l_entry_t
*entry
369 = &APR_ARRAY_IDX(entries
, i
, svn_fs_x__p2l_entry_t
);
371 /* check all sub-items for consist entries in the L2P index */
372 for (k
= 0; k
< entry
->item_count
; ++k
)
374 apr_off_t l2p_offset
;
375 apr_uint32_t sub_item
;
376 svn_fs_x__id_t
*p2l_item
= &entry
->items
[k
];
377 svn_revnum_t revision
378 = svn_fs_x__get_revnum(p2l_item
->change_set
);
380 svn_pool_clear(iterpool2
);
381 SVN_ERR(svn_fs_x__item_offset(&l2p_offset
, &sub_item
, fs
,
382 rev_file
, p2l_item
, iterpool2
));
384 if (sub_item
!= k
|| l2p_offset
!= entry
->offset
)
385 return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT
,
387 _("l2p index entry PHYS o%s:s%ld "
388 "does not match p2l index value "
389 "LOG r%ld:i%ld for PHYS o%s:s%ld"),
390 apr_off_t_toa(scratch_pool
,
394 (long)p2l_item
->number
,
395 apr_off_t_toa(scratch_pool
,
402 SVN_ERR(cancel_func(cancel_baton
));
405 svn_pool_destroy(iterpool2
);
406 svn_pool_destroy(iterpool
);
408 SVN_ERR(svn_fs_x__close_revision_file(rev_file
));
413 /* Items smaller than this can be read at once into a buffer and directly
414 * be checksummed. Larger items require stream processing.
415 * Must be a multiple of 8. */
416 #define STREAM_THRESHOLD 4096
418 /* Verify that the next SIZE bytes read from FILE are NUL. SIZE must not
419 * exceed STREAM_THRESHOLD. Use SCRATCH_POOL for temporary allocations.
422 expect_buffer_nul(svn_fs_x__revision_file_t
*file
,
424 apr_pool_t
*scratch_pool
)
428 unsigned char buffer
[STREAM_THRESHOLD
];
429 apr_uint64_t chunks
[STREAM_THRESHOLD
/ sizeof(apr_uint64_t
)];
433 SVN_ERR_ASSERT(size
<= STREAM_THRESHOLD
);
435 /* read the whole data block; error out on failure */
436 data
.chunks
[(size
- 1)/ sizeof(apr_uint64_t
)] = 0;
437 SVN_ERR(svn_fs_x__rev_file_read(file
, data
.buffer
, size
));
440 for (i
= 0; i
< size
/ sizeof(apr_uint64_t
); ++i
)
441 if (data
.chunks
[i
] != 0)
444 /* byte-wise check upon mismatch or at the end of the block */
445 for (i
*= sizeof(apr_uint64_t
); i
< size
; ++i
)
446 if (data
.buffer
[i
] != 0)
448 const char *file_name
;
451 SVN_ERR(svn_fs_x__rev_file_name(&file_name
, file
, scratch_pool
));
452 SVN_ERR(svn_fs_x__rev_file_offset(&offset
, file
));
455 return svn_error_createf(SVN_ERR_FS_CORRUPT
, NULL
,
456 _("Empty section in file %s contains "
457 "non-NUL data at offset %s"),
459 apr_off_t_toa(scratch_pool
, offset
));
465 /* Verify that the next SIZE bytes read from FILE are NUL.
466 * Use SCRATCH_POOL for temporary allocations.
469 read_all_nul(svn_fs_x__revision_file_t
*file
,
471 apr_pool_t
*scratch_pool
)
473 for (; size
>= STREAM_THRESHOLD
; size
-= STREAM_THRESHOLD
)
474 SVN_ERR(expect_buffer_nul(file
, STREAM_THRESHOLD
, scratch_pool
));
477 SVN_ERR(expect_buffer_nul(file
, size
, scratch_pool
));
482 /* Compare the ACTUAL checksum with the one expected by ENTRY.
483 * Return an error in case of mismatch. Use the name of FILE
484 * in error message. Allocate temporary data in SCRATCH_POOL.
487 expected_checksum(svn_fs_x__revision_file_t
*file
,
488 svn_fs_x__p2l_entry_t
*entry
,
490 apr_pool_t
*scratch_pool
)
492 if (actual
!= entry
->fnv1_checksum
)
494 const char *file_name
;
496 SVN_ERR(svn_fs_x__rev_file_name(&file_name
, file
, scratch_pool
));
497 return svn_error_createf(SVN_ERR_FS_CORRUPT
, NULL
,
498 _("Checksum mismatch in item at offset %s of "
499 "length %s bytes in file %s"),
500 apr_off_t_toa(scratch_pool
, entry
->offset
),
501 apr_off_t_toa(scratch_pool
, entry
->size
),
508 /* Verify that the FNV checksum over the next ENTRY->SIZE bytes read
509 * from FILE will match ENTRY's expected checksum. SIZE must not
510 * exceed STREAM_THRESHOLD. Use SCRATCH_POOL for temporary allocations.
513 expected_buffered_checksum(svn_fs_x__revision_file_t
*file
,
514 svn_fs_x__p2l_entry_t
*entry
,
515 apr_pool_t
*scratch_pool
)
517 unsigned char buffer
[STREAM_THRESHOLD
];
518 SVN_ERR_ASSERT(entry
->size
<= STREAM_THRESHOLD
);
520 SVN_ERR(svn_fs_x__rev_file_read(file
, buffer
, (apr_size_t
)entry
->size
));
521 SVN_ERR(expected_checksum(file
, entry
,
522 svn__fnv1a_32x4(buffer
, (apr_size_t
)entry
->size
),
528 /* Verify that the FNV checksum over the next ENTRY->SIZE bytes read from
529 * FILE will match ENTRY's expected checksum.
530 * Use SCRATCH_POOL for temporary allocations.
533 expected_streamed_checksum(svn_fs_x__revision_file_t
*file
,
534 svn_fs_x__p2l_entry_t
*entry
,
535 apr_pool_t
*scratch_pool
)
537 unsigned char buffer
[STREAM_THRESHOLD
];
538 svn_checksum_t
*checksum
;
539 svn_checksum_ctx_t
*context
540 = svn_checksum_ctx_create(svn_checksum_fnv1a_32x4
, scratch_pool
);
541 apr_off_t size
= entry
->size
;
545 apr_size_t to_read
= size
> sizeof(buffer
)
548 SVN_ERR(svn_fs_x__rev_file_read(file
, buffer
, to_read
));
549 SVN_ERR(svn_checksum_update(context
, buffer
, to_read
));
553 SVN_ERR(svn_checksum_final(&checksum
, context
, scratch_pool
));
554 SVN_ERR(expected_checksum(file
, entry
,
555 ntohl(*(const apr_uint32_t
*)checksum
->digest
),
561 /* Verify that for all phys-to-log index entries for revisions START to
562 * START + COUNT-1 in FS match the actual pack / rev file contents.
563 * If given, invoke CANCEL_FUNC with CANCEL_BATON at regular intervals.
564 * Use SCRATCH_POOL for temporary allocations.
566 * Please note that we can only check on pack / rev file granularity and
567 * must only be called for a single rev / pack file.
570 compare_p2l_to_rev(svn_fs_t
*fs
,
573 svn_cancel_func_t cancel_func
,
575 apr_pool_t
*scratch_pool
)
577 svn_fs_x__data_t
*ffd
= fs
->fsap_data
;
578 apr_pool_t
*iterpool
= svn_pool_create(scratch_pool
);
579 apr_off_t max_offset
;
580 apr_off_t offset
= 0;
581 svn_fs_x__revision_file_t
*rev_file
;
582 svn_fs_x__index_info_t l2p_index_info
;
584 /* open the pack / rev file that is covered by the p2l index */
585 SVN_ERR(svn_fs_x__rev_file_init(&rev_file
, fs
, start
, scratch_pool
));
587 /* check file size vs. range covered by index */
588 SVN_ERR(svn_fs_x__rev_file_l2p_info(&l2p_index_info
, rev_file
));
589 SVN_ERR(svn_fs_x__p2l_get_max_offset(&max_offset
, fs
, rev_file
, start
,
592 if (l2p_index_info
.start
!= max_offset
)
593 return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT
, NULL
,
594 _("File size of %s for revision r%ld does "
595 "not match p2l index size of %s"),
596 apr_off_t_toa(scratch_pool
,
597 l2p_index_info
.start
),
599 apr_off_t_toa(scratch_pool
,
602 SVN_ERR(svn_fs_x__rev_file_seek(rev_file
, NULL
, 0));
604 /* for all offsets in the file, get the P2L index entries and check
605 them against the L2P index */
606 for (offset
= 0; offset
< max_offset
; )
608 apr_array_header_t
*entries
;
611 svn_pool_clear(iterpool
);
613 /* get all entries for the current block */
614 SVN_ERR(svn_fs_x__p2l_index_lookup(&entries
, fs
, rev_file
, start
,
615 offset
, ffd
->p2l_page_size
,
616 iterpool
, iterpool
));
618 /* The above might have moved the file pointer.
619 * Ensure we actually start reading at OFFSET. */
620 SVN_ERR(svn_fs_x__rev_file_seek(rev_file
, NULL
, offset
));
622 /* process all entries (and later continue with the next block) */
623 for (i
= 0; i
< entries
->nelts
; ++i
)
625 svn_fs_x__p2l_entry_t
*entry
626 = &APR_ARRAY_IDX(entries
, i
, svn_fs_x__p2l_entry_t
);
628 /* skip bits we previously checked */
629 if (i
== 0 && entry
->offset
< offset
)
632 /* skip zero-sized entries */
633 if (entry
->size
== 0)
636 /* p2l index must cover all rev / pack file offsets exactly once */
637 if (entry
->offset
!= offset
)
638 return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT
,
640 _("p2l index entry for revision r%ld"
641 " is non-contiguous between offsets "
644 apr_off_t_toa(scratch_pool
, offset
),
645 apr_off_t_toa(scratch_pool
,
648 /* empty sections must contain NUL bytes only */
649 if (entry
->type
== SVN_FS_X__ITEM_TYPE_UNUSED
)
651 /* skip filler entry at the end of the p2l index */
652 if (entry
->offset
!= max_offset
)
653 SVN_ERR(read_all_nul(rev_file
, entry
->size
, iterpool
));
657 if (entry
->size
< STREAM_THRESHOLD
)
658 SVN_ERR(expected_buffered_checksum(rev_file
, entry
,
661 SVN_ERR(expected_streamed_checksum(rev_file
, entry
,
666 offset
+= entry
->size
;
670 SVN_ERR(cancel_func(cancel_baton
));
673 svn_pool_destroy(iterpool
);
678 /* Verify that the revprops of the revisions START to END in FS can be
679 * accessed. Invoke CANCEL_FUNC with CANCEL_BATON at regular intervals.
681 * The values of START and END have already been auto-selected and
685 verify_revprops(svn_fs_t
*fs
,
688 svn_cancel_func_t cancel_func
,
690 apr_pool_t
*scratch_pool
)
692 svn_revnum_t revision
;
693 apr_pool_t
*iterpool
= svn_pool_create(scratch_pool
);
695 /* Invalidate the revprop generation once.
696 * Use the cache inside the loop to speed up packed revprop access. */
697 svn_fs_x__invalidate_revprop_generation(fs
);
699 for (revision
= start
; revision
< end
; ++revision
)
704 svn_pool_clear(iterpool
);
706 /* Access the svn:date revprop.
707 * This implies parsing all revprops for that revision. */
708 SVN_ERR(svn_fs_x__revision_prop(&date
, fs
, revision
,
709 SVN_PROP_REVISION_DATE
, FALSE
,
710 iterpool
, iterpool
));
712 /* The time stamp is the only revprop that, if given, needs to
713 * have a valid content. */
715 SVN_ERR(svn_time_from_cstring(&timetemp
, date
->data
, iterpool
));
718 SVN_ERR(cancel_func(cancel_baton
));
721 svn_pool_destroy(iterpool
);
726 /* Verify that on-disk representation has not been tempered with (in a way
727 * that leaves the repository in a corrupted state). This compares log-to-
728 * phys with phys-to-log indexes, verifies the low-level checksums and
729 * checks that all revprops are available. The function signature is
730 * similar to svn_fs_x__verify.
732 * The values of START and END have already been auto-selected and
736 verify_metadata_consistency(svn_fs_t
*fs
,
739 svn_fs_progress_notify_func_t notify_func
,
741 svn_cancel_func_t cancel_func
,
743 apr_pool_t
*scratch_pool
)
746 svn_fs_x__data_t
*ffd
= fs
->fsap_data
;
747 svn_revnum_t revision
, next_revision
;
748 apr_pool_t
*iterpool
= svn_pool_create(scratch_pool
);
750 for (revision
= start
; revision
<= end
; revision
= next_revision
)
752 svn_revnum_t count
= svn_fs_x__packed_base_rev(fs
, revision
);
753 svn_revnum_t pack_start
= count
;
754 svn_revnum_t pack_end
= pack_start
+ svn_fs_x__pack_size(fs
, revision
);
756 svn_pool_clear(iterpool
);
758 if (notify_func
&& (pack_start
% ffd
->max_files_per_dir
== 0))
759 notify_func(pack_start
, notify_baton
, iterpool
);
761 /* Check for external corruption to the indexes. */
762 err
= verify_index_checksums(fs
, pack_start
, cancel_func
,
763 cancel_baton
, iterpool
);
765 /* two-way index check */
767 err
= compare_l2p_to_p2l_index(fs
, pack_start
, pack_end
- pack_start
,
768 cancel_func
, cancel_baton
, iterpool
);
770 err
= compare_p2l_to_l2p_index(fs
, pack_start
, pack_end
- pack_start
,
771 cancel_func
, cancel_baton
, iterpool
);
773 /* verify in-index checksums and types vs. actual rev / pack files */
775 err
= compare_p2l_to_rev(fs
, pack_start
, pack_end
- pack_start
,
776 cancel_func
, cancel_baton
, iterpool
);
778 /* ensure that revprops are available and accessible */
780 err
= verify_revprops(fs
, pack_start
, pack_end
,
781 cancel_func
, cancel_baton
, iterpool
);
783 /* concurrent packing is one of the reasons why verification may fail.
784 Make sure, we operate on up-to-date information. */
788 = svn_fs_x__read_min_unpacked_rev(&ffd
->min_unpacked_rev
,
791 /* Be careful to not leak ERR. */
793 return svn_error_trace(svn_error_compose_create(err
, err2
));
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 /* Input validation. */
828 if (! SVN_IS_VALID_REVNUM(start
))
830 if (! SVN_IS_VALID_REVNUM(end
))
832 SVN_ERR(svn_fs_x__youngest_rev(&end
, fs
, scratch_pool
));
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
));