2 Unix SMB/Netbios implementation.
4 Samba database functions
5 Copyright (C) Andrew Tridgell 1999
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
36 #define TDB_VERSION (0x26011967 + 0)
38 #define MIN_REC_SIZE (2*sizeof(struct list_struct) + TDB_ALIGN)
39 #define DEFAULT_HASH_SIZE 512
40 #define TDB_PAGE_SIZE 0x1000
41 #define FREELIST_TOP (sizeof(struct tdb_header))
43 /* the body of the database is made of one list_struct for the free space
44 plus a separate data list for each hash value */
46 tdb_len rec_len; /* total byte length of record */
47 tdb_off next; /* offset of the next record in the list */
48 tdb_len key_len; /* byte length of key */
49 tdb_len data_len; /* byte length of data */
50 unsigned full_hash; /* the full 32 bit hash of the key */
52 the following union is implied
63 /* a null data record - useful for error returns */
64 static TDB_DATA null_data;
67 /* like strdup but for memory */
68 static char *memdup(char *d, int size)
71 ret = (char *)malloc(size);
72 if (!ret) return NULL;
79 /* a byte range locking function - return 0 on success
80 this functions locks/unlocks 1 byte at the specified offset */
81 static int tdb_brlock(TDB_CONTEXT *tdb, tdb_off offset, int set)
86 fl.l_type = set?F_WRLCK:F_UNLCK;
87 fl.l_whence = SEEK_SET;
92 if (fcntl(tdb->fd, F_SETLKW, &fl) != 0) return -1;
98 /* the hash algorithm - turn a key into an integer
99 This is based on the hash agorithm from gdbm */
100 static unsigned tdb_hash(TDB_DATA *key)
102 unsigned value; /* Used to compute the hash value. */
103 unsigned i; /* Used to cycle through random values. */
105 /* Set the initial value from the key size. */
106 value = 0x238F13AF * key->dsize;
107 for (i=0; i < key->dsize; i++) {
108 value = (value + (key->dptr[i] << (i*5 % 24)));
111 value = (1103515243 * value + 12345);
116 /* find the top of the hash chain for an open database */
117 static tdb_off tdb_hash_top(TDB_CONTEXT *tdb, unsigned hash)
120 hash = hash % tdb->header.hash_size;
121 ret = FREELIST_TOP + (hash+1)*sizeof(tdb_off);
126 /* check for an out of bounds access - if it is out of bounds then
127 see if the database has been expanded by someone else and expand
129 static int tdb_oob(TDB_CONTEXT *tdb, tdb_off offset)
132 if (offset < tdb->map_size) return 0;
135 if (st.st_size <= tdb->map_size) return -1;
139 munmap(tdb->map_ptr, tdb->map_size);
144 tdb->map_size = st.st_size;
146 tdb->map_ptr = (void *)mmap(NULL, tdb->map_size,
147 tdb->read_only?PROT_READ:PROT_READ|PROT_WRITE,
148 MAP_SHARED | MAP_FILE, tdb->fd, 0);
154 /* write a lump of data at a specified offset */
155 static int tdb_write(TDB_CONTEXT *tdb, tdb_off offset, char *buf, tdb_len len)
157 if (tdb_oob(tdb, offset + len) != 0) {
158 /* oops - trying to write beyond the end of the database! */
160 printf("write error of length %u at offset %u (max %u)\n",
161 len, offset, tdb->map_size);
167 memcpy(offset + (char *)tdb->map_ptr, buf, len);
169 lseek(tdb->fd, offset, SEEK_SET);
170 if (write(tdb->fd, buf, len) != (ssize_t)len) {
177 /* read a lump of data at a specified offset */
178 static int tdb_read(TDB_CONTEXT *tdb, tdb_off offset, char *buf, tdb_len len)
180 if (tdb_oob(tdb, offset + len) != 0) {
181 /* oops - trying to read beyond the end of the database! */
183 printf("read error of length %u at offset %u (max %u)\n",
184 len, offset, tdb->map_size);
190 memcpy(buf, offset + (char *)tdb->map_ptr, len);
192 lseek(tdb->fd, offset, SEEK_SET);
193 if (read(tdb->fd, buf, len) != (ssize_t)len) {
201 /* read a lump of data, allocating the space for it */
202 static char *tdb_alloc_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_len len)
206 buf = (char *)malloc(len);
208 if (tdb_read(tdb, offset, buf, len) == -1) {
216 /* expand the database at least length bytes by expanding the
217 underlying file and doing the mmap again if necessary */
218 static int tdb_expand(TDB_CONTEXT *tdb, tdb_off length)
220 struct list_struct rec;
224 /* always make room for at least 10 more records */
227 /* and round the database up to a multiple of TDB_PAGE_SIZE */
228 length = ((tdb->map_size + length + TDB_PAGE_SIZE) & ~(TDB_PAGE_SIZE - 1)) - tdb->map_size;
230 /* expand the file itself */
231 lseek(tdb->fd, tdb->map_size + length - 1, SEEK_SET);
232 if (write(tdb->fd, &b, 1) != 1) return -1;
234 /* form a new freelist record */
235 offset = FREELIST_TOP;
236 rec.rec_len = length - sizeof(rec);
237 if (tdb_read(tdb, offset, (char *)&rec.next, sizeof(rec.next)) == -1) return -1;
241 munmap(tdb->map_ptr, tdb->map_size);
246 tdb->map_size += length;
249 if (tdb_write(tdb, tdb->map_size - length, (char *)&rec, sizeof(rec)) == -1) return -1;
251 /* link it into the free list */
252 ptr = tdb->map_size - length;
253 if (tdb_write(tdb, offset, (char *)&ptr, sizeof(ptr)) == -1) return -1;
256 tdb->map_ptr = (void *)mmap(NULL, tdb->map_size,
257 PROT_READ|PROT_WRITE,
258 MAP_SHARED | MAP_FILE, tdb->fd, 0);
262 printf("expanded database by %u bytes\n", length);
268 /* allocate some space from the free list. The offset returned points
269 to a unconnected list_struct within the database with room for at
270 least length bytes of total data
272 0 is returned if the space could not be allocated
274 static tdb_off tdb_allocate(TDB_CONTEXT *tdb, tdb_len length)
276 tdb_off offset, rec_ptr, last_ptr;
277 struct list_struct rec, lastrec, newrec;
281 offset = FREELIST_TOP;
283 /* read in the freelist top */
284 if (tdb_read(tdb, offset, (char *)&rec_ptr, sizeof(rec_ptr)) == -1) {
288 /* keep looking until we find a freelist record that is big
291 if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) {
295 if (rec.rec_len >= length) {
296 /* found it - now possibly split it up */
297 if (rec.rec_len > length + MIN_REC_SIZE) {
298 length = (length + TDB_ALIGN) & ~(TDB_ALIGN-1);
300 newrec.rec_len = rec.rec_len - (sizeof(rec) + length);
301 newrec.next = rec.next;
303 rec.rec_len = length;
304 rec.next = rec_ptr + sizeof(rec) + rec.rec_len;
306 if (tdb_write(tdb, rec.next, (char *)&newrec,
307 sizeof(newrec)) == -1) {
311 if (tdb_write(tdb, rec_ptr, (char *)&rec,
312 sizeof(rec)) == -1) {
317 /* remove it from the list */
319 offset = FREELIST_TOP;
321 if (tdb_write(tdb, offset, (char *)&rec.next,
322 sizeof(tdb_off)) == -1) {
326 lastrec.next = rec.next;
327 if (tdb_write(tdb, last_ptr, (char *)&lastrec,
328 sizeof(lastrec)) == -1) {
333 /* all done - return the new record offset */
335 printf("allocated %u bytes in database\n", rec.rec_len);
340 /* move to the next record */
346 /* we didn't find enough space. See if we can expand the database and if we can
348 if (tdb_expand(tdb, length + sizeof(rec)) == 0) goto again;
352 printf("tdb_allocate failed for size %u\n", length);
357 /* initialise a new database with a specified hash size */
358 static int tdb_new_database(TDB_CONTEXT *tdb, int hash_size)
360 struct tdb_header header;
364 /* create the header */
365 header.version = TDB_VERSION;
366 header.hash_size = hash_size;
367 if (write(tdb->fd, &header, sizeof(header)) != sizeof(header)) return -1;
369 /* the freelist and hash pointers */
371 for (i=0;i<hash_size+1;i++) {
372 if (write(tdb->fd, &offset, sizeof(tdb_off)) != sizeof(tdb_off)) return -1;
376 printf("initialised database of hash_size %u available space %u\n",
377 hash_size, rec.rec_len);
383 /* update an entry in place - this only works if the new data size
384 is <= the old data size and the key exists.
387 int tdb_update(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf)
390 tdb_off offset, rec_ptr;
391 struct list_struct rec;
394 /* find which hash bucket it is in */
395 hash = tdb_hash(&key);
397 /* find the top of the hash chain */
398 offset = tdb_hash_top(tdb, hash);
400 /* read in the hash top */
401 if (tdb_read(tdb, offset, (char *)&rec_ptr, sizeof(rec_ptr)) == -1) {
405 /* keep looking until we find the right record */
407 if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) {
411 if (hash == rec.full_hash && key.dsize == rec.key_len) {
412 /* a very likely hit - read the full key */
413 data = tdb_alloc_read(tdb, rec_ptr + sizeof(rec),
415 if (!data) goto fail;
417 if (memcmp(key.dptr, data, key.dsize) == 0) {
419 if (rec.rec_len < key.dsize + dbuf.dsize) {
420 /* the update won't fit! */
423 if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len,
424 dbuf.dptr, dbuf.dsize) == -1) {
427 if (dbuf.dsize != rec.data_len) {
428 rec.data_len = dbuf.dsize;
429 if (tdb_write(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) {
442 /* move to the next record */
446 /* we didn't find it */
448 if (data) free(data);
453 /* find an entry in the database given a key */
454 TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key)
457 tdb_off offset, rec_ptr;
458 struct list_struct rec;
462 /* find which hash bucket it is in */
463 hash = tdb_hash(&key);
465 /* find the top of the hash chain */
466 offset = tdb_hash_top(tdb, hash);
468 /* read in the hash top */
469 if (tdb_read(tdb, offset, (char *)&rec_ptr, sizeof(rec_ptr)) == -1) {
473 /* keep looking until we find the right record */
475 if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) {
479 if (hash == rec.full_hash && key.dsize == rec.key_len) {
480 /* a very likely hit - read the full record */
481 data = tdb_alloc_read(tdb, rec_ptr + sizeof(rec),
482 rec.key_len + rec.data_len);
487 if (memcmp(key.dptr, data, key.dsize) == 0) {
488 /* a definate match */
489 ret.dptr = (char *)memdup(data + rec.key_len, rec.data_len);
490 ret.dsize = rec.data_len;
499 /* move to the next record */
503 /* we didn't find it */
507 /* check if an entry in the database exists
509 note that 1 is returned if the key is found and 0 is returned if not found
510 this doesn't match the conventions in the rest of this module, but is
513 int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key)
516 tdb_off offset, rec_ptr;
517 struct list_struct rec;
520 /* find which hash bucket it is in */
521 hash = tdb_hash(&key);
523 /* find the top of the hash chain */
524 offset = tdb_hash_top(tdb, hash);
526 /* read in the hash top */
527 if (tdb_read(tdb, offset, (char *)&rec_ptr, sizeof(rec_ptr)) == -1) {
531 /* keep looking until we find the right record */
533 if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) {
537 if (hash == rec.full_hash && key.dsize == rec.key_len) {
538 /* a very likely hit - read the full record */
539 data = tdb_alloc_read(tdb, rec_ptr + sizeof(rec),
540 rec.key_len + rec.data_len);
545 if (memcmp(key.dptr, data, key.dsize) == 0) {
546 /* a definate match */
555 /* move to the next record */
559 /* we didn't find it */
564 /* traverse the entire database - calling fn(tdb, key, data) on each element.
565 return -1 on error or the record count traversed
566 if fn is NULL then it is not called
567 a non-zero return value from fn() indicates that the traversal should stop
569 int tdb_traverse(TDB_CONTEXT *tdb, int (*fn)(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf))
573 tdb_off offset, rec_ptr;
574 struct list_struct rec;
578 /* loop over all hash chains */
579 for (h = 0; h < tdb->header.hash_size; h++) {
580 /* read in the hash top */
581 offset = tdb_hash_top(tdb, h);
582 if (tdb_read(tdb, offset, (char *)&rec_ptr, sizeof(rec_ptr)) == -1) {
586 /* traverse all records for this hash */
588 if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) {
592 /* now read the full record */
593 data = tdb_alloc_read(tdb, rec_ptr + sizeof(rec),
594 rec.key_len + rec.data_len);
600 key.dsize = rec.key_len;
601 dbuf.dptr = data + rec.key_len;
602 dbuf.dsize = rec.data_len;
605 if (fn && fn(tdb, key, dbuf) != 0) {
606 /* they want us to stop traversing */
614 /* move to the next record */
620 /* return the number traversed */
625 /* find the first entry in the database and return its key */
626 TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb)
628 tdb_off offset, rec_ptr;
629 struct list_struct rec;
633 /* look for a non-empty hash chain */
634 for (hash = 0, rec_ptr = 0;
635 hash < tdb->header.hash_size && rec_ptr == 0;
637 /* find the top of the hash chain */
638 offset = tdb_hash_top(tdb, hash);
640 /* read in the hash top */
641 if (tdb_read(tdb, offset, (char *)&rec_ptr, sizeof(rec_ptr)) == -1) {
646 if (rec_ptr == 0) return null_data;
648 /* we've found a non-empty chain, now read the record */
649 if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) {
653 /* allocate and read the key space */
654 ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec), rec.key_len);
655 ret.dsize = rec.key_len;
660 /* find the next entry in the database, returning its key */
661 TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key)
664 tdb_off offset, rec_ptr;
665 struct list_struct rec;
669 /* find which hash bucket it is in */
670 hash = tdb_hash(&key);
672 /* find the top of the hash chain */
673 offset = tdb_hash_top(tdb, hash);
675 /* read in the hash top */
676 if (tdb_read(tdb, offset, (char *)&rec_ptr, sizeof(rec_ptr)) == -1) {
680 /* look until we find the right record */
682 if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) {
686 if (hash == rec.full_hash && key.dsize == rec.key_len) {
687 /* a very likely hit - read the full key */
688 data = tdb_alloc_read(tdb, rec_ptr + sizeof(rec),
694 if (memcmp(key.dptr, data, key.dsize) == 0) {
695 /* a definate match - we want the next
696 record after this one */
699 if (rec_ptr == 0) goto next_hash;
707 /* move to the next record */
713 printf("tdb_nextkey trying next hash from %u\n",
714 hash % tdb->header.hash_size);
717 h = hash % tdb->header.hash_size;
718 if (h == tdb->header.hash_size - 1) return null_data;
720 /* look for a non-empty hash chain */
721 for (h = h+1, rec_ptr = 0;
722 h < tdb->header.hash_size && rec_ptr == 0;
725 printf("tdb_nextkey trying bucket %u\n",h);
727 /* find the top of the hash chain */
728 offset = tdb_hash_top(tdb, h);
730 /* read in the hash top */
731 if (tdb_read(tdb, offset, (char *)&rec_ptr, sizeof(rec_ptr)) == -1) {
736 if (rec_ptr == 0) return null_data;
740 /* we've found a non-empty chain, now read the record */
741 if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) {
746 printf("tdb_nextkey found hash 0x%08x in bucket %u from bucket %u\n",
747 rec.full_hash, rec.full_hash % tdb->header.hash_size,
748 hash % tdb->header.hash_size);
750 /* allocate and read the key space */
751 ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec), rec.key_len);
752 ret.dsize = rec.key_len;
757 /* delete an entry in the database given a key */
758 int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key)
761 tdb_off offset, rec_ptr, last_ptr;
762 struct list_struct rec, lastrec;
767 /* find which hash bucket it is in */
768 hash = tdb_hash(&key);
770 /* find the top of the hash chain */
771 offset = tdb_hash_top(tdb, hash);
773 /* read in the hash top */
774 if (tdb_read(tdb, offset, (char *)&rec_ptr, sizeof(rec_ptr)) == -1) {
780 /* keep looking until we find the right record */
782 if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) {
786 if (hash == rec.full_hash && key.dsize == rec.key_len) {
787 /* a very likely hit - read the record and full key */
788 data = tdb_alloc_read(tdb, rec_ptr + sizeof(rec),
794 if (memcmp(key.dptr, data, key.dsize) == 0) {
795 /* a definate match - delete it */
797 printf("deleting record with hash 0x%08x in bucket %u\n",
798 hash, hash % tdb->header.hash_size);
801 offset = tdb_hash_top(tdb, hash);
802 if (tdb_write(tdb, offset, (char *)&rec.next, sizeof(rec.next)) == -1) {
806 lastrec.next = rec.next;
807 if (tdb_write(tdb, last_ptr, (char *)&lastrec, sizeof(lastrec)) == -1) {
811 /* and recover the space */
812 offset = FREELIST_TOP;
813 if (tdb_read(tdb, offset, (char *)&rec.next, sizeof(rec.next)) == -1) {
816 if (tdb_write(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) {
819 if (tdb_write(tdb, offset, (char *)&rec_ptr, sizeof(rec_ptr)) == -1) {
823 /* yipee - all done */
825 tdb_writeunlock(tdb);
833 /* move to the next record */
841 tdb_writeunlock(tdb);
846 /* store an element in the database, replacing any existing element
849 return 0 on success, -1 on failure
851 int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
853 struct list_struct rec;
856 tdb_off rec_ptr, offset;
858 /* check for it existing */
859 if (flag == TDB_INSERT && tdb_exists(tdb, key)) {
865 /* first try in-place update */
866 if (flag != TDB_INSERT && tdb_update(tdb, key, dbuf) == 0) {
867 tdb_writeunlock(tdb);
871 /* delete any existing record - if it doesn't exist we don't care */
872 if (flag != TDB_INSERT) {
873 tdb_delete(tdb, key);
876 /* find which hash bucket it is in */
877 hash = tdb_hash(&key);
879 rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize);
884 /* read the newly created record */
885 if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) {
889 /* find the top of the hash chain */
890 offset = tdb_hash_top(tdb, hash);
892 /* read in the hash top diretcly into our next pointer */
893 if (tdb_read(tdb, offset, (char *)&rec.next, sizeof(rec.next)) == -1) {
897 rec.key_len = key.dsize;
898 rec.data_len = dbuf.dsize;
899 rec.full_hash = hash;
901 /* write the new record */
902 if (tdb_write(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) goto fail;
903 if (tdb_write(tdb, rec_ptr + sizeof(rec), key.dptr, key.dsize) == -1) goto fail;
904 if (tdb_write(tdb, rec_ptr + sizeof(rec) + key.dsize, dbuf.dptr, dbuf.dsize) == -1) goto fail;
906 /* and point the top of the hash chain at it */
907 if (tdb_write(tdb, offset, (char *)&rec_ptr, sizeof(rec_ptr)) == -1) goto fail;
909 tdb_writeunlock(tdb);
911 printf("added record with hash 0x%08x in bucket %u\n", hash, hash % tdb->header.hash_size);
917 printf("store failed for hash 0x%08x in bucket %u\n", hash, hash % tdb->header.hash_size);
919 if (data) free(data);
920 tdb_writeunlock(tdb);
925 /* open the database, creating it if necessary
927 The flags and mode are passed straight to the open call on the database
928 file. A flags value of O_WRONLY is invalid
930 The hash size is advisory, use zero for a default value.
932 return is NULL on error
934 TDB_CONTEXT *tdb_open(char *name, int hash_size, int flags, mode_t mode)
936 TDB_CONTEXT tdb, *ret;
937 struct tdb_header header;
944 if ((flags & O_ACCMODE) == O_WRONLY) goto fail;
946 if (hash_size == 0) hash_size = DEFAULT_HASH_SIZE;
948 tdb.fd = open(name, flags, mode);
949 if (tdb.fd == -1) goto fail;
951 if (read(tdb.fd, &header, sizeof(header)) != sizeof(header) ||
952 header.version != TDB_VERSION) {
953 /* its not a valid database - possibly initialise it */
954 if (!(flags & O_CREAT)) {
957 if (tdb_new_database(&tdb, hash_size) == -1) goto fail;
959 lseek(tdb.fd, 0, SEEK_SET);
960 if (read(tdb.fd, &header, sizeof(header)) != sizeof(header)) goto fail;
965 /* map the database and fill in the return structure */
966 tdb.name = (char *)strdup(name);
967 tdb.map_size = st.st_size;
968 tdb.read_only = ((flags & O_ACCMODE) == O_RDONLY);
970 tdb.map_ptr = (void *)mmap(NULL, st.st_size,
971 tdb.read_only? PROT_READ : PROT_READ|PROT_WRITE,
972 MAP_SHARED | MAP_FILE, tdb.fd, 0);
976 ret = (TDB_CONTEXT *)malloc(sizeof(tdb));
982 printf("mapped database of hash_size %u map_size=%u\n",
983 hash_size, db.map_size);
989 if (tdb.name) free(tdb.name);
990 if (tdb.fd != -1) close(tdb.fd);
991 if (tdb.map_ptr) munmap(tdb.map_ptr, tdb.map_size);
996 /* close a database */
997 int tdb_close(TDB_CONTEXT *tdb)
1001 if (tdb->name) free(tdb->name);
1002 if (tdb->fd != -1) close(tdb->fd);
1003 if (tdb->map_ptr) munmap(tdb->map_ptr, tdb->map_size);
1005 memset(tdb, 0, sizeof(*tdb));
1011 /* lock the database. If we already have it locked then don't do anything */
1012 int tdb_writelock(TDB_CONTEXT *tdb)
1014 return tdb_brlock(tdb, 0, 1);
1017 /* unlock the database. */
1018 int tdb_writeunlock(TDB_CONTEXT *tdb)
1020 return tdb_brlock(tdb, 0, 0);
1023 /* lock one hash chain. This is meant to be used to reduce locking
1024 contention - it cannot guarantee how many records will be locked */
1025 int tdb_lockchain(TDB_CONTEXT *tdb, TDB_DATA key)
1027 return tdb_brlock(tdb, tdb_hash_top(tdb, tdb_hash(&key)), 1);
1031 /* unlock one hash chain */
1032 int tdb_unlockchain(TDB_CONTEXT *tdb, TDB_DATA key)
1034 return tdb_brlock(tdb, tdb_hash_top(tdb, tdb_hash(&key)), 0);