2 Trivial Database 2: free list/block handling
3 Copyright (C) Rusty Russell 2010
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 3 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 #include <ccan/likely/likely.h>
20 #include <ccan/asearch/asearch.h>
22 /* We keep an ordered array of offsets. */
23 static bool append(tdb_off_t
**arr
, size_t *num
, tdb_off_t off
)
25 tdb_off_t
*new = realloc(*arr
, (*num
+ 1) * sizeof(tdb_off_t
));
33 static enum TDB_ERROR
check_header(struct tdb_context
*tdb
, tdb_off_t
*recovery
,
34 uint64_t *features
, size_t *num_capabilities
)
37 struct tdb_header hdr
;
41 ecode
= tdb_read_convert(tdb
, 0, &hdr
, sizeof(hdr
));
42 if (ecode
!= TDB_SUCCESS
) {
45 /* magic food should not be converted, so convert back. */
46 tdb_convert(tdb
, hdr
.magic_food
, sizeof(hdr
.magic_food
));
48 hash_test
= TDB_HASH_MAGIC
;
49 hash_test
= tdb_hash(tdb
, &hash_test
, sizeof(hash_test
));
50 if (hdr
.hash_test
!= hash_test
) {
51 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
, TDB_LOG_ERROR
,
52 "check: hash test %llu should be %llu",
53 (long long)hdr
.hash_test
,
54 (long long)hash_test
);
57 if (strcmp(hdr
.magic_food
, TDB_MAGIC_FOOD
) != 0) {
58 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
, TDB_LOG_ERROR
,
59 "check: bad magic '%.*s'",
60 (unsigned)sizeof(hdr
.magic_food
),
64 /* Features which are used must be a subset of features offered. */
65 if (hdr
.features_used
& ~hdr
.features_offered
) {
66 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
, TDB_LOG_ERROR
,
67 "check: features used (0x%llx) which"
68 " are not offered (0x%llx)",
69 (long long)hdr
.features_used
,
70 (long long)hdr
.features_offered
);
73 *features
= hdr
.features_offered
;
74 *recovery
= hdr
.recovery
;
76 if (*recovery
< sizeof(hdr
)
77 || *recovery
> tdb
->file
->map_size
) {
78 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
, TDB_LOG_ERROR
,
80 " invalid recovery offset %zu",
85 for (off
= hdr
.capabilities
; off
&& ecode
== TDB_SUCCESS
; off
= next
) {
86 const struct tdb_capability
*cap
;
89 cap
= tdb_access_read(tdb
, off
, sizeof(*cap
), true);
90 if (TDB_PTR_IS_ERR(cap
)) {
91 return TDB_PTR_ERR(cap
);
94 /* All capabilities are unknown. */
95 e
= unknown_capability(tdb
, "tdb_check", cap
->type
);
97 tdb_access_release(tdb
, cap
);
100 (*num_capabilities
)++;
103 /* Don't check reserved: they *can* be used later. */
107 static enum TDB_ERROR
check_hash_tree(struct tdb_context
*tdb
,
108 tdb_off_t off
, unsigned int group_bits
,
110 unsigned hprefix_bits
,
114 enum TDB_ERROR (*check
)(TDB_DATA
,
118 static enum TDB_ERROR
check_hash_chain(struct tdb_context
*tdb
,
124 enum TDB_ERROR (*check
)(TDB_DATA
,
129 struct tdb_used_record rec
;
130 enum TDB_ERROR ecode
;
132 ecode
= tdb_read_convert(tdb
, off
, &rec
, sizeof(rec
));
133 if (ecode
!= TDB_SUCCESS
) {
137 if (rec_magic(&rec
) != TDB_CHAIN_MAGIC
) {
138 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
, TDB_LOG_ERROR
,
139 "tdb_check: Bad hash chain magic %llu",
140 (long long)rec_magic(&rec
));
143 if (rec_data_length(&rec
) != sizeof(struct tdb_chain
)) {
144 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
, TDB_LOG_ERROR
,
146 " Bad hash chain length %llu vs %zu",
147 (long long)rec_data_length(&rec
),
148 sizeof(struct tdb_chain
));
150 if (rec_key_length(&rec
) != 0) {
151 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
, TDB_LOG_ERROR
,
152 "tdb_check: Bad hash chain key length %llu",
153 (long long)rec_key_length(&rec
));
155 if (rec_hash(&rec
) != 0) {
156 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
, TDB_LOG_ERROR
,
157 "tdb_check: Bad hash chain hash value %llu",
158 (long long)rec_hash(&rec
));
162 ecode
= check_hash_tree(tdb
, off
, 0, hash
, 64,
163 used
, num_used
, num_found
, check
, data
);
164 if (ecode
!= TDB_SUCCESS
) {
168 off
= tdb_read_off(tdb
, off
+ offsetof(struct tdb_chain
, next
));
169 if (TDB_OFF_IS_ERR(off
)) {
170 return TDB_OFF_TO_ERR(off
);
175 return check_hash_chain(tdb
, off
, hash
, used
, num_used
, num_found
,
179 static enum TDB_ERROR
check_hash_record(struct tdb_context
*tdb
,
182 unsigned hprefix_bits
,
186 enum TDB_ERROR (*check
)(TDB_DATA
,
191 struct tdb_used_record rec
;
192 enum TDB_ERROR ecode
;
194 if (hprefix_bits
>= 64)
195 return check_hash_chain(tdb
, off
, hprefix
, used
, num_used
,
196 num_found
, check
, data
);
198 ecode
= tdb_read_convert(tdb
, off
, &rec
, sizeof(rec
));
199 if (ecode
!= TDB_SUCCESS
) {
203 if (rec_magic(&rec
) != TDB_HTABLE_MAGIC
) {
204 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
, TDB_LOG_ERROR
,
205 "tdb_check: Bad hash table magic %llu",
206 (long long)rec_magic(&rec
));
208 if (rec_data_length(&rec
)
209 != sizeof(tdb_off_t
) << TDB_SUBLEVEL_HASH_BITS
) {
210 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
, TDB_LOG_ERROR
,
212 " Bad hash table length %llu vs %llu",
213 (long long)rec_data_length(&rec
),
214 (long long)sizeof(tdb_off_t
)
215 << TDB_SUBLEVEL_HASH_BITS
);
217 if (rec_key_length(&rec
) != 0) {
218 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
, TDB_LOG_ERROR
,
219 "tdb_check: Bad hash table key length %llu",
220 (long long)rec_key_length(&rec
));
222 if (rec_hash(&rec
) != 0) {
223 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
, TDB_LOG_ERROR
,
224 "tdb_check: Bad hash table hash value %llu",
225 (long long)rec_hash(&rec
));
229 return check_hash_tree(tdb
, off
,
230 TDB_SUBLEVEL_HASH_BITS
-TDB_HASH_GROUP_BITS
,
231 hprefix
, hprefix_bits
,
232 used
, num_used
, num_found
, check
, data
);
235 static int off_cmp(const tdb_off_t
*a
, const tdb_off_t
*b
)
237 /* Can overflow an int. */
243 static uint64_t get_bits(uint64_t h
, unsigned num
, unsigned *used
)
247 return (h
>> (64 - *used
)) & ((1U << num
) - 1);
250 static enum TDB_ERROR
check_hash_tree(struct tdb_context
*tdb
,
251 tdb_off_t off
, unsigned int group_bits
,
253 unsigned hprefix_bits
,
257 enum TDB_ERROR (*check
)(TDB_DATA
,
262 const tdb_off_t
*hash
;
263 struct tdb_used_record rec
;
264 enum TDB_ERROR ecode
;
266 hash
= tdb_access_read(tdb
, off
,
268 << (group_bits
+ TDB_HASH_GROUP_BITS
),
270 if (TDB_PTR_IS_ERR(hash
)) {
271 return TDB_PTR_ERR(hash
);
274 for (g
= 0; g
< (1 << group_bits
); g
++) {
275 const tdb_off_t
*group
= hash
+ (g
<< TDB_HASH_GROUP_BITS
);
276 for (b
= 0; b
< (1 << TDB_HASH_GROUP_BITS
); b
++) {
277 unsigned int bucket
, i
, used_bits
;
283 off
= group
[b
] & TDB_OFF_MASK
;
284 p
= asearch(&off
, used
, num_used
, off_cmp
);
286 ecode
= tdb_logerr(tdb
, TDB_ERR_CORRUPT
,
288 "tdb_check: Invalid offset"
293 /* Mark it invalid. */
297 if (hprefix_bits
== 64) {
298 /* Chained entries are unordered. */
299 if (is_subhash(group
[b
])) {
300 ecode
= TDB_ERR_CORRUPT
;
301 tdb_logerr(tdb
, ecode
,
303 "tdb_check: Invalid chain"
307 h
= hash_record(tdb
, off
);
309 ecode
= TDB_ERR_CORRUPT
;
310 tdb_logerr(tdb
, ecode
,
312 "check: bad hash chain"
319 ecode
= tdb_read_convert(tdb
, off
, &rec
,
321 if (ecode
!= TDB_SUCCESS
) {
327 if (is_subhash(group
[b
])) {
330 << (group_bits
+ TDB_HASH_GROUP_BITS
))
331 + g
* (1 << TDB_HASH_GROUP_BITS
) + b
;
333 ecode
= check_hash_record(tdb
,
334 group
[b
] & TDB_OFF_MASK
,
338 + TDB_HASH_GROUP_BITS
,
339 used
, num_used
, num_found
,
341 if (ecode
!= TDB_SUCCESS
) {
348 /* Does it belong here at all? */
349 h
= hash_record(tdb
, off
);
351 if (get_bits(h
, hprefix_bits
, &used_bits
) != hprefix
353 ecode
= tdb_logerr(tdb
, TDB_ERR_CORRUPT
,
355 "check: bad hash placement"
362 /* Does it belong in this group? */
363 if (get_bits(h
, group_bits
, &used_bits
) != g
) {
364 ecode
= tdb_logerr(tdb
, TDB_ERR_CORRUPT
,
366 "check: bad group %llu"
372 /* Are bucket bits correct? */
373 bucket
= group
[b
] & TDB_OFF_HASH_GROUP_MASK
;
374 if (get_bits(h
, TDB_HASH_GROUP_BITS
, &used_bits
)
376 used_bits
-= TDB_HASH_GROUP_BITS
;
377 ecode
= tdb_logerr(tdb
, TDB_ERR_CORRUPT
,
379 "check: bad bucket %u vs %u",
380 (unsigned)get_bits(h
,
387 /* There must not be any zero entries between
388 * the bucket it belongs in and this one! */
391 i
= (i
+ 1) % (1 << TDB_HASH_GROUP_BITS
)) {
393 ecode
= TDB_ERR_CORRUPT
;
394 tdb_logerr(tdb
, ecode
,
396 "check: bad group placement"
403 ecode
= tdb_read_convert(tdb
, off
, &rec
, sizeof(rec
));
404 if (ecode
!= TDB_SUCCESS
) {
408 /* Bottom bits must match header. */
409 if ((h
& ((1 << 11)-1)) != rec_hash(&rec
)) {
410 ecode
= tdb_logerr(tdb
, TDB_ERR_CORRUPT
,
412 "tdb_check: Bad hash magic"
414 " (0x%llx vs 0x%llx)",
417 (long long)rec_hash(&rec
));
424 const unsigned char *kptr
;
426 kptr
= tdb_access_read(tdb
,
429 + rec_data_length(&rec
),
431 if (TDB_PTR_IS_ERR(kptr
)) {
432 ecode
= TDB_PTR_ERR(kptr
);
436 k
= tdb_mkdata(kptr
, rec_key_length(&rec
));
437 d
= tdb_mkdata(kptr
+ k
.dsize
,
438 rec_data_length(&rec
));
439 ecode
= check(k
, d
, data
);
440 tdb_access_release(tdb
, kptr
);
441 if (ecode
!= TDB_SUCCESS
) {
447 tdb_access_release(tdb
, hash
);
451 tdb_access_release(tdb
, hash
);
455 static enum TDB_ERROR
check_hash(struct tdb_context
*tdb
,
457 size_t num_used
, size_t num_other_used
,
458 enum TDB_ERROR (*check
)(TDB_DATA
, TDB_DATA
, void *),
461 /* Free tables and capabilities also show up as used. */
462 size_t num_found
= num_other_used
;
463 enum TDB_ERROR ecode
;
465 ecode
= check_hash_tree(tdb
, offsetof(struct tdb_header
, hashtable
),
466 TDB_TOPLEVEL_HASH_BITS
-TDB_HASH_GROUP_BITS
,
467 0, 0, used
, num_used
, &num_found
,
469 if (ecode
== TDB_SUCCESS
) {
470 if (num_found
!= num_used
) {
471 ecode
= tdb_logerr(tdb
, TDB_ERR_CORRUPT
, TDB_LOG_ERROR
,
472 "tdb_check: Not all entries"
479 static enum TDB_ERROR
check_free(struct tdb_context
*tdb
,
481 const struct tdb_free_record
*frec
,
482 tdb_off_t prev
, unsigned int ftable
,
485 enum TDB_ERROR ecode
;
487 if (frec_magic(frec
) != TDB_FREE_MAGIC
) {
488 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
, TDB_LOG_ERROR
,
489 "tdb_check: offset %llu bad magic 0x%llx",
491 (long long)frec
->magic_and_prev
);
493 if (frec_ftable(frec
) != ftable
) {
494 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
, TDB_LOG_ERROR
,
495 "tdb_check: offset %llu bad freetable %u",
496 (long long)off
, frec_ftable(frec
));
500 ecode
= tdb
->tdb2
.io
->oob(tdb
, off
,
502 + sizeof(struct tdb_used_record
),
504 if (ecode
!= TDB_SUCCESS
) {
507 if (size_to_bucket(frec_len(frec
)) != bucket
) {
508 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
, TDB_LOG_ERROR
,
509 "tdb_check: offset %llu in wrong bucket"
512 bucket
, size_to_bucket(frec_len(frec
)));
514 if (prev
&& prev
!= frec_prev(frec
)) {
515 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
, TDB_LOG_ERROR
,
516 "tdb_check: offset %llu bad prev"
519 (long long)prev
, (long long)frec_len(frec
));
524 static enum TDB_ERROR
check_free_table(struct tdb_context
*tdb
,
525 tdb_off_t ftable_off
,
531 struct tdb_freetable ft
;
534 enum TDB_ERROR ecode
;
536 ecode
= tdb_read_convert(tdb
, ftable_off
, &ft
, sizeof(ft
));
537 if (ecode
!= TDB_SUCCESS
) {
541 if (rec_magic(&ft
.hdr
) != TDB_FTABLE_MAGIC
542 || rec_key_length(&ft
.hdr
) != 0
543 || rec_data_length(&ft
.hdr
) != sizeof(ft
) - sizeof(ft
.hdr
)
544 || rec_hash(&ft
.hdr
) != 0) {
545 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
, TDB_LOG_ERROR
,
546 "tdb_check: Invalid header on free table");
549 for (i
= 0; i
< TDB_FREE_BUCKETS
; i
++) {
550 tdb_off_t off
, prev
= 0, *p
, first
= 0;
551 struct tdb_free_record f
;
553 h
= bucket_off(ftable_off
, i
);
554 for (off
= tdb_read_off(tdb
, h
); off
; off
= f
.next
) {
555 if (TDB_OFF_IS_ERR(off
)) {
556 return TDB_OFF_TO_ERR(off
);
562 ecode
= tdb_read_convert(tdb
, off
, &f
, sizeof(f
));
563 if (ecode
!= TDB_SUCCESS
) {
566 ecode
= check_free(tdb
, off
, &f
, prev
, ftable_num
, i
);
567 if (ecode
!= TDB_SUCCESS
) {
571 /* FIXME: Check hash bits */
572 p
= asearch(&off
, fr
, num_free
, off_cmp
);
574 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
,
576 "tdb_check: Invalid offset"
577 " %llu in free table",
580 /* Mark it invalid. */
587 /* Now we can check first back pointer. */
588 ecode
= tdb_read_convert(tdb
, first
, &f
, sizeof(f
));
589 if (ecode
!= TDB_SUCCESS
) {
592 ecode
= check_free(tdb
, first
, &f
, prev
, ftable_num
, i
);
593 if (ecode
!= TDB_SUCCESS
) {
601 /* Slow, but should be very rare. */
602 tdb_off_t
dead_space(struct tdb_context
*tdb
, tdb_off_t off
)
605 enum TDB_ERROR ecode
;
607 for (len
= 0; off
+ len
< tdb
->file
->map_size
; len
++) {
609 ecode
= tdb
->tdb2
.io
->tread(tdb
, off
, &c
, 1);
610 if (ecode
!= TDB_SUCCESS
) {
611 return TDB_ERR_TO_OFF(ecode
);
613 if (c
!= 0 && c
!= 0x43)
619 static enum TDB_ERROR
check_linear(struct tdb_context
*tdb
,
620 tdb_off_t
**used
, size_t *num_used
,
621 tdb_off_t
**fr
, size_t *num_free
,
622 uint64_t features
, tdb_off_t recovery
)
626 enum TDB_ERROR ecode
;
627 bool found_recovery
= false;
629 for (off
= sizeof(struct tdb_header
);
630 off
< tdb
->file
->map_size
;
633 struct tdb_used_record u
;
634 struct tdb_free_record f
;
635 struct tdb_recovery_record r
;
637 /* r is larger: only get that if we need to. */
638 ecode
= tdb_read_convert(tdb
, off
, &rec
, sizeof(rec
.f
));
639 if (ecode
!= TDB_SUCCESS
) {
643 /* If we crash after ftruncate, we can get zeroes or fill. */
644 if (rec
.r
.magic
== TDB_RECOVERY_INVALID_MAGIC
645 || rec
.r
.magic
== 0x4343434343434343ULL
) {
646 ecode
= tdb_read_convert(tdb
, off
, &rec
, sizeof(rec
.r
));
647 if (ecode
!= TDB_SUCCESS
) {
650 if (recovery
== off
) {
651 found_recovery
= true;
652 len
= sizeof(rec
.r
) + rec
.r
.max_len
;
654 len
= dead_space(tdb
, off
);
655 if (TDB_OFF_IS_ERR(len
)) {
656 return TDB_OFF_TO_ERR(len
);
658 if (len
< sizeof(rec
.r
)) {
659 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
,
662 " dead space at %zu",
666 tdb_logerr(tdb
, TDB_SUCCESS
, TDB_LOG_WARNING
,
667 "Dead space at %zu-%zu (of %zu)",
668 (size_t)off
, (size_t)(off
+ len
),
669 (size_t)tdb
->file
->map_size
);
671 } else if (rec
.r
.magic
== TDB_RECOVERY_MAGIC
) {
672 ecode
= tdb_read_convert(tdb
, off
, &rec
, sizeof(rec
.r
));
673 if (ecode
!= TDB_SUCCESS
) {
676 if (recovery
!= off
) {
677 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
,
679 "tdb_check: unexpected"
680 " recovery record at offset"
684 if (rec
.r
.len
> rec
.r
.max_len
) {
685 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
,
687 "tdb_check: invalid recovery"
691 if (rec
.r
.eof
> tdb
->file
->map_size
) {
692 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
,
694 "tdb_check: invalid old EOF"
695 " %zu", (size_t)rec
.r
.eof
);
697 found_recovery
= true;
698 len
= sizeof(rec
.r
) + rec
.r
.max_len
;
699 } else if (frec_magic(&rec
.f
) == TDB_FREE_MAGIC
) {
700 len
= sizeof(rec
.u
) + frec_len(&rec
.f
);
701 if (off
+ len
> tdb
->file
->map_size
) {
702 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
,
704 "tdb_check: free overlength"
705 " %llu at offset %llu",
709 /* This record should be in free lists. */
710 if (frec_ftable(&rec
.f
) != TDB_FTABLE_NONE
711 && !append(fr
, num_free
, off
)) {
712 return tdb_logerr(tdb
, TDB_ERR_OOM
,
714 "tdb_check: tracking %zu'th"
715 " free record.", *num_free
);
717 } else if (rec_magic(&rec
.u
) == TDB_USED_MAGIC
718 || rec_magic(&rec
.u
) == TDB_CHAIN_MAGIC
719 || rec_magic(&rec
.u
) == TDB_HTABLE_MAGIC
720 || rec_magic(&rec
.u
) == TDB_FTABLE_MAGIC
721 || rec_magic(&rec
.u
) == TDB_CAP_MAGIC
) {
722 uint64_t klen
, dlen
, extra
;
724 /* This record is used! */
725 if (!append(used
, num_used
, off
)) {
726 return tdb_logerr(tdb
, TDB_ERR_OOM
,
728 "tdb_check: tracking %zu'th"
729 " used record.", *num_used
);
732 klen
= rec_key_length(&rec
.u
);
733 dlen
= rec_data_length(&rec
.u
);
734 extra
= rec_extra_padding(&rec
.u
);
736 len
= sizeof(rec
.u
) + klen
+ dlen
+ extra
;
737 if (off
+ len
> tdb
->file
->map_size
) {
738 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
,
740 "tdb_check: used overlength"
741 " %llu at offset %llu",
746 if (len
< sizeof(rec
.f
)) {
747 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
,
749 "tdb_check: too short record"
755 /* Check that records have correct 0 at end (but may
757 if (extra
&& !features
758 && rec_magic(&rec
.u
) != TDB_CAP_MAGIC
) {
761 p
= tdb_access_read(tdb
, off
+ sizeof(rec
.u
)
762 + klen
+ dlen
, 1, false);
763 if (TDB_PTR_IS_ERR(p
))
764 return TDB_PTR_ERR(p
);
766 tdb_access_release(tdb
, p
);
769 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
,
778 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
,
780 "tdb_check: Bad magic 0x%llx"
782 (long long)rec_magic(&rec
.u
),
787 /* We must have found recovery area if there was one. */
788 if (recovery
!= 0 && !found_recovery
) {
789 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
, TDB_LOG_ERROR
,
790 "tdb_check: expected a recovery area at %zu",
797 _PUBLIC_
enum TDB_ERROR
tdb_check_(struct tdb_context
*tdb
,
798 enum TDB_ERROR (*check
)(TDB_DATA
, TDB_DATA
, void *),
801 tdb_off_t
*fr
= NULL
, *used
= NULL
, ft
, recovery
;
802 size_t num_free
= 0, num_used
= 0, num_found
= 0, num_ftables
= 0,
803 num_capabilities
= 0;
805 enum TDB_ERROR ecode
;
807 if (tdb
->flags
& TDB_CANT_CHECK
) {
808 return tdb_logerr(tdb
, TDB_SUCCESS
, TDB_LOG_WARNING
,
809 "tdb_check: database has unknown capability,"
813 if (tdb
->flags
& TDB_VERSION1
) {
814 if (tdb1_check(tdb
, check
, data
) == -1)
815 return tdb
->last_error
;
819 ecode
= tdb_allrecord_lock(tdb
, F_RDLCK
, TDB_LOCK_WAIT
, false);
820 if (ecode
!= TDB_SUCCESS
) {
821 return tdb
->last_error
= ecode
;
824 ecode
= tdb_lock_expand(tdb
, F_RDLCK
);
825 if (ecode
!= TDB_SUCCESS
) {
826 tdb_allrecord_unlock(tdb
, F_RDLCK
);
827 return tdb
->last_error
= ecode
;
830 ecode
= check_header(tdb
, &recovery
, &features
, &num_capabilities
);
831 if (ecode
!= TDB_SUCCESS
)
834 /* First we do a linear scan, checking all records. */
835 ecode
= check_linear(tdb
, &used
, &num_used
, &fr
, &num_free
, features
,
837 if (ecode
!= TDB_SUCCESS
)
840 for (ft
= first_ftable(tdb
); ft
; ft
= next_ftable(tdb
, ft
)) {
841 if (TDB_OFF_IS_ERR(ft
)) {
842 ecode
= TDB_OFF_TO_ERR(ft
);
845 ecode
= check_free_table(tdb
, ft
, num_ftables
, fr
, num_free
,
847 if (ecode
!= TDB_SUCCESS
)
852 /* FIXME: Check key uniqueness? */
853 ecode
= check_hash(tdb
, used
, num_used
, num_ftables
+ num_capabilities
,
855 if (ecode
!= TDB_SUCCESS
)
858 if (num_found
!= num_free
) {
859 ecode
= tdb_logerr(tdb
, TDB_ERR_CORRUPT
, TDB_LOG_ERROR
,
860 "tdb_check: Not all entries are in"
865 tdb_allrecord_unlock(tdb
, F_RDLCK
);
866 tdb_unlock_expand(tdb
, F_RDLCK
);
869 return tdb
->last_error
= ecode
;