📄 tdb.c
字号:
if (dbuf.dsize != rec.data_len) { /* update size */ rec.data_len = dbuf.dsize; ret = rec_write(tdb, rec_ptr, &rec); } else ret = 0; out: tdb_unlock(tdb, BUCKET(hash)); return ret;}/* find an entry in the database given a key */TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key){ unsigned hash; tdb_off rec_ptr; struct list_struct rec; TDB_DATA ret = null_data; if (tdb == NULL) {#ifdef TDB_DEBUG printf("tdb_fetch() called with null context\n");#endif return null_data; } /* find which hash bucket it is in */ hash = tdb_hash(&key); tdb_lock(tdb, BUCKET(hash)); rec_ptr = tdb_find(tdb, key, hash, &rec); if (rec_ptr) { ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec) + rec.key_len, rec.data_len); ret.dsize = rec.data_len; } tdb_unlock(tdb, BUCKET(hash)); return ret;}/* check if an entry in the database exists note that 1 is returned if the key is found and 0 is returned if not found this doesn't match the conventions in the rest of this module, but is compatible with gdbm*/int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key){ unsigned hash; tdb_off rec_ptr; struct list_struct rec; if (tdb == NULL) {#ifdef TDB_DEBUG printf("tdb_exists() called with null context\n");#endif return 0; } /* find which hash bucket it is in */ hash = tdb_hash(&key); tdb_lock(tdb, BUCKET(hash)); rec_ptr = tdb_find(tdb, key, hash, &rec); tdb_unlock(tdb, BUCKET(hash)); return rec_ptr != 0;}/* traverse the entire database - calling fn(tdb, key, data) on each element. return -1 on error or the record count traversed if fn is NULL then it is not called a non-zero return value from fn() indicates that the traversal should stop */int tdb_traverse(TDB_CONTEXT *tdb, int (*fn)(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void* state), void* state){ int count = 0; unsigned h; tdb_off offset, rec_ptr; struct list_struct rec; char *data; TDB_DATA key, dbuf; if (tdb == NULL) {#ifdef TDB_DEBUG printf("tdb_traverse() called with null context\n");#endif return -1; } /* loop over all hash chains */ for (h = 0; h < tdb->header.hash_size; h++) { tdb_lock(tdb, BUCKET(h)); /* read in the hash top */ offset = tdb_hash_top(tdb, h); if (ofs_read(tdb, offset, &rec_ptr) == -1) { goto fail; } /* traverse all records for this hash */ while (rec_ptr) { if (rec_read(tdb, rec_ptr, &rec) == -1) { goto fail; } /* now read the full record */ data = tdb_alloc_read(tdb, rec_ptr + sizeof(rec), rec.key_len + rec.data_len); if (!data) { goto fail; } key.dptr = data; key.dsize = rec.key_len; dbuf.dptr = data + rec.key_len; dbuf.dsize = rec.data_len; count++; if (fn && fn(tdb, key, dbuf, state) != 0) { /* they want us to stop traversing */ free(data); tdb_unlock(tdb, BUCKET(h)); return count; } /* a miss - drat */ free(data); /* move to the next record */ rec_ptr = rec.next; } tdb_unlock(tdb, BUCKET(h)); } /* return the number traversed */ return count; fail: tdb_unlock(tdb, BUCKET(h)); return -1;}/* find the first entry in the database and return its key */TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb){ tdb_off offset, rec_ptr; struct list_struct rec; unsigned hash; TDB_DATA ret; if (tdb == NULL) {#ifdef TDB_DEBUG printf("tdb_firstkey() called with null context\n");#endif return null_data; } /* look for a non-empty hash chain */ for (hash = 0, rec_ptr = 0; hash < tdb->header.hash_size; hash++) { /* find the top of the hash chain */ offset = tdb_hash_top(tdb, hash); tdb_lock(tdb, BUCKET(hash)); /* read in the hash top */ if (ofs_read(tdb, offset, &rec_ptr) == -1) { goto fail; } if (rec_ptr) break; tdb_unlock(tdb, BUCKET(hash)); } if (rec_ptr == 0) return null_data; /* we've found a non-empty chain, now read the record */ if (rec_read(tdb, rec_ptr, &rec) == -1) { goto fail; } /* allocate and read the key space */ ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec), rec.key_len); ret.dsize = rec.key_len; tdb_unlock(tdb, BUCKET(hash)); return ret; fail: tdb_unlock(tdb, BUCKET(hash)); return null_data;}/* find the next entry in the database, returning its key */TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key){ unsigned hash, hbucket; tdb_off rec_ptr, offset; struct list_struct rec; TDB_DATA ret; if (tdb == NULL) {#ifdef TDB_DEBUG printf("tdb_nextkey() called with null context\n");#endif return null_data; } /* find which hash bucket it is in */ hash = tdb_hash(&key); hbucket = BUCKET(hash); tdb_lock(tdb, hbucket); rec_ptr = tdb_find(tdb, key, hash, &rec); if (rec_ptr) { /* we want the next record after this one */ rec_ptr = rec.next; } /* not found or last in hash: look for next non-empty hash chain */ while (rec_ptr == 0) { tdb_unlock(tdb, hbucket); if (++hbucket >= tdb->header.hash_size - 1) return null_data; offset = tdb_hash_top(tdb, hbucket); tdb_lock(tdb, hbucket); /* read in the hash top */ if (ofs_read(tdb, offset, &rec_ptr) == -1) { tdb_unlock(tdb, hbucket); return null_data; } } /* Read the record. */ if (rec_read(tdb, rec_ptr, &rec) == -1) { tdb_unlock(tdb, hbucket); return null_data; } /* allocate and read the key */ ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec), rec.key_len); ret.dsize = rec.key_len; tdb_unlock(tdb, hbucket); return ret;}/* delete an entry in the database given a key */int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key){ unsigned hash; tdb_off offset, rec_ptr, last_ptr; struct list_struct rec, lastrec; char *data = NULL; if (tdb == NULL) {#ifdef TDB_DEBUG printf("tdb_delete() called with null context\n");#endif return -1; } /* find which hash bucket it is in */ hash = tdb_hash(&key); tdb_lock(tdb, BUCKET(hash)); /* find the top of the hash chain */ offset = tdb_hash_top(tdb, hash); /* read in the hash top */ if (ofs_read(tdb, offset, &rec_ptr) == -1) { goto fail; } last_ptr = 0; /* keep looking until we find the right record */ while (rec_ptr) { if (rec_read(tdb, rec_ptr, &rec) == -1) { goto fail; } if (hash == rec.full_hash && key.dsize == rec.key_len) { /* a very likely hit - read the record and full key */ data = tdb_alloc_read(tdb, rec_ptr + sizeof(rec), rec.key_len); if (!data) { goto fail; } if (memcmp(key.dptr, data, key.dsize) == 0) { /* a definite match - delete it */ if (last_ptr == 0) { offset = tdb_hash_top(tdb, hash); if (ofs_write(tdb, offset, &rec.next) == -1) { goto fail; } } else { lastrec.next = rec.next; if (rec_write(tdb, last_ptr, &lastrec) == -1) { goto fail; } } tdb_unlock(tdb, BUCKET(hash)); tdb_lock(tdb, -1); /* and recover the space */ offset = FREELIST_TOP; if (ofs_read(tdb, offset, &rec.next) == -1) { goto fail2; } rec.magic = TDB_FREE_MAGIC; if (rec_write(tdb, rec_ptr, &rec) == -1) { goto fail2; } if (ofs_write(tdb, offset, &rec_ptr) == -1) { goto fail2; } /* yipee - all done */ free(data); tdb_unlock(tdb, -1); return 0; } /* a miss - drat */ free(data); data = NULL; } /* move to the next record */ last_ptr = rec_ptr; lastrec = rec; rec_ptr = rec.next; } fail: if (data) free(data); tdb_unlock(tdb, BUCKET(hash)); return -1; fail2: if (data) free(data); tdb_unlock(tdb, -1); return -1;}/* store an element in the database, replacing any existing element with the same key return 0 on success, -1 on failure*/int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag){ struct list_struct rec; unsigned hash; tdb_off rec_ptr, offset; char *p = NULL; if (tdb == NULL) {#ifdef TDB_DEBUG printf("tdb_store() called with null context\n");#endif return -1; } /* find which hash bucket it is in */ hash = tdb_hash(&key); /* check for it existing */ if (flag == TDB_INSERT && tdb_exists(tdb, key)) { tdb->ecode = TDB_ERR_EXISTS; return -1; } /* first try in-place update */ if (flag != TDB_INSERT && tdb_update(tdb, key, dbuf) == 0) { return 0; } rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize); if (rec_ptr == 0) { return -1; } tdb_lock(tdb, BUCKET(hash)); /* delete any existing record - if it doesn't exist we don't care */ if (flag != TDB_INSERT) { tdb_delete(tdb, key); } /* read the newly created record */ if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) { goto fail; } if (rec.magic != TDB_FREE_MAGIC) goto fail; /* find the top of the hash chain */ offset = tdb_hash_top(tdb, hash); /* read in the hash top diretcly into our next pointer */ if (ofs_read(tdb, offset, &rec.next) == -1) { goto fail; } rec.key_len = key.dsize; rec.data_len = dbuf.dsize; rec.full_hash = hash; rec.magic = TDB_MAGIC; p = (char *)malloc(sizeof(rec) + key.dsize + dbuf.dsize); if (!p) { tdb->ecode = TDB_ERR_OOM; goto fail; } memcpy(p, &rec, sizeof(rec)); memcpy(p+sizeof(rec), key.dptr, key.dsize); memcpy(p+sizeof(rec)+key.dsize, dbuf.dptr, dbuf.dsize); if (tdb_write(tdb, rec_ptr, p, sizeof(rec)+key.dsize+dbuf.dsize) == -1) goto fail; free(p); p = NULL; /* and point the top of the hash chain at it */ if (ofs_write(tdb, offset, &rec_ptr) == -1) goto fail; tdb_unlock(tdb, BUCKET(hash)); return 0; fail:#if TDB_DEBUG printf("store failed for hash 0x%08x in bucket %u\n", hash, BUCKET(hash));#endif if (p) free(p); tdb_unlock(tdb, BUCKET(hash)); return -1;}/* open the database, creating it if necessary The open_flags and mode are passed straight to the open call on the database file. A flags value of O_WRONLY is invalid The hash size is advisory, use zero for a default value. return is NULL on error*/TDB_CONTEXT *tdb_open(char *name, int hash_size, int tdb_flags, int open_flags, mode_t mode){ TDB_CONTEXT tdb, *ret; struct stat st; memset(&tdb, 0, sizeof(tdb)); tdb.fd = -1; tdb.name = NULL; tdb.map_ptr = NULL; if ((open_flags & O_ACCMODE) == O_WRONLY) { goto fail; } if (hash_size == 0) hash_size = DEFAULT_HASH_SIZE; tdb.read_only = ((open_flags & O_ACCMODE) == O_RDONLY); if (name != NULL) { tdb.fd = open(name, open_flags, mode); if (tdb.fd == -1) { goto fail; } } /* ensure there is only one process initialising at once */ tdb_brlock(&tdb, GLOBAL_LOCK, LOCK_SET, F_WRLCK, F_SETLKW); if (tdb_flags & TDB_CLEAR_IF_FIRST) { /* we need to zero the database if we are the only one with it open */ if (tdb_brlock(&tdb, ACTIVE_LOCK, LOCK_SET, F_WRLCK, F_SETLK) == 0) { ftruncate(tdb.fd, 0); tdb_brlock(&tdb, ACTIVE_LOCK, LOCK_CLEAR, F_WRLCK, F_SETLK); } } /* leave this lock in place */ tdb_brlock(&tdb, ACTIVE_LOCK, LOCK_SET, F_RDLCK, F_SETLKW); if (read(tdb.fd, &tdb.header, sizeof(tdb.header)) != sizeof(tdb.header) || strcmp(tdb.header.magic_food, TDB_MAGIC_FOOD) != 0 || tdb.header.version != TDB_VERSION) { /* its not a valid database - possibly initialise it */ if (!(open_flags & O_CREAT)) { goto fail; } if (tdb_new_database(&tdb, hash_size) == -1) goto fail; lseek(tdb.fd, 0, SEEK_SET); if (tdb.fd != -1 && read(tdb.fd, &tdb.header, sizeof(tdb.header)) != sizeof(tdb.header)) goto fail; } if (tdb.fd != -1) { fstat(tdb.fd, &st); /* map the database and fill in the return structure */ tdb.name = (char *)strdup(name); tdb.map_size = st.st_size; } tdb.locked = (int *)calloc(tdb.header.hash_size+1, sizeof(tdb.locked[0])); if (!tdb.locked) { goto fail; }#if HAVE_MMAP if (tdb.fd != -1) { tdb.map_ptr = (void *)mmap(NULL, st.st_size, tdb.read_only? PROT_READ : PROT_READ|PROT_WRITE, MAP_SHARED | MAP_FILE, tdb.fd, 0); }#endif ret = (TDB_CONTEXT *)malloc(sizeof(tdb)); if (!ret) goto fail; *ret = tdb;#if TDB_DEBUG printf("mapped database of hash_size %u map_size=%u\n", hash_size, tdb.map_size);#endif tdb_brlock(&tdb, GLOBAL_LOCK, LOCK_CLEAR, F_WRLCK, F_SETLKW); return ret; fail: if (tdb.name) free(tdb.name); if (tdb.fd != -1) close(tdb.fd); if (tdb.map_ptr) munmap(tdb.map_ptr, tdb.map_size); return NULL;}/* close a database */int tdb_close(TDB_CONTEXT *tdb){ if (!tdb) return -1; if (tdb->name) free(tdb->name); if (tdb->fd != -1) close(tdb->fd); if (tdb->locked) free(tdb->locked); if (tdb->map_ptr) { if (tdb->fd != -1) { munmap(tdb->map_ptr, tdb->map_size); } else { free(tdb->map_ptr); } } memset(tdb, 0, sizeof(*tdb)); free(tdb); return 0;}/* lock the database. If we already have it locked then don't do anything */int tdb_writelock(TDB_CONTEXT *tdb){ if (tdb == NULL) {#ifdef TDB_DEBUG printf("tdb_writelock() called with null context\n");#endif return -1; } return tdb_lock(tdb, -1);}/* unlock the database. */int tdb_writeunlock(TDB_CONTEXT *tdb){ if (tdb == NULL) {#ifdef TDB_DEBUG printf("tdb_writeunlock() called with null context\n");#endif return -1; } return tdb_unlock(tdb, -1);}/* lock one hash chain. This is meant to be used to reduce locking contention - it cannot guarantee how many records will be locked */int tdb_lockchain(TDB_CONTEXT *tdb, TDB_DATA key){ if (tdb == NULL) {#ifdef TDB_DEBUG printf("tdb_lockchain() called with null context\n");#endif return -1; } return tdb_lock(tdb, BUCKET(tdb_hash(&key)));}/* unlock one hash chain */int tdb_unlockchain(TDB_CONTEXT *tdb, TDB_DATA key){ if (tdb == NULL) {#ifdef TDB_DEBUG printf("tdb_unlockchain() called with null context\n");#endif return -1; } return tdb_unlock(tdb, BUCKET(tdb_hash(&key)));}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -