3 * Copyright (C) Andrew Tridgell 1999
5 * Redistribution and use in source and binary forms are permitted
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms AND provided that this software or
8 * any derived work is only used as part of the PPP daemon (pppd)
9 * and related utilities.
10 * The name of the author may not be used to endorse or promote products
11 * derived from this software without specific prior written permission.
12 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
13 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
14 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16 * Note: this software is also available under the Gnu Public License
31 #define TDB_VERSION (0x26011967 + 1)
32 #define TDB_MAGIC (0x26011999U)
33 #define TDB_FREE_MAGIC (~TDB_MAGIC)
35 #define MIN_REC_SIZE (2*sizeof(struct list_struct) + TDB_ALIGN)
36 #define DEFAULT_HASH_SIZE 128
37 #define TDB_PAGE_SIZE 0x2000
38 #define TDB_LEN_MULTIPLIER 10
39 #define FREELIST_TOP (sizeof(struct tdb_header))
47 #define LIST_LOCK_BASE 1024
49 #define BUCKET(hash) ((hash) % tdb->header.hash_size)
55 /* the body of the database is made of one list_struct for the free space
56 plus a separate data list for each hash value */
58 tdb_len rec_len; /* total byte length of record */
59 tdb_off next; /* offset of the next record in the list */
60 tdb_len key_len; /* byte length of key */
61 tdb_len data_len; /* byte length of data */
62 unsigned full_hash; /* the full 32 bit hash of the key */
63 unsigned magic; /* try to catch errors */
65 the following union is implied
76 /* a null data record - useful for error returns */
77 static TDB_DATA null_data;
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,
82 int set, int rw_type, int lck_type)
89 if (tdb->fd == -1) return 0; /* for in memory tdb */
91 if (tdb->read_only) return -1;
93 fl.l_type = set==LOCK_SET?rw_type:F_UNLCK;
94 fl.l_whence = SEEK_SET;
99 if (fcntl(tdb->fd, lck_type, &fl) != 0) {
101 if (lck_type == F_SETLKW) {
102 printf("lock %d failed at %d (%s)\n",
103 set, offset, strerror(errno));
106 tdb->ecode = TDB_ERR_LOCK;
113 /* lock a list in the database. list -1 is the alloc list */
114 static int tdb_lock(TDB_CONTEXT *tdb, int list)
116 if (list < -1 || list >= (int)tdb->header.hash_size) {
118 printf("bad list %d\n", list);
122 if (tdb->locked[list+1] == 0) {
123 if (tdb_brlock(tdb, LIST_LOCK_BASE + 4*list, LOCK_SET,
124 F_WRLCK, F_SETLKW) != 0) {
128 tdb->locked[list+1]++;
132 /* unlock the database. */
133 static int tdb_unlock(TDB_CONTEXT *tdb, int list)
135 if (list < -1 || list >= (int)tdb->header.hash_size) {
137 printf("bad unlock list %d\n", list);
142 if (tdb->locked[list+1] == 0) {
144 printf("not locked %d\n", list);
146 tdb->ecode = TDB_ERR_LOCK;
149 if (tdb->locked[list+1] == 1) {
150 if (tdb_brlock(tdb, LIST_LOCK_BASE + 4*list, LOCK_CLEAR,
151 F_WRLCK, F_SETLKW) != 0) {
155 tdb->locked[list+1]--;
159 /* the hash algorithm - turn a key into an integer
160 This is based on the hash agorithm from gdbm */
161 static unsigned tdb_hash(TDB_DATA *key)
163 unsigned value; /* Used to compute the hash value. */
164 unsigned i; /* Used to cycle through random values. */
166 /* Set the initial value from the key size. */
167 value = 0x238F13AF * key->dsize;
168 for (i=0; i < key->dsize; i++) {
169 value = (value + (key->dptr[i] << (i*5 % 24)));
172 value = (1103515243 * value + 12345);
177 /* find the top of the hash chain for an open database */
178 static tdb_off tdb_hash_top(TDB_CONTEXT *tdb, unsigned hash)
182 ret = FREELIST_TOP + (hash+1)*sizeof(tdb_off);
187 /* check for an out of bounds access - if it is out of bounds then
188 see if the database has been expanded by someone else and expand
190 static int tdb_oob(TDB_CONTEXT *tdb, tdb_off offset)
193 if ((offset <= tdb->map_size) || (tdb->fd == -1)) return 0;
196 if (st.st_size <= (ssize_t)offset) {
197 tdb->ecode = TDB_ERR_IO;
203 munmap(tdb->map_ptr, tdb->map_size);
208 tdb->map_size = st.st_size;
210 tdb->map_ptr = (void *)mmap(NULL, tdb->map_size,
211 tdb->read_only?PROT_READ:PROT_READ|PROT_WRITE,
212 MAP_SHARED | MAP_FILE, tdb->fd, 0);
213 if (tdb->map_ptr == MAP_FAILED) {
221 /* write a lump of data at a specified offset */
222 static int tdb_write(TDB_CONTEXT *tdb, tdb_off offset, const char *buf, tdb_len len)
224 if (tdb_oob(tdb, offset + len) != 0) {
225 /* oops - trying to write beyond the end of the database! */
230 memcpy(offset + (char *)tdb->map_ptr, buf, len);
232 if (lseek(tdb->fd, offset, SEEK_SET) != offset ||
233 write(tdb->fd, buf, len) != (ssize_t)len) {
234 tdb->ecode = TDB_ERR_IO;
241 /* read a lump of data at a specified offset */
242 static int tdb_read(TDB_CONTEXT *tdb, tdb_off offset, char *buf, tdb_len len)
244 if (tdb_oob(tdb, offset + len) != 0) {
245 /* oops - trying to read beyond the end of the database! */
250 memcpy(buf, offset + (char *)tdb->map_ptr, len);
252 if (lseek(tdb->fd, offset, SEEK_SET) != offset ||
253 read(tdb->fd, buf, len) != (ssize_t)len) {
254 tdb->ecode = TDB_ERR_IO;
262 /* read a lump of data, allocating the space for it */
263 static char *tdb_alloc_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_len len)
267 buf = (char *)malloc(len);
270 tdb->ecode = TDB_ERR_OOM;
274 if (tdb_read(tdb, offset, buf, len) == -1) {
282 /* convenience routine for writing a record */
283 static int rec_write(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
285 return tdb_write(tdb, offset, (char *)rec, sizeof(*rec));
288 /* convenience routine for writing a tdb_off */
289 static int ofs_write(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d)
291 return tdb_write(tdb, offset, (char *)d, sizeof(*d));
294 /* read a tdb_off from the store */
295 static int ofs_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d)
297 return tdb_read(tdb, offset, (char *)d, sizeof(*d));
300 /* read a record and check for simple errors */
301 static int rec_read(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
303 if (tdb_read(tdb, offset, (char *)rec, sizeof(*rec)) == -1) return -1;
304 if (rec->magic != TDB_MAGIC) {
306 printf("bad magic 0x%08x at offset %d\n",
309 tdb->ecode = TDB_ERR_CORRUPT;
312 if (tdb_oob(tdb, rec->next) != 0) {
318 /* expand the database at least length bytes by expanding the
319 underlying file and doing the mmap again if necessary */
320 static int tdb_expand(TDB_CONTEXT *tdb, tdb_off length)
322 struct list_struct rec;
328 /* make sure we know about any previous expansions by another
330 tdb_oob(tdb,tdb->map_size + 1);
332 /* always make room for at least 10 more records */
333 length *= TDB_LEN_MULTIPLIER;
335 /* and round the database up to a multiple of TDB_PAGE_SIZE */
336 length = ((tdb->map_size + length + TDB_PAGE_SIZE) & ~(TDB_PAGE_SIZE - 1)) - tdb->map_size;
338 /* expand the file itself */
340 lseek(tdb->fd, tdb->map_size + length - 1, SEEK_SET);
341 if (write(tdb->fd, &b, 1) != 1) goto fail;
344 /* form a new freelist record */
345 offset = FREELIST_TOP;
346 rec.rec_len = length - sizeof(rec);
347 rec.magic = TDB_FREE_MAGIC;
348 if (ofs_read(tdb, offset, &rec.next) == -1) {
353 if (tdb->fd != -1 && tdb->map_ptr) {
354 munmap(tdb->map_ptr, tdb->map_size);
359 tdb->map_size += length;
362 tdb->map_ptr = realloc(tdb->map_ptr, tdb->map_size);
366 if (rec_write(tdb, tdb->map_size - length, &rec) == -1) {
370 /* link it into the free list */
371 ptr = tdb->map_size - length;
372 if (ofs_write(tdb, offset, &ptr) == -1) goto fail;
376 tdb->map_ptr = (void *)mmap(NULL, tdb->map_size,
377 PROT_READ|PROT_WRITE,
378 MAP_SHARED | MAP_FILE, tdb->fd, 0);
379 if (tdb->map_ptr == MAP_FAILED) {
393 /* allocate some space from the free list. The offset returned points
394 to a unconnected list_struct within the database with room for at
395 least length bytes of total data
397 0 is returned if the space could not be allocated
399 static tdb_off tdb_allocate(TDB_CONTEXT *tdb, tdb_len length)
401 tdb_off offset, rec_ptr, last_ptr;
402 struct list_struct rec, lastrec, newrec;
408 offset = FREELIST_TOP;
410 /* read in the freelist top */
411 if (ofs_read(tdb, offset, &rec_ptr) == -1) {
415 /* keep looking until we find a freelist record that is big
418 if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) {
422 if (rec.magic != TDB_FREE_MAGIC) {
424 printf("bad magic 0x%08x in free list\n", rec.magic);
429 if (rec.rec_len >= length) {
430 /* found it - now possibly split it up */
431 if (rec.rec_len > length + MIN_REC_SIZE) {
432 length = (length + TDB_ALIGN) & ~(TDB_ALIGN-1);
434 newrec.rec_len = rec.rec_len - (sizeof(rec) + length);
435 newrec.next = rec.next;
436 newrec.magic = TDB_FREE_MAGIC;
438 rec.rec_len = length;
439 rec.next = rec_ptr + sizeof(rec) + rec.rec_len;
441 if (rec_write(tdb, rec.next, &newrec) == -1) {
445 if (rec_write(tdb, rec_ptr, &rec) == -1) {
450 /* remove it from the list */
452 offset = FREELIST_TOP;
454 if (ofs_write(tdb, offset, &rec.next) == -1) {
458 lastrec.next = rec.next;
459 if (rec_write(tdb, last_ptr, &lastrec) == -1) {
464 /* all done - return the new record offset */
469 /* move to the next record */
475 /* we didn't find enough space. See if we can expand the
476 database and if we can then try again */
477 if (tdb_expand(tdb, length + sizeof(rec)) == 0) goto again;
481 printf("tdb_allocate failed for size %u\n", length);
487 /* initialise a new database with a specified hash size */
488 static int tdb_new_database(TDB_CONTEXT *tdb, int hash_size)
490 struct tdb_header header;
495 /* create the header */
496 memset(&header, 0, sizeof(header));
497 memcpy(header.magic_food, TDB_MAGIC_FOOD, strlen(TDB_MAGIC_FOOD)+1);
498 header.version = TDB_VERSION;
499 header.hash_size = hash_size;
500 lseek(tdb->fd, 0, SEEK_SET);
501 ftruncate(tdb->fd, 0);
503 if (tdb->fd != -1 && write(tdb->fd, &header, sizeof(header)) !=
505 tdb->ecode = TDB_ERR_IO;
507 } else size += sizeof(header);
509 /* the freelist and hash pointers */
511 memset(buf, 0, sizeof(buf));
513 for (i=0;(hash_size+1)-i >= 16; i += 16) {
514 if (tdb->fd != -1 && write(tdb->fd, buf, sizeof(buf)) !=
516 tdb->ecode = TDB_ERR_IO;
518 } else size += sizeof(buf);
521 for (;i<hash_size+1; i++) {
522 if (tdb->fd != -1 && write(tdb->fd, buf, sizeof(tdb_off)) !=
524 tdb->ecode = TDB_ERR_IO;
526 } else size += sizeof(tdb_off);
530 tdb->map_ptr = calloc(size, 1);
531 tdb->map_size = size;
532 if (tdb->map_ptr == NULL) {
533 tdb->ecode = TDB_ERR_IO;
536 memcpy(&tdb->header, &header, sizeof(header));
540 printf("initialised database of hash_size %u\n",
546 /* Returns 0 on fail. On success, return offset of record, and fills
548 static tdb_off tdb_find(TDB_CONTEXT *tdb, TDB_DATA key, unsigned int hash,
549 struct list_struct *rec)
551 tdb_off offset, rec_ptr;
553 /* find the top of the hash chain */
554 offset = tdb_hash_top(tdb, hash);
556 /* read in the hash top */
557 if (ofs_read(tdb, offset, &rec_ptr) == -1)
560 /* keep looking until we find the right record */
562 if (rec_read(tdb, rec_ptr, rec) == -1)
565 if (hash == rec->full_hash && key.dsize == rec->key_len) {
567 /* a very likely hit - read the key */
568 k = tdb_alloc_read(tdb, rec_ptr + sizeof(*rec),
574 if (memcmp(key.dptr, k, key.dsize) == 0) {
581 /* move to the next record */
588 return an error string for the last tdb error
590 char *tdb_error(TDB_CONTEXT *tdb)
594 enum TDB_ERROR ecode;
597 {TDB_SUCCESS, "Success"},
598 {TDB_ERR_CORRUPT, "Corrupt database"},
599 {TDB_ERR_IO, "IO Error"},
600 {TDB_ERR_LOCK, "Locking error"},
601 {TDB_ERR_OOM, "Out of memory"},
602 {TDB_ERR_EXISTS, "Record exists"},
605 for (i=0;emap[i].estring;i++) {
606 if (tdb->ecode == emap[i].ecode) return emap[i].estring;
609 return "Invalid tdb context";
611 return "Invalid error code";
615 /* update an entry in place - this only works if the new data size
616 is <= the old data size and the key exists.
619 int tdb_update(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf)
622 struct list_struct rec;
628 printf("tdb_update() called with null context\n");
633 /* find which hash bucket it is in */
634 hash = tdb_hash(&key);
636 tdb_lock(tdb, BUCKET(hash));
637 rec_ptr = tdb_find(tdb, key, hash, &rec);
642 /* must be long enough */
643 if (rec.rec_len < key.dsize + dbuf.dsize)
646 if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len,
647 dbuf.dptr, dbuf.dsize) == -1)
650 if (dbuf.dsize != rec.data_len) {
652 rec.data_len = dbuf.dsize;
653 ret = rec_write(tdb, rec_ptr, &rec);
658 tdb_unlock(tdb, BUCKET(hash));
662 /* find an entry in the database given a key */
663 TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key)
667 struct list_struct rec;
668 TDB_DATA ret = null_data;
672 printf("tdb_fetch() called with null context\n");
677 /* find which hash bucket it is in */
678 hash = tdb_hash(&key);
680 tdb_lock(tdb, BUCKET(hash));
681 rec_ptr = tdb_find(tdb, key, hash, &rec);
684 ret.dptr = tdb_alloc_read(tdb,
685 rec_ptr + sizeof(rec) + rec.key_len,
687 ret.dsize = rec.data_len;
690 tdb_unlock(tdb, BUCKET(hash));
694 /* check if an entry in the database exists
696 note that 1 is returned if the key is found and 0 is returned if not found
697 this doesn't match the conventions in the rest of this module, but is
700 int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key)
704 struct list_struct rec;
708 printf("tdb_exists() called with null context\n");
713 /* find which hash bucket it is in */
714 hash = tdb_hash(&key);
716 tdb_lock(tdb, BUCKET(hash));
717 rec_ptr = tdb_find(tdb, key, hash, &rec);
718 tdb_unlock(tdb, BUCKET(hash));
723 /* traverse the entire database - calling fn(tdb, key, data) on each element.
724 return -1 on error or the record count traversed
725 if fn is NULL then it is not called
726 a non-zero return value from fn() indicates that the traversal should stop
728 int tdb_traverse(TDB_CONTEXT *tdb, int (*fn)(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void* state), void* state)
732 tdb_off offset, rec_ptr;
733 struct list_struct rec;
739 printf("tdb_traverse() called with null context\n");
744 /* loop over all hash chains */
745 for (h = 0; h < tdb->header.hash_size; h++) {
746 tdb_lock(tdb, BUCKET(h));
748 /* read in the hash top */
749 offset = tdb_hash_top(tdb, h);
750 if (ofs_read(tdb, offset, &rec_ptr) == -1) {
754 /* traverse all records for this hash */
756 if (rec_read(tdb, rec_ptr, &rec) == -1) {
760 /* now read the full record */
761 data = tdb_alloc_read(tdb, rec_ptr + sizeof(rec),
762 rec.key_len + rec.data_len);
768 key.dsize = rec.key_len;
769 dbuf.dptr = data + rec.key_len;
770 dbuf.dsize = rec.data_len;
773 if (fn && fn(tdb, key, dbuf, state) != 0) {
774 /* they want us to stop traversing */
776 tdb_unlock(tdb, BUCKET(h));
783 /* move to the next record */
786 tdb_unlock(tdb, BUCKET(h));
789 /* return the number traversed */
793 tdb_unlock(tdb, BUCKET(h));
798 /* find the first entry in the database and return its key */
799 TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb)
801 tdb_off offset, rec_ptr;
802 struct list_struct rec;
808 printf("tdb_firstkey() called with null context\n");
813 /* look for a non-empty hash chain */
814 for (hash = 0, rec_ptr = 0;
815 hash < tdb->header.hash_size;
817 /* find the top of the hash chain */
818 offset = tdb_hash_top(tdb, hash);
820 tdb_lock(tdb, BUCKET(hash));
822 /* read in the hash top */
823 if (ofs_read(tdb, offset, &rec_ptr) == -1) {
829 tdb_unlock(tdb, BUCKET(hash));
832 if (rec_ptr == 0) return null_data;
834 /* we've found a non-empty chain, now read the record */
835 if (rec_read(tdb, rec_ptr, &rec) == -1) {
839 /* allocate and read the key space */
840 ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec), rec.key_len);
841 ret.dsize = rec.key_len;
842 tdb_unlock(tdb, BUCKET(hash));
846 tdb_unlock(tdb, BUCKET(hash));
850 /* find the next entry in the database, returning its key */
851 TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key)
853 unsigned hash, hbucket;
854 tdb_off rec_ptr, offset;
855 struct list_struct rec;
860 printf("tdb_nextkey() called with null context\n");
865 /* find which hash bucket it is in */
866 hash = tdb_hash(&key);
867 hbucket = BUCKET(hash);
869 tdb_lock(tdb, hbucket);
870 rec_ptr = tdb_find(tdb, key, hash, &rec);
872 /* we want the next record after this one */
876 /* not found or last in hash: look for next non-empty hash chain */
877 while (rec_ptr == 0) {
878 tdb_unlock(tdb, hbucket);
880 if (++hbucket >= tdb->header.hash_size - 1)
883 offset = tdb_hash_top(tdb, hbucket);
884 tdb_lock(tdb, hbucket);
885 /* read in the hash top */
886 if (ofs_read(tdb, offset, &rec_ptr) == -1) {
887 tdb_unlock(tdb, hbucket);
892 /* Read the record. */
893 if (rec_read(tdb, rec_ptr, &rec) == -1) {
894 tdb_unlock(tdb, hbucket);
897 /* allocate and read the key */
898 ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec), rec.key_len);
899 ret.dsize = rec.key_len;
900 tdb_unlock(tdb, hbucket);
905 /* delete an entry in the database given a key */
906 int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key)
909 tdb_off offset, rec_ptr, last_ptr;
910 struct list_struct rec, lastrec;
915 printf("tdb_delete() called with null context\n");
920 /* find which hash bucket it is in */
921 hash = tdb_hash(&key);
923 tdb_lock(tdb, BUCKET(hash));
925 /* find the top of the hash chain */
926 offset = tdb_hash_top(tdb, hash);
928 /* read in the hash top */
929 if (ofs_read(tdb, offset, &rec_ptr) == -1) {
935 /* keep looking until we find the right record */
937 if (rec_read(tdb, rec_ptr, &rec) == -1) {
941 if (hash == rec.full_hash && key.dsize == rec.key_len) {
942 /* a very likely hit - read the record and full key */
943 data = tdb_alloc_read(tdb, rec_ptr + sizeof(rec),
949 if (memcmp(key.dptr, data, key.dsize) == 0) {
950 /* a definite match - delete it */
952 offset = tdb_hash_top(tdb, hash);
953 if (ofs_write(tdb, offset, &rec.next) == -1) {
957 lastrec.next = rec.next;
958 if (rec_write(tdb, last_ptr, &lastrec) == -1) {
962 tdb_unlock(tdb, BUCKET(hash));
964 /* and recover the space */
965 offset = FREELIST_TOP;
966 if (ofs_read(tdb, offset, &rec.next) == -1) {
969 rec.magic = TDB_FREE_MAGIC;
970 if (rec_write(tdb, rec_ptr, &rec) == -1) {
973 if (ofs_write(tdb, offset, &rec_ptr) == -1) {
977 /* yipee - all done */
988 /* move to the next record */
995 if (data) free(data);
996 tdb_unlock(tdb, BUCKET(hash));
1000 if (data) free(data);
1001 tdb_unlock(tdb, -1);
1006 /* store an element in the database, replacing any existing element
1009 return 0 on success, -1 on failure
1011 int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
1013 struct list_struct rec;
1015 tdb_off rec_ptr, offset;
1020 printf("tdb_store() called with null context\n");
1025 /* find which hash bucket it is in */
1026 hash = tdb_hash(&key);
1028 /* check for it existing */
1029 if (flag == TDB_INSERT && tdb_exists(tdb, key)) {
1030 tdb->ecode = TDB_ERR_EXISTS;
1034 /* first try in-place update */
1035 if (flag != TDB_INSERT && tdb_update(tdb, key, dbuf) == 0) {
1039 rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize);
1044 tdb_lock(tdb, BUCKET(hash));
1046 /* delete any existing record - if it doesn't exist we don't care */
1047 if (flag != TDB_INSERT) {
1048 tdb_delete(tdb, key);
1051 /* read the newly created record */
1052 if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) {
1056 if (rec.magic != TDB_FREE_MAGIC) goto fail;
1058 /* find the top of the hash chain */
1059 offset = tdb_hash_top(tdb, hash);
1061 /* read in the hash top diretcly into our next pointer */
1062 if (ofs_read(tdb, offset, &rec.next) == -1) {
1066 rec.key_len = key.dsize;
1067 rec.data_len = dbuf.dsize;
1068 rec.full_hash = hash;
1069 rec.magic = TDB_MAGIC;
1071 p = (char *)malloc(sizeof(rec) + key.dsize + dbuf.dsize);
1073 tdb->ecode = TDB_ERR_OOM;
1077 memcpy(p, &rec, sizeof(rec));
1078 memcpy(p+sizeof(rec), key.dptr, key.dsize);
1079 memcpy(p+sizeof(rec)+key.dsize, dbuf.dptr, dbuf.dsize);
1081 if (tdb_write(tdb, rec_ptr, p, sizeof(rec)+key.dsize+dbuf.dsize) == -1)
1087 /* and point the top of the hash chain at it */
1088 if (ofs_write(tdb, offset, &rec_ptr) == -1) goto fail;
1090 tdb_unlock(tdb, BUCKET(hash));
1095 printf("store failed for hash 0x%08x in bucket %u\n", hash, BUCKET(hash));
1098 tdb_unlock(tdb, BUCKET(hash));
1103 /* open the database, creating it if necessary
1105 The open_flags and mode are passed straight to the open call on the database
1106 file. A flags value of O_WRONLY is invalid
1108 The hash size is advisory, use zero for a default value.
1110 return is NULL on error
1112 TDB_CONTEXT *tdb_open(char *name, int hash_size, int tdb_flags,
1113 int open_flags, mode_t mode)
1115 TDB_CONTEXT tdb, *ret;
1118 memset(&tdb, 0, sizeof(tdb));
1124 if ((open_flags & O_ACCMODE) == O_WRONLY) {
1128 if (hash_size == 0) hash_size = DEFAULT_HASH_SIZE;
1130 tdb.read_only = ((open_flags & O_ACCMODE) == O_RDONLY);
1133 tdb.fd = open(name, open_flags, mode);
1139 /* ensure there is only one process initialising at once */
1140 tdb_brlock(&tdb, GLOBAL_LOCK, LOCK_SET, F_WRLCK, F_SETLKW);
1142 if (tdb_flags & TDB_CLEAR_IF_FIRST) {
1143 /* we need to zero the database if we are the only
1145 if (tdb_brlock(&tdb, ACTIVE_LOCK, LOCK_SET, F_WRLCK, F_SETLK) == 0) {
1146 ftruncate(tdb.fd, 0);
1147 tdb_brlock(&tdb, ACTIVE_LOCK, LOCK_CLEAR, F_WRLCK, F_SETLK);
1151 /* leave this lock in place */
1152 tdb_brlock(&tdb, ACTIVE_LOCK, LOCK_SET, F_RDLCK, F_SETLKW);
1154 if (read(tdb.fd, &tdb.header, sizeof(tdb.header)) != sizeof(tdb.header) ||
1155 strcmp(tdb.header.magic_food, TDB_MAGIC_FOOD) != 0 ||
1156 tdb.header.version != TDB_VERSION) {
1157 /* its not a valid database - possibly initialise it */
1158 if (!(open_flags & O_CREAT)) {
1161 if (tdb_new_database(&tdb, hash_size) == -1) goto fail;
1163 lseek(tdb.fd, 0, SEEK_SET);
1164 if (tdb.fd != -1 && read(tdb.fd, &tdb.header,
1165 sizeof(tdb.header)) !=
1173 /* map the database and fill in the return structure */
1174 tdb.name = (char *)strdup(name);
1175 tdb.map_size = st.st_size;
1178 tdb.locked = (int *)calloc(tdb.header.hash_size+1,
1179 sizeof(tdb.locked[0]));
1186 tdb.map_ptr = (void *)mmap(NULL, st.st_size,
1187 tdb.read_only? PROT_READ : PROT_READ|PROT_WRITE,
1188 MAP_SHARED | MAP_FILE, tdb.fd, 0);
1189 if (tdb.map_ptr == MAP_FAILED) {
1195 ret = (TDB_CONTEXT *)malloc(sizeof(tdb));
1196 if (!ret) goto fail;
1201 printf("mapped database of hash_size %u map_size=%u\n",
1202 hash_size, tdb.map_size);
1205 tdb_brlock(&tdb, GLOBAL_LOCK, LOCK_CLEAR, F_WRLCK, F_SETLKW);
1209 if (tdb.name) free(tdb.name);
1210 if (tdb.fd != -1) close(tdb.fd);
1211 if (tdb.map_ptr) munmap(tdb.map_ptr, tdb.map_size);
1216 /* close a database */
1217 int tdb_close(TDB_CONTEXT *tdb)
1219 if (!tdb) return -1;
1221 if (tdb->name) free(tdb->name);
1222 if (tdb->fd != -1) close(tdb->fd);
1223 if (tdb->locked) free(tdb->locked);
1226 if (tdb->fd != -1) {
1227 munmap(tdb->map_ptr, tdb->map_size);
1233 memset(tdb, 0, sizeof(*tdb));
1239 /* lock the database. If we already have it locked then don't do anything */
1240 int tdb_writelock(TDB_CONTEXT *tdb)
1244 printf("tdb_writelock() called with null context\n");
1249 return tdb_lock(tdb, -1);
1252 /* unlock the database. */
1253 int tdb_writeunlock(TDB_CONTEXT *tdb)
1257 printf("tdb_writeunlock() called with null context\n");
1262 return tdb_unlock(tdb, -1);
1265 /* lock one hash chain. This is meant to be used to reduce locking
1266 contention - it cannot guarantee how many records will be locked */
1267 int tdb_lockchain(TDB_CONTEXT *tdb, TDB_DATA key)
1271 printf("tdb_lockchain() called with null context\n");
1276 return tdb_lock(tdb, BUCKET(tdb_hash(&key)));
1280 /* unlock one hash chain */
1281 int tdb_unlockchain(TDB_CONTEXT *tdb, TDB_DATA key)
1285 printf("tdb_unlockchain() called with null context\n");
1290 return tdb_unlock(tdb, BUCKET(tdb_hash(&key)));