check.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. /*
  2. Trivial Database 2: free list/block handling
  3. Copyright (C) Rusty Russell 2010
  4. This library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Lesser General Public
  6. License as published by the Free Software Foundation; either
  7. version 3 of the License, or (at your option) any later version.
  8. This library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public
  13. License along with this library; if not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #include "private.h"
  16. #include <ccan/likely/likely.h>
  17. #include <ccan/asearch/asearch.h>
  18. /* We keep an ordered array of offsets. */
  19. static bool append(tdb_off_t **arr, size_t *num, tdb_off_t off)
  20. {
  21. tdb_off_t *new = realloc(*arr, (*num + 1) * sizeof(tdb_off_t));
  22. if (!new)
  23. return false;
  24. new[(*num)++] = off;
  25. *arr = new;
  26. return true;
  27. }
  28. static bool check_header(struct tdb_context *tdb, tdb_off_t *recovery)
  29. {
  30. uint64_t hash_test;
  31. struct tdb_header hdr;
  32. if (tdb_read_convert(tdb, 0, &hdr, sizeof(hdr)) == -1)
  33. return false;
  34. /* magic food should not be converted, so convert back. */
  35. tdb_convert(tdb, hdr.magic_food, sizeof(hdr.magic_food));
  36. hash_test = TDB_HASH_MAGIC;
  37. hash_test = tdb_hash(tdb, &hash_test, sizeof(hash_test));
  38. if (hdr.hash_test != hash_test) {
  39. tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_DEBUG_ERROR,
  40. "check: hash test %llu should be %llu",
  41. (long long)hdr.hash_test,
  42. (long long)hash_test);
  43. return false;
  44. }
  45. if (strcmp(hdr.magic_food, TDB_MAGIC_FOOD) != 0) {
  46. tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_DEBUG_ERROR,
  47. "check: bad magic '%.*s'",
  48. (unsigned)sizeof(hdr.magic_food), hdr.magic_food);
  49. return false;
  50. }
  51. *recovery = hdr.recovery;
  52. if (*recovery) {
  53. if (*recovery < sizeof(hdr) || *recovery > tdb->map_size) {
  54. tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_DEBUG_ERROR,
  55. "tdb_check: invalid recovery offset %zu",
  56. (size_t)*recovery);
  57. return false;
  58. }
  59. }
  60. /* Don't check reserved: they *can* be used later. */
  61. return true;
  62. }
  63. static bool check_hash_tree(struct tdb_context *tdb,
  64. tdb_off_t off, unsigned int group_bits,
  65. uint64_t hprefix,
  66. unsigned hprefix_bits,
  67. tdb_off_t used[],
  68. size_t num_used,
  69. size_t *num_found,
  70. int (*check)(TDB_DATA, TDB_DATA, void *),
  71. void *private_data);
  72. static bool check_hash_record(struct tdb_context *tdb,
  73. tdb_off_t off,
  74. uint64_t hprefix,
  75. unsigned hprefix_bits,
  76. tdb_off_t used[],
  77. size_t num_used,
  78. size_t *num_found,
  79. int (*check)(TDB_DATA, TDB_DATA, void *),
  80. void *private_data)
  81. {
  82. struct tdb_used_record rec;
  83. if (tdb_read_convert(tdb, off, &rec, sizeof(rec)) == -1)
  84. return false;
  85. if (rec_data_length(&rec)
  86. != sizeof(tdb_off_t) << TDB_SUBLEVEL_HASH_BITS) {
  87. tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_DEBUG_ERROR,
  88. "tdb_check: Bad hash table length %llu vs %llu",
  89. (long long)rec_data_length(&rec),
  90. (long long)sizeof(tdb_off_t)
  91. << TDB_SUBLEVEL_HASH_BITS);
  92. return false;
  93. }
  94. if (rec_key_length(&rec) != 0) {
  95. tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_DEBUG_ERROR,
  96. "tdb_check: Bad hash table key length %llu",
  97. (long long)rec_key_length(&rec));
  98. return false;
  99. }
  100. if (rec_hash(&rec) != 0) {
  101. tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_DEBUG_ERROR,
  102. "tdb_check: Bad hash table hash value %llu",
  103. (long long)rec_hash(&rec));
  104. return false;
  105. }
  106. off += sizeof(rec);
  107. return check_hash_tree(tdb, off,
  108. TDB_SUBLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS,
  109. hprefix, hprefix_bits,
  110. used, num_used, num_found, check, private_data);
  111. }
  112. static int off_cmp(const tdb_off_t *a, const tdb_off_t *b)
  113. {
  114. /* Can overflow an int. */
  115. return *a > *b ? 1
  116. : *a < *b ? -1
  117. : 0;
  118. }
  119. static uint64_t get_bits(uint64_t h, unsigned num, unsigned *used)
  120. {
  121. *used += num;
  122. return (h >> (64 - *used)) & ((1U << num) - 1);
  123. }
  124. static bool check_hash_tree(struct tdb_context *tdb,
  125. tdb_off_t off, unsigned int group_bits,
  126. uint64_t hprefix,
  127. unsigned hprefix_bits,
  128. tdb_off_t used[],
  129. size_t num_used,
  130. size_t *num_found,
  131. int (*check)(TDB_DATA, TDB_DATA, void *),
  132. void *private_data)
  133. {
  134. unsigned int g, b;
  135. const tdb_off_t *hash;
  136. struct tdb_used_record rec;
  137. hash = tdb_access_read(tdb, off,
  138. sizeof(tdb_off_t)
  139. << (group_bits + TDB_HASH_GROUP_BITS),
  140. true);
  141. if (!hash)
  142. return false;
  143. for (g = 0; g < (1 << group_bits); g++) {
  144. const tdb_off_t *group = hash + (g << TDB_HASH_GROUP_BITS);
  145. for (b = 0; b < (1 << TDB_HASH_GROUP_BITS); b++) {
  146. unsigned int bucket, i, used_bits;
  147. uint64_t h;
  148. tdb_off_t *p;
  149. if (group[b] == 0)
  150. continue;
  151. off = group[b] & TDB_OFF_MASK;
  152. p = asearch(&off, used, num_used, off_cmp);
  153. if (!p) {
  154. tdb_logerr(tdb, TDB_ERR_CORRUPT,
  155. TDB_DEBUG_ERROR,
  156. "tdb_check: Invalid offset %llu "
  157. "in hash", (long long)off);
  158. goto fail;
  159. }
  160. /* Mark it invalid. */
  161. *p ^= 1;
  162. (*num_found)++;
  163. if (is_subhash(group[b])) {
  164. uint64_t subprefix;
  165. subprefix = (hprefix
  166. << (group_bits + TDB_HASH_GROUP_BITS))
  167. + g * (1 << TDB_HASH_GROUP_BITS) + b;
  168. if (!check_hash_record(tdb,
  169. group[b] & TDB_OFF_MASK,
  170. subprefix,
  171. hprefix_bits
  172. + group_bits
  173. + TDB_HASH_GROUP_BITS,
  174. used, num_used, num_found,
  175. check, private_data))
  176. goto fail;
  177. continue;
  178. }
  179. /* A normal entry */
  180. /* Does it belong here at all? */
  181. h = hash_record(tdb, off);
  182. used_bits = 0;
  183. if (get_bits(h, hprefix_bits, &used_bits) != hprefix
  184. && hprefix_bits) {
  185. tdb_logerr(tdb, TDB_ERR_CORRUPT,
  186. TDB_DEBUG_ERROR,
  187. "check: bad hash placement"
  188. " 0x%llx vs 0x%llx",
  189. (long long)h, (long long)hprefix);
  190. goto fail;
  191. }
  192. /* Does it belong in this group? */
  193. if (get_bits(h, group_bits, &used_bits) != g) {
  194. tdb_logerr(tdb, TDB_ERR_CORRUPT,
  195. TDB_DEBUG_ERROR,
  196. "check: bad group %llu vs %u",
  197. (long long)h, g);
  198. goto fail;
  199. }
  200. /* Are bucket bits correct? */
  201. bucket = group[b] & TDB_OFF_HASH_GROUP_MASK;
  202. if (get_bits(h, TDB_HASH_GROUP_BITS, &used_bits)
  203. != bucket) {
  204. used_bits -= TDB_HASH_GROUP_BITS;
  205. tdb_logerr(tdb, TDB_ERR_CORRUPT,
  206. TDB_DEBUG_ERROR,
  207. "check: bad bucket %u vs %u",
  208. (unsigned)get_bits(h,
  209. TDB_HASH_GROUP_BITS,
  210. &used_bits),
  211. bucket);
  212. goto fail;
  213. }
  214. /* There must not be any zero entries between
  215. * the bucket it belongs in and this one! */
  216. for (i = bucket;
  217. i != b;
  218. i = (i + 1) % (1 << TDB_HASH_GROUP_BITS)) {
  219. if (group[i] == 0) {
  220. tdb_logerr(tdb, TDB_ERR_CORRUPT,
  221. TDB_DEBUG_ERROR,
  222. "check: bad group placement"
  223. " %u vs %u",
  224. b, bucket);
  225. goto fail;
  226. }
  227. }
  228. if (tdb_read_convert(tdb, off, &rec, sizeof(rec)))
  229. goto fail;
  230. /* Bottom bits must match header. */
  231. if ((h & ((1 << 11)-1)) != rec_hash(&rec)) {
  232. tdb_logerr(tdb, TDB_ERR_CORRUPT,
  233. TDB_DEBUG_ERROR,
  234. "tdb_check: Bad hash magic at"
  235. " offset %llu (0x%llx vs 0x%llx)",
  236. (long long)off,
  237. (long long)h,
  238. (long long)rec_hash(&rec));
  239. goto fail;
  240. }
  241. if (check) {
  242. TDB_DATA key, data;
  243. key.dsize = rec_key_length(&rec);
  244. data.dsize = rec_data_length(&rec);
  245. key.dptr = (void *)tdb_access_read(tdb,
  246. off + sizeof(rec),
  247. key.dsize + data.dsize,
  248. false);
  249. if (!key.dptr)
  250. goto fail;
  251. data.dptr = key.dptr + key.dsize;
  252. if (check(key, data, private_data) != 0)
  253. goto fail;
  254. tdb_access_release(tdb, key.dptr);
  255. }
  256. }
  257. }
  258. tdb_access_release(tdb, hash);
  259. return true;
  260. fail:
  261. tdb_access_release(tdb, hash);
  262. return false;
  263. }
  264. static bool check_hash(struct tdb_context *tdb,
  265. tdb_off_t used[],
  266. size_t num_used, size_t num_flists,
  267. int (*check)(TDB_DATA, TDB_DATA, void *),
  268. void *private_data)
  269. {
  270. /* Free lists also show up as used. */
  271. size_t num_found = num_flists;
  272. if (!check_hash_tree(tdb, offsetof(struct tdb_header, hashtable),
  273. TDB_TOPLEVEL_HASH_BITS-TDB_HASH_GROUP_BITS,
  274. 0, 0, used, num_used, &num_found,
  275. check, private_data))
  276. return false;
  277. if (num_found != num_used) {
  278. tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_DEBUG_ERROR,
  279. "tdb_check: Not all entries are in hash");
  280. return false;
  281. }
  282. return true;
  283. }
  284. static bool check_free(struct tdb_context *tdb,
  285. tdb_off_t off,
  286. const struct tdb_free_record *frec,
  287. tdb_off_t prev, unsigned int flist, unsigned int bucket)
  288. {
  289. if (frec_magic(frec) != TDB_FREE_MAGIC) {
  290. tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_DEBUG_ERROR,
  291. "tdb_check: offset %llu bad magic 0x%llx",
  292. (long long)off, (long long)frec->magic_and_prev);
  293. return false;
  294. }
  295. if (frec_flist(frec) != flist) {
  296. tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_DEBUG_ERROR,
  297. "tdb_check: offset %llu bad freelist %u",
  298. (long long)off, frec_flist(frec));
  299. return false;
  300. }
  301. if (tdb->methods->oob(tdb, off
  302. + frec_len(frec) + sizeof(struct tdb_used_record),
  303. false))
  304. return false;
  305. if (size_to_bucket(frec_len(frec)) != bucket) {
  306. tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_DEBUG_ERROR,
  307. "tdb_check: offset %llu in wrong bucket %u vs %u",
  308. (long long)off,
  309. bucket, size_to_bucket(frec_len(frec)));
  310. return false;
  311. }
  312. if (prev != frec_prev(frec)) {
  313. tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_DEBUG_ERROR,
  314. "tdb_check: offset %llu bad prev %llu vs %llu",
  315. (long long)off,
  316. (long long)prev, (long long)frec_len(frec));
  317. return false;
  318. }
  319. return true;
  320. }
  321. static bool check_free_list(struct tdb_context *tdb,
  322. tdb_off_t flist_off,
  323. unsigned flist_num,
  324. tdb_off_t free[],
  325. size_t num_free,
  326. size_t *num_found)
  327. {
  328. struct tdb_freelist flist;
  329. tdb_off_t h;
  330. unsigned int i;
  331. if (tdb_read_convert(tdb, flist_off, &flist, sizeof(flist)) == -1)
  332. return false;
  333. if (rec_magic(&flist.hdr) != TDB_MAGIC
  334. || rec_key_length(&flist.hdr) != 0
  335. || rec_data_length(&flist.hdr) != sizeof(flist) - sizeof(flist.hdr)
  336. || rec_hash(&flist.hdr) != 1) {
  337. tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_DEBUG_ERROR,
  338. "tdb_check: Invalid header on free list");
  339. return false;
  340. }
  341. for (i = 0; i < TDB_FREE_BUCKETS; i++) {
  342. tdb_off_t off, prev = 0, *p;
  343. struct tdb_free_record f;
  344. h = bucket_off(flist_off, i);
  345. for (off = tdb_read_off(tdb, h); off; off = f.next) {
  346. if (off == TDB_OFF_ERR)
  347. return false;
  348. if (tdb_read_convert(tdb, off, &f, sizeof(f)))
  349. return false;
  350. if (!check_free(tdb, off, &f, prev, flist_num, i))
  351. return false;
  352. /* FIXME: Check hash bits */
  353. p = asearch(&off, free, num_free, off_cmp);
  354. if (!p) {
  355. tdb_logerr(tdb, TDB_ERR_CORRUPT,
  356. TDB_DEBUG_ERROR,
  357. "tdb_check: Invalid offset"
  358. " %llu in free table",
  359. (long long)off);
  360. return false;
  361. }
  362. /* Mark it invalid. */
  363. *p ^= 1;
  364. (*num_found)++;
  365. prev = off;
  366. }
  367. }
  368. return true;
  369. }
  370. /* Slow, but should be very rare. */
  371. size_t dead_space(struct tdb_context *tdb, tdb_off_t off)
  372. {
  373. size_t len;
  374. for (len = 0; off + len < tdb->map_size; len++) {
  375. char c;
  376. if (tdb->methods->read(tdb, off, &c, 1))
  377. return 0;
  378. if (c != 0 && c != 0x43)
  379. break;
  380. }
  381. return len;
  382. }
  383. static bool check_linear(struct tdb_context *tdb,
  384. tdb_off_t **used, size_t *num_used,
  385. tdb_off_t **free, size_t *num_free,
  386. tdb_off_t recovery)
  387. {
  388. tdb_off_t off;
  389. tdb_len_t len;
  390. bool found_recovery = false;
  391. for (off = sizeof(struct tdb_header); off < tdb->map_size; off += len) {
  392. union {
  393. struct tdb_used_record u;
  394. struct tdb_free_record f;
  395. struct tdb_recovery_record r;
  396. } rec;
  397. /* r is larger: only get that if we need to. */
  398. if (tdb_read_convert(tdb, off, &rec, sizeof(rec.f)) == -1)
  399. return false;
  400. /* If we crash after ftruncate, we can get zeroes or fill. */
  401. if (rec.r.magic == TDB_RECOVERY_INVALID_MAGIC
  402. || rec.r.magic == 0x4343434343434343ULL) {
  403. if (tdb_read_convert(tdb, off, &rec, sizeof(rec.r)))
  404. return false;
  405. if (recovery == off) {
  406. found_recovery = true;
  407. len = sizeof(rec.r) + rec.r.max_len;
  408. } else {
  409. len = dead_space(tdb, off);
  410. if (len < sizeof(rec.r)) {
  411. tdb_logerr(tdb, TDB_ERR_CORRUPT,
  412. TDB_DEBUG_ERROR,
  413. "tdb_check: invalid dead"
  414. " space at %zu",
  415. (size_t)off);
  416. return false;
  417. }
  418. tdb_logerr(tdb, TDB_SUCCESS, TDB_DEBUG_WARNING,
  419. "Dead space at %zu-%zu (of %zu)",
  420. (size_t)off, (size_t)(off + len),
  421. (size_t)tdb->map_size);
  422. }
  423. } else if (rec.r.magic == TDB_RECOVERY_MAGIC) {
  424. if (tdb_read_convert(tdb, off, &rec, sizeof(rec.r)))
  425. return false;
  426. if (recovery != off) {
  427. tdb_logerr(tdb, TDB_ERR_CORRUPT,
  428. TDB_DEBUG_ERROR,
  429. "tdb_check: unexpected recovery"
  430. " record at offset %zu",
  431. (size_t)off);
  432. return false;
  433. }
  434. if (rec.r.len > rec.r.max_len) {
  435. tdb_logerr(tdb, TDB_ERR_CORRUPT,
  436. TDB_DEBUG_ERROR,
  437. "tdb_check: invalid recovery length"
  438. " %zu", (size_t)rec.r.len);
  439. return false;
  440. }
  441. if (rec.r.eof > tdb->map_size) {
  442. tdb_logerr(tdb, TDB_ERR_CORRUPT,
  443. TDB_DEBUG_ERROR,
  444. "tdb_check: invalid old EOF"
  445. " %zu", (size_t)rec.r.eof);
  446. return false;
  447. }
  448. found_recovery = true;
  449. len = sizeof(rec.r) + rec.r.max_len;
  450. } else if (frec_magic(&rec.f) == TDB_FREE_MAGIC
  451. || frec_magic(&rec.f) == TDB_COALESCING_MAGIC) {
  452. len = sizeof(rec.u) + frec_len(&rec.f);
  453. if (off + len > tdb->map_size) {
  454. tdb_logerr(tdb, TDB_ERR_CORRUPT,
  455. TDB_DEBUG_ERROR,
  456. "tdb_check: free overlength %llu"
  457. " at offset %llu",
  458. (long long)len, (long long)off);
  459. return false;
  460. }
  461. /* This record is free! */
  462. if (frec_magic(&rec.f) == TDB_FREE_MAGIC
  463. && !append(free, num_free, off))
  464. return false;
  465. } else {
  466. uint64_t klen, dlen, extra;
  467. /* This record is used! */
  468. if (rec_magic(&rec.u) != TDB_MAGIC) {
  469. tdb_logerr(tdb, TDB_ERR_CORRUPT,
  470. TDB_DEBUG_ERROR,
  471. "tdb_check: Bad magic 0x%llx"
  472. " at offset %zu",
  473. (long long)rec_magic(&rec.u),
  474. (size_t)off);
  475. return false;
  476. }
  477. if (!append(used, num_used, off))
  478. return false;
  479. klen = rec_key_length(&rec.u);
  480. dlen = rec_data_length(&rec.u);
  481. extra = rec_extra_padding(&rec.u);
  482. len = sizeof(rec.u) + klen + dlen + extra;
  483. if (off + len > tdb->map_size) {
  484. tdb_logerr(tdb, TDB_ERR_CORRUPT,
  485. TDB_DEBUG_ERROR,
  486. "tdb_check: used overlength %llu"
  487. " at offset %llu",
  488. (long long)len, (long long)off);
  489. return false;
  490. }
  491. if (len < sizeof(rec.f)) {
  492. tdb_logerr(tdb, TDB_ERR_CORRUPT,
  493. TDB_DEBUG_ERROR,
  494. "tdb_check: too short record %llu"
  495. " at %llu",
  496. (long long)len, (long long)off);
  497. return false;
  498. }
  499. }
  500. }
  501. /* We must have found recovery area if there was one. */
  502. if (recovery != 0 && !found_recovery) {
  503. tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_DEBUG_ERROR,
  504. "tdb_check: expected a recovery area at %zu",
  505. (size_t)recovery);
  506. return false;
  507. }
  508. return true;
  509. }
  510. int tdb_check(struct tdb_context *tdb,
  511. int (*check)(TDB_DATA key, TDB_DATA data, void *private_data),
  512. void *private_data)
  513. {
  514. tdb_off_t *free = NULL, *used = NULL, flist, recovery;
  515. size_t num_free = 0, num_used = 0, num_found = 0, num_flists = 0;
  516. if (tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_WAIT, false) != 0)
  517. return -1;
  518. if (tdb_lock_expand(tdb, F_RDLCK) != 0) {
  519. tdb_allrecord_unlock(tdb, F_RDLCK);
  520. return -1;
  521. }
  522. if (!check_header(tdb, &recovery))
  523. goto fail;
  524. /* First we do a linear scan, checking all records. */
  525. if (!check_linear(tdb, &used, &num_used, &free, &num_free, recovery))
  526. goto fail;
  527. for (flist = first_flist(tdb); flist; flist = next_flist(tdb, flist)) {
  528. if (flist == TDB_OFF_ERR)
  529. goto fail;
  530. if (!check_free_list(tdb, flist, num_flists, free, num_free,
  531. &num_found))
  532. goto fail;
  533. num_flists++;
  534. }
  535. /* FIXME: Check key uniqueness? */
  536. if (!check_hash(tdb, used, num_used, num_flists, check, private_data))
  537. goto fail;
  538. if (num_found != num_free) {
  539. tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_DEBUG_ERROR,
  540. "tdb_check: Not all entries are in free table");
  541. return -1;
  542. }
  543. tdb_allrecord_unlock(tdb, F_RDLCK);
  544. tdb_unlock_expand(tdb, F_RDLCK);
  545. return 0;
  546. fail:
  547. tdb_allrecord_unlock(tdb, F_RDLCK);
  548. tdb_unlock_expand(tdb, F_RDLCK);
  549. return -1;
  550. }