⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tdb.c

📁 自己精简过的PPPD代码。在嵌入中应用可以更好的发挥。比原先的小了很多
💻 C
📖 第 1 页 / 共 4 页
字号:
	if (!(rec_ptr = tdb_find(tdb, key, hash, rec)))		tdb_unlock(tdb, BUCKET(hash), locktype);	return rec_ptr;}enum TDB_ERROR tdb_error(TDB_CONTEXT *tdb){	return tdb->ecode;}static struct tdb_errname {	enum TDB_ERROR ecode; const char *estring;} emap[] = { {TDB_SUCCESS, "Success"},	     {TDB_ERR_CORRUPT, "Corrupt database"},	     {TDB_ERR_IO, "IO Error"},	     {TDB_ERR_LOCK, "Locking error"},	     {TDB_ERR_OOM, "Out of memory"},	     {TDB_ERR_EXISTS, "Record exists"},	     {TDB_ERR_NOLOCK, "Lock exists on other keys"},	     {TDB_ERR_NOEXIST, "Record does not exist"} };/* Error string for the last tdb error */const char *tdb_errorstr(TDB_CONTEXT *tdb){	u32 i;	for (i = 0; i < sizeof(emap) / sizeof(struct tdb_errname); i++)		if (tdb->ecode == emap[i].ecode)			return emap[i].estring;	return "Invalid error code";}/* update an entry in place - this only works if the new data size   is <= the old data size and the key exists.   on failure return -1.*/static int tdb_update_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash, TDB_DATA dbuf){	struct list_struct rec;	tdb_off rec_ptr;	/* find entry */	if (!(rec_ptr = tdb_find(tdb, key, hash, &rec)))		return -1;	/* must be long enough key, data and tailer */	if (rec.rec_len < key.dsize + dbuf.dsize + sizeof(tdb_off)) {		tdb->ecode = TDB_SUCCESS; /* Not really an error */		return -1;	}	if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len,		      dbuf.dptr, dbuf.dsize) == -1)		return -1;	if (dbuf.dsize != rec.data_len) {		/* update size */		rec.data_len = dbuf.dsize;		return rec_write(tdb, rec_ptr, &rec);	} 	return 0;}/* find an entry in the database given a key *//* If an entry doesn't exist tdb_err will be set to * TDB_ERR_NOEXIST. If a key has no data attached * tdb_err will not be set. Both will return a * zero pptr and zero dsize. */TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key){	tdb_off rec_ptr;	struct list_struct rec;	TDB_DATA ret;	u32 hash;	/* find which hash bucket it is in */	hash = tdb->hash_fn(&key);	if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec)))		return tdb_null;	if (rec.data_len)		ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec) + rec.key_len,					  rec.data_len);	else		ret.dptr = NULL;	ret.dsize = rec.data_len;	tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);	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*/static int tdb_exists_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash){	struct list_struct rec;		if (tdb_find_lock_hash(tdb, key, hash, F_RDLCK, &rec) == 0)		return 0;	tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);	return 1;}int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key){	u32 hash = tdb->hash_fn(&key);	return tdb_exists_hash(tdb, key, hash);}/* record lock stops delete underneath */static int lock_record(TDB_CONTEXT *tdb, tdb_off off){	return off ? tdb_brlock(tdb, off, F_RDLCK, F_SETLKW, 0) : 0;}/*  Write locks override our own fcntl readlocks, so check it here.  Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not  an error to fail to get the lock here.*/ static int write_lock_record(TDB_CONTEXT *tdb, tdb_off off){	struct tdb_traverse_lock *i;	for (i = &tdb->travlocks; i; i = i->next)		if (i->off == off)			return -1;	return tdb_brlock(tdb, off, F_WRLCK, F_SETLK, 1);}/*  Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not  an error to fail to get the lock here.*/static int write_unlock_record(TDB_CONTEXT *tdb, tdb_off off){	return tdb_brlock(tdb, off, F_UNLCK, F_SETLK, 0);}/* fcntl locks don't stack: avoid unlocking someone else's */static int unlock_record(TDB_CONTEXT *tdb, tdb_off off){	struct tdb_traverse_lock *i;	u32 count = 0;	if (off == 0)		return 0;	for (i = &tdb->travlocks; i; i = i->next)		if (i->off == off)			count++;	return (count == 1 ? tdb_brlock(tdb, off, F_UNLCK, F_SETLKW, 0) : 0);}/* actually delete an entry in the database given the offset */static int do_delete(TDB_CONTEXT *tdb, tdb_off rec_ptr, struct list_struct*rec){	tdb_off last_ptr, i;	struct list_struct lastrec;	if (tdb->read_only) return -1;	if (write_lock_record(tdb, rec_ptr) == -1) {		/* Someone traversing here: mark it as dead */		rec->magic = TDB_DEAD_MAGIC;		return rec_write(tdb, rec_ptr, rec);	}	if (write_unlock_record(tdb, rec_ptr) != 0)		return -1;	/* find previous record in hash chain */	if (ofs_read(tdb, TDB_HASH_TOP(rec->full_hash), &i) == -1)		return -1;	for (last_ptr = 0; i != rec_ptr; last_ptr = i, i = lastrec.next)		if (rec_read(tdb, i, &lastrec) == -1)			return -1;	/* unlink it: next ptr is at start of record. */	if (last_ptr == 0)		last_ptr = TDB_HASH_TOP(rec->full_hash);	if (ofs_write(tdb, last_ptr, &rec->next) == -1)		return -1;	/* recover the space */	if (tdb_free(tdb, rec_ptr, rec) == -1)		return -1;	return 0;}/* Uses traverse lock: 0 = finish, -1 = error, other = record offset */static int tdb_next_lock(TDB_CONTEXT *tdb, struct tdb_traverse_lock *tlock,			 struct list_struct *rec){	int want_next = (tlock->off != 0);	/* Lock each chain from the start one. */	for (; tlock->hash < tdb->header.hash_size; tlock->hash++) {		if (tdb_lock(tdb, tlock->hash, F_WRLCK) == -1)			return -1;		/* No previous record?  Start at top of chain. */		if (!tlock->off) {			if (ofs_read(tdb, TDB_HASH_TOP(tlock->hash),				     &tlock->off) == -1)				goto fail;		} else {			/* Otherwise unlock the previous record. */			if (unlock_record(tdb, tlock->off) != 0)				goto fail;		}		if (want_next) {			/* We have offset of old record: grab next */			if (rec_read(tdb, tlock->off, rec) == -1)				goto fail;			tlock->off = rec->next;		}		/* Iterate through chain */		while( tlock->off) {			tdb_off current;			if (rec_read(tdb, tlock->off, rec) == -1)				goto fail;			if (!TDB_DEAD(rec)) {				/* Woohoo: we found one! */				if (lock_record(tdb, tlock->off) != 0)					goto fail;				return tlock->off;			}			/* Try to clean dead ones from old traverses */			current = tlock->off;			tlock->off = rec->next;			if (!tdb->read_only && 			    do_delete(tdb, current, rec) != 0)				goto fail;		}		tdb_unlock(tdb, tlock->hash, F_WRLCK);		want_next = 0;	}	/* We finished iteration without finding anything */	return TDB_ERRCODE(TDB_SUCCESS, 0); fail:	tlock->off = 0;	if (tdb_unlock(tdb, tlock->hash, F_WRLCK) != 0)		TDB_LOG((tdb, 0, "tdb_next_lock: On error unlock failed!\n"));	return -1;}/* 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, tdb_traverse_func fn, void *private){	TDB_DATA key, dbuf;	struct list_struct rec;	struct tdb_traverse_lock tl = { NULL, 0, 0 };	int ret, count = 0;	/* This was in the initializaton, above, but the IRIX compiler	 * did not like it.  crh	 */	tl.next = tdb->travlocks.next;	/* fcntl locks don't stack: beware traverse inside traverse */	tdb->travlocks.next = &tl;	/* tdb_next_lock places locks on the record returned, and its chain */	while ((ret = tdb_next_lock(tdb, &tl, &rec)) > 0) {		count++;		/* now read the full record */		key.dptr = tdb_alloc_read(tdb, tl.off + sizeof(rec), 					  rec.key_len + rec.data_len);		if (!key.dptr) {			ret = -1;			if (tdb_unlock(tdb, tl.hash, F_WRLCK) != 0)				goto out;			if (unlock_record(tdb, tl.off) != 0)				TDB_LOG((tdb, 0, "tdb_traverse: key.dptr == NULL and unlock_record failed!\n"));			goto out;		}		key.dsize = rec.key_len;		dbuf.dptr = key.dptr + rec.key_len;		dbuf.dsize = rec.data_len;		/* Drop chain lock, call out */		if (tdb_unlock(tdb, tl.hash, F_WRLCK) != 0) {			ret = -1;			goto out;		}		if (fn && fn(tdb, key, dbuf, private)) {			/* They want us to terminate traversal */			ret = count;			if (unlock_record(tdb, tl.off) != 0) {				TDB_LOG((tdb, 0, "tdb_traverse: unlock_record failed!\n"));;				ret = -1;			}			tdb->travlocks.next = tl.next;			SAFE_FREE(key.dptr);			return count;		}		SAFE_FREE(key.dptr);	}out:	tdb->travlocks.next = tl.next;	if (ret < 0)		return -1;	else		return count;}/* find the first entry in the database and return its key */TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb){	TDB_DATA key;	struct list_struct rec;	/* release any old lock */	if (unlock_record(tdb, tdb->travlocks.off) != 0)		return tdb_null;	tdb->travlocks.off = tdb->travlocks.hash = 0;	if (tdb_next_lock(tdb, &tdb->travlocks, &rec) <= 0)		return tdb_null;	/* now read the key */	key.dsize = rec.key_len;	key.dptr =tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),key.dsize);	if (tdb_unlock(tdb, BUCKET(tdb->travlocks.hash), F_WRLCK) != 0)		TDB_LOG((tdb, 0, "tdb_firstkey: error occurred while tdb_unlocking!\n"));	return key;}/* find the next entry in the database, returning its key */TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA oldkey){	u32 oldhash;	TDB_DATA key = tdb_null;	struct list_struct rec;	char *k = NULL;	/* Is locked key the old key?  If so, traverse will be reliable. */	if (tdb->travlocks.off) {		if (tdb_lock(tdb,tdb->travlocks.hash,F_WRLCK))			return tdb_null;		if (rec_read(tdb, tdb->travlocks.off, &rec) == -1		    || !(k = tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),					    rec.key_len))		    || memcmp(k, oldkey.dptr, oldkey.dsize) != 0) {			/* No, it wasn't: unlock it and start from scratch */			if (unlock_record(tdb, tdb->travlocks.off) != 0)				return tdb_null;			if (tdb_unlock(tdb, tdb->travlocks.hash, F_WRLCK) != 0)				return tdb_null;			tdb->travlocks.off = 0;		}		SAFE_FREE(k);	}	if (!tdb->travlocks.off) {		/* No previous element: do normal find, and lock record */		tdb->travlocks.off = tdb_find_lock_hash(tdb, oldkey, tdb->hash_fn(&oldkey), F_WRLCK, &rec);		if (!tdb->travlocks.off)			return tdb_null;		tdb->travlocks.hash = BUCKET(rec.full_hash);		if (lock_record(tdb, tdb->travlocks.off) != 0) {			TDB_LOG((tdb, 0, "tdb_nextkey: lock_record failed (%s)!\n", strerror(errno)));			return tdb_null;		}	}	oldhash = tdb->travlocks.hash;	/* Grab next record: locks chain and returned record,	   unlocks old record */	if (tdb_next_lock(tdb, &tdb->travlocks, &rec) > 0) {		key.dsize = rec.key_len;		key.dptr = tdb_alloc_read(tdb, tdb->travlocks.off+sizeof(rec),					  key.dsize);		/* Unlock the chain of this new record */		if (tdb_unlock(tdb, tdb->travlocks.hash, F_WRLCK) != 0)			TDB_LOG((tdb, 0, "tdb_nextkey: WARNING tdb_unlock failed!\n"));	}	/* Unlock the chain of old record */	if (tdb_unlock(tdb, BUCKET(oldhash), F_WRLCK) != 0)		TDB_LOG((tdb, 0, "tdb_nextkey: WARNING tdb_unlock failed!\n"));	return key;}/* delete an entry in the database given a key */static int tdb_delete_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash){	tdb_off rec_ptr;	struct list_struct rec;	int ret;	if (!(rec_ptr = tdb_find_lock_hash(tdb, key, hash, F_WRLCK, &rec)))		return -1;	ret = do_delete(tdb, rec_ptr, &rec);	if (tdb_unlock(tdb, BUCKET(rec.full_hash), F_WRLCK) != 0)		TDB_LOG((tdb, 0, "tdb_delete: WARNING tdb_unlock failed!\n"));	return ret;}int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key){	u32 hash = tdb->hash_fn(&key);	return tdb_delete_hash(tdb, key, hash);}/* 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;	u32 hash;	tdb_off rec_ptr;	char *p = NULL;	int ret = 0;	/* find which hash bucket it is in */	hash = tdb->hash_fn(&key);	if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)		return -1;	/* check for it existing, on insert. */	if (flag == TDB_INSERT) {		if (tdb_exists_hash(tdb, key, hash)) {			tdb->ecode = TDB_ERR_EXISTS;			goto fail;		}	} else {		/* first try in-place update, on modify or replace. */		if (tdb_update_hash(tdb, key, hash, dbuf) == 0)			goto out;		if (tdb->ecode == TDB_ERR_NOEXIST &&		    flag == TDB_MODIFY) {			/* if the record doesn't exist and we are in TDB_MODIFY mode then			 we should fail the store */			goto fail;	}	}	/* reset the error code potentially set by the tdb_update() */	tdb->ecode = TDB_SUCCESS;	/* delete any existing record - if it doesn't exist we don't           care.  Doing this first reduces fragmentation, and avoids           coalescing with `allocated' block before it's updated. */	if (flag != TDB_INSERT)		tdb_delete_hash(tdb, key, hash);	/* Copy key+value *before* allocating free space in case malloc	   fails and we are left with a dead spot in the tdb. */	if (!(p = (char *)malloc(key.dsize + dbuf.dsize))) {		tdb->ecode = TDB_ERR_OOM;		goto fail;	}	memcpy(p, key.dptr, key.dsize);	if (dbuf.dsize)		memcpy(p+key.dsize, dbuf.dptr, dbuf.dsize);	/* we have to allocate some space */	if (!(rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize, &rec)))		goto fail;	/* Read hash top into next ptr */	if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1)		goto fail;	rec.key_len = key.dsize;	rec.data_len = dbuf.dsize;	rec.full_hash = hash;	rec.magic = TDB_MAGIC;	/* write out and point the top of the hash chain at it */	if (rec_write(tdb, rec_ptr, &rec) == -1	    || tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+dbuf.dsize)==-1	    || ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) {		/* Need to tdb_unallocate() here */		goto fail;	} out:	SAFE_FREE(p); 	tdb_unlock(tdb, BUCKET(hash), F_WRLCK);	return ret;fail:	ret = -1;	goto out;}/* Attempt to append data to an entry in place - this only works if the new data size   is <= the old data size and the key exists.   on failure return -1. Record must be locked before calling.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -