示例#1
0
/* read a freelist record and check for simple errors */
int tdb1_rec_free_read(struct tdb_context *tdb, tdb1_off_t off, struct tdb1_record *rec)
{
	if (tdb->tdb1.io->tdb1_read(tdb, off, rec, sizeof(*rec),TDB1_DOCONV()) == -1)
		return -1;

	if (rec->magic == TDB1_MAGIC) {
		/* this happens when a app is showdown while deleting a record - we should
		   not completely fail when this happens */
		tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_WARNING,
			   "tdb1_rec_free_read non-free magic 0x%x at offset=%d - fixing\n",
			   rec->magic, off);
		rec->magic = TDB1_FREE_MAGIC;
		if (tdb->tdb1.io->tdb1_write(tdb, off, rec, sizeof(*rec)) == -1)
			return -1;
	}

	if (rec->magic != TDB1_FREE_MAGIC) {
		tdb->last_error = tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
					"tdb1_rec_free_read bad magic 0x%x at offset=%d\n",
					rec->magic, off);
		return -1;
	}
	if (tdb->tdb1.io->tdb1_oob(tdb, rec->next+sizeof(*rec), 0) != 0)
		return -1;
	return 0;
}
示例#2
0
文件: tdb.c 项目: Arkhont/samba
void tdb_remove_flag(struct tdb_context *tdb, unsigned flag)
{
	if (tdb->flags & TDB_INTERNAL) {
		tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
					     TDB_LOG_USE_ERROR,
					     "tdb_remove_flag: internal db");
		return;
	}
	switch (flag) {
	case TDB_NOLOCK:
		tdb->flags &= ~TDB_NOLOCK;
		break;
	case TDB_NOMMAP:
		tdb->flags &= ~TDB_NOMMAP;
		tdb_mmap(tdb);
		break;
	case TDB_NOSYNC:
		tdb->flags &= ~TDB_NOSYNC;
		break;
	case TDB_SEQNUM:
		tdb->flags &= ~TDB_SEQNUM;
		break;
	case TDB_ALLOW_NESTING:
		tdb->flags &= ~TDB_ALLOW_NESTING;
		break;
	default:
		tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
					     TDB_LOG_USE_ERROR,
					     "tdb_remove_flag: Unknown flag %u",
					     flag);
	}
}
示例#3
0
文件: tdb.c 项目: stewartsmith/ccan
static uint64_t random_number(struct tdb_context *tdb)
{
	int fd;
	uint64_t ret = 0;
	struct timeval now;

	fd = open("/dev/urandom", O_RDONLY);
	if (fd >= 0) {
		if (tdb_read_all(fd, &ret, sizeof(ret))) {
			tdb_logerr(tdb, TDB_SUCCESS, TDB_DEBUG_TRACE,
				 "tdb_open: random from /dev/urandom");
			close(fd);
			return ret;
		}
		close(fd);
	}
	/* FIXME: Untested!  Based on Wikipedia protocol description! */
	fd = open("/dev/egd-pool", O_RDWR);
	if (fd >= 0) {
		/* Command is 1, next byte is size we want to read. */
		char cmd[2] = { 1, sizeof(uint64_t) };
		if (write(fd, cmd, sizeof(cmd)) == sizeof(cmd)) {
			char reply[1 + sizeof(uint64_t)];
			int r = read(fd, reply, sizeof(reply));
			if (r > 1) {
				tdb_logerr(tdb, TDB_SUCCESS, TDB_DEBUG_TRACE,
					   "tdb_open: %u random bytes from"
					   " /dev/egd-pool", r-1);
				/* Copy at least some bytes. */
				memcpy(&ret, reply+1, r - 1);
				if (reply[0] == sizeof(uint64_t)
				    && r == sizeof(reply)) {
					close(fd);
					return ret;
				}
			}
		}
		close(fd);
	}

	/* Fallback: pid and time. */
	gettimeofday(&now, NULL);
	ret = getpid() * 100132289ULL + now.tv_sec * 1000000ULL + now.tv_usec;
	tdb_logerr(tdb, TDB_SUCCESS, TDB_DEBUG_TRACE,
		   "tdb_open: random from getpid and time");
	return ret;
}
示例#4
0
文件: tdb.c 项目: atlant2011/samba
void tdb_remove_flag(struct tdb_context *tdb, unsigned flag)
{
	if (tdb->flags & TDB_INTERNAL) {
		tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
					     TDB_LOG_USE_ERROR,
					     "tdb_remove_flag: internal db");
		return;
	}
	switch (flag) {
	case TDB_NOLOCK:
		tdb->flags &= ~TDB_NOLOCK;
		break;
	case TDB_NOMMAP:
		tdb->flags &= ~TDB_NOMMAP;
		tdb_mmap(tdb);
		break;
	case TDB_NOSYNC:
		tdb->flags &= ~TDB_NOSYNC;
		break;
	case TDB_SEQNUM:
		tdb->flags &= ~TDB_SEQNUM;
		break;
	case TDB_ALLOW_NESTING:
		tdb->flags &= ~TDB_ALLOW_NESTING;
		break;
	case TDB_RDONLY:
		if ((tdb->open_flags & O_ACCMODE) == O_RDONLY) {
			tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
						     TDB_LOG_USE_ERROR,
						     "tdb_remove_flag: can't"
						     " remove TDB_RDONLY on tdb"
						     " opened with O_RDONLY");
			break;
		}
		if (readonly_changable(tdb, "tdb_remove_flag"))
			tdb->flags &= ~TDB_RDONLY;
		break;
	default:
		tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
					     TDB_LOG_USE_ERROR,
					     "tdb_remove_flag: Unknown flag %u",
					     flag);
	}
}
示例#5
0
文件: tdb.c 项目: atlant2011/samba
static bool readonly_changable(struct tdb_context *tdb, const char *caller)
{
	if (inside_transaction(tdb)) {
		tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
					     TDB_LOG_USE_ERROR,
					     "%s: can't change"
					     " TDB_RDONLY inside transaction",
					     caller);
		return false;
	}
	return true;
}
示例#6
0
/* delete an entry in the database given a key */
static int tdb1_delete_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash)
{
	tdb1_off_t rec_ptr;
	struct tdb1_record rec;
	int ret;

	if (tdb->tdb1.max_dead_records != 0) {

		/*
		 * Allow for some dead records per hash chain, mainly for
		 * tdb's with a very high create/delete rate like locking.tdb.
		 */

		if (tdb1_lock(tdb, TDB1_BUCKET(hash), F_WRLCK) == -1)
			return -1;

		if (tdb1_count_dead(tdb, hash) >= tdb->tdb1.max_dead_records) {
			/*
			 * Don't let the per-chain freelist grow too large,
			 * delete all existing dead records
			 */
			tdb1_purge_dead(tdb, hash);
		}

		if (!(rec_ptr = tdb1_find(tdb, key, hash, &rec))) {
			tdb1_unlock(tdb, TDB1_BUCKET(hash), F_WRLCK);
			return -1;
		}

		/*
		 * Just mark the record as dead.
		 */
		rec.magic = TDB1_DEAD_MAGIC;
		ret = tdb1_rec_write(tdb, rec_ptr, &rec);
	}
	else {
		if (!(rec_ptr = tdb1_find_lock_hash(tdb, key, hash, F_WRLCK,
						   &rec)))
			return -1;

		ret = tdb1_do_delete(tdb, rec_ptr, &rec);
	}

	if (ret == 0) {
		tdb1_increment_seqnum(tdb);
	}

	if (tdb1_unlock(tdb, TDB1_BUCKET(rec.full_hash), F_WRLCK) != 0)
		tdb_logerr(tdb, tdb->last_error, TDB_LOG_ERROR,
			   "tdb1_delete: WARNING tdb1_unlock failed!");
	return ret;
}
示例#7
0
/*
  add a region of the file to the freelist. Length is the size of the region in bytes,
  which includes the free list header that needs to be added
 */
static int tdb1_free_region(struct tdb_context *tdb, tdb1_off_t offset, ssize_t length)
{
	struct tdb1_record rec;
	if (length <= sizeof(rec)) {
		/* the region is not worth adding */
		return 0;
	}
	if (length + offset > tdb->file->map_size) {
		tdb->last_error = tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
					"tdb1_free_region: adding region beyond"
					" end of file");
		return -1;
	}
	memset(&rec,'\0',sizeof(rec));
	rec.rec_len = length - sizeof(rec);
	if (tdb1_free(tdb, offset, &rec) == -1) {
		tdb_logerr(tdb, tdb->last_error, TDB_LOG_ERROR,
			   "tdb1_free_region: failed to add free record");
		return -1;
	}
	return 0;
}
示例#8
0
/* Returns 0 on fail; last_error will be TDB_ERR_NOEXIST if it simply
 * wasn't there, otherwise a real error.
 * On success, return offset of record, and fills in rec */
static tdb1_off_t tdb1_find(struct tdb_context *tdb, TDB_DATA key, uint32_t hash,
			struct tdb1_record *r)
{
	tdb1_off_t rec_ptr;

	/* read in the hash top */
	if (tdb1_ofs_read(tdb, TDB1_HASH_TOP(hash), &rec_ptr) == -1)
		return 0;

	/* keep looking until we find the right record */
	while (rec_ptr) {
		if (tdb1_rec_read(tdb, rec_ptr, r) == -1)
			return 0;

		tdb->stats.compares++;
		if (TDB1_DEAD(r)) {
			tdb->stats.compare_wrong_bucket++;
		} else if (key.dsize != r->key_len) {
			tdb->stats.compare_wrong_keylen++;
		} else if (hash != r->full_hash) {
			tdb->stats.compare_wrong_rechash++;
		} else {
			enum TDB_ERROR ecode;
			bool matches;
			ecode = tdb1_parse_data(tdb, key, rec_ptr + sizeof(*r),
						r->key_len, tdb1_key_compare,
						&matches);

			if (ecode != TDB_SUCCESS) {
				tdb->last_error = ecode;
				return 0;
			}

			if (!matches) {
				tdb->stats.compare_wrong_keycmp++;
			} else {
				return rec_ptr;
			}
		}
		/* detect tight infinite loop */
		if (rec_ptr == r->next) {
			tdb->last_error = tdb_logerr(tdb, TDB_ERR_CORRUPT,
						TDB_LOG_ERROR,
						"tdb1_find: loop detected.");
			return 0;
		}
		rec_ptr = r->next;
	}
	tdb->last_error = TDB_ERR_NOEXIST;
	return 0;
}
示例#9
0
文件: tdb.c 项目: atlant2011/samba
void tdb_add_flag(struct tdb_context *tdb, unsigned flag)
{
	if (tdb->flags & TDB_INTERNAL) {
		tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
					     TDB_LOG_USE_ERROR,
					     "tdb_add_flag: internal db");
		return;
	}
	switch (flag) {
	case TDB_NOLOCK:
		tdb->flags |= TDB_NOLOCK;
		break;
	case TDB_NOMMAP:
		tdb->flags |= TDB_NOMMAP;
		tdb_munmap(tdb->file);
		break;
	case TDB_NOSYNC:
		tdb->flags |= TDB_NOSYNC;
		break;
	case TDB_SEQNUM:
		tdb->flags |= TDB_SEQNUM;
		break;
	case TDB_ALLOW_NESTING:
		tdb->flags |= TDB_ALLOW_NESTING;
		break;
	case TDB_RDONLY:
		if (readonly_changable(tdb, "tdb_add_flag"))
			tdb->flags |= TDB_RDONLY;
		break;
	default:
		tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
					     TDB_LOG_USE_ERROR,
					     "tdb_add_flag: Unknown flag %u",
					     flag);
	}
}
示例#10
0
文件: tdb.c 项目: atlant2011/samba
enum TDB_ERROR tdb_repack(struct tdb_context *tdb)
{
	struct tdb_context *tmp_db;
	struct traverse_state state;

	state.error = tdb_transaction_start(tdb);
	if (state.error != TDB_SUCCESS) {
		return state.error;
	}

	tmp_db = tdb_open("tmpdb", TDB_INTERNAL, O_RDWR|O_CREAT, 0, NULL);
	if (tmp_db == NULL) {
		state.error = tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
					 __location__
					 " Failed to create tmp_db");
		tdb_transaction_cancel(tdb);
		return tdb->last_error = state.error;
	}

	state.dest_db = tmp_db;
	if (tdb_traverse(tdb, repack_traverse, &state) < 0) {
		goto fail;
	}

	state.error = tdb_wipe_all(tdb);
	if (state.error != TDB_SUCCESS) {
		goto fail;
	}

	state.dest_db = tdb;
	if (tdb_traverse(tmp_db, repack_traverse, &state) < 0) {
		goto fail;
	}

	tdb_close(tmp_db);
	return tdb_transaction_commit(tdb);

fail:
	tdb_transaction_cancel(tdb);
	tdb_close(tmp_db);
	return state.error;
}
示例#11
0
/* store an element in the database, replacing any existing element
   with the same key

   return 0 on success, -1 on failure
*/
int tdb1_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
{
	uint32_t hash;
	int ret;

	assert(tdb->flags & TDB_VERSION1);

	if ((tdb->flags & TDB_RDONLY) || tdb->tdb1.traverse_read) {
		tdb->last_error = tdb_logerr(tdb, TDB_ERR_RDONLY,
					     TDB_LOG_USE_ERROR,
					     "tdb_store: read-only tdb");
		return -1;
	}

	/* find which hash bucket it is in */
	hash = tdb_hash(tdb, key.dptr, key.dsize);
	if (tdb1_lock(tdb, TDB1_BUCKET(hash), F_WRLCK) == -1)
		return -1;

	ret = _tdb1_store(tdb, key, dbuf, flag, hash);
	tdb1_unlock(tdb, TDB1_BUCKET(hash), F_WRLCK);
	return ret;
}
示例#12
0
文件: tdb.c 项目: Arkhont/samba
enum TDB_ERROR tdb_append(struct tdb_context *tdb,
			  struct tdb_data key, struct tdb_data dbuf)
{
	struct hash_info h;
	tdb_off_t off;
	struct tdb_used_record rec;
	tdb_len_t old_room = 0, old_dlen;
	unsigned char *newdata;
	struct tdb_data new_dbuf;
	enum TDB_ERROR ecode;

	off = find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL);
	if (TDB_OFF_IS_ERR(off)) {
		return tdb->last_error = off;
	}

	if (off) {
		old_dlen = rec_data_length(&rec);
		old_room = old_dlen + rec_extra_padding(&rec);

		/* Fast path: can append in place. */
		if (rec_extra_padding(&rec) >= dbuf.dsize) {
			ecode = update_rec_hdr(tdb, off, key.dsize,
					       old_dlen + dbuf.dsize, &rec,
					       h.h);
			if (ecode != TDB_SUCCESS) {
				goto out;
			}

			off += sizeof(rec) + key.dsize + old_dlen;
			ecode = update_data(tdb, off, dbuf,
					    rec_extra_padding(&rec));
			goto out;
		}

		/* Slow path. */
		newdata = malloc(key.dsize + old_dlen + dbuf.dsize);
		if (!newdata) {
			ecode = tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
					   "tdb_append:"
					   " failed to allocate %zu bytes",
					   (size_t)(key.dsize + old_dlen
						    + dbuf.dsize));
			goto out;
		}
		ecode = tdb->methods->tread(tdb, off + sizeof(rec) + key.dsize,
					    newdata, old_dlen);
		if (ecode != TDB_SUCCESS) {
			goto out_free_newdata;
		}
		memcpy(newdata + old_dlen, dbuf.dptr, dbuf.dsize);
		new_dbuf.dptr = newdata;
		new_dbuf.dsize = old_dlen + dbuf.dsize;
	} else {
		newdata = NULL;
		new_dbuf = dbuf;
	}

	/* If they're using tdb_append(), it implies they're growing record. */
	ecode = replace_data(tdb, &h, key, new_dbuf, off, old_room, true);

out_free_newdata:
	free(newdata);
out:
	tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_WRLCK);
	return tdb->last_error = ecode;
}
示例#13
0
/*
  wipe the entire database, deleting all records. This can be done
  very fast by using a allrecord lock. The entire data portion of the
  file becomes a single entry in the freelist.

  This code carefully steps around the recovery area, leaving it alone
 */
int tdb1_wipe_all(struct tdb_context *tdb)
{
	int i;
	tdb1_off_t offset = 0;
	ssize_t data_len;
	tdb1_off_t recovery_head;
	tdb1_len_t recovery_size = 0;

	if (tdb_lockall(tdb) != TDB_SUCCESS) {
		return -1;
	}


	/* see if the tdb has a recovery area, and remember its size
	   if so. We don't want to lose this as otherwise each
	   tdb1_wipe_all() in a transaction will increase the size of
	   the tdb by the size of the recovery area */
	if (tdb1_ofs_read(tdb, TDB1_RECOVERY_HEAD, &recovery_head) == -1) {
		tdb_logerr(tdb, tdb->last_error, TDB_LOG_ERROR,
			   "tdb1_wipe_all: failed to read recovery head");
		goto failed;
	}

	if (recovery_head != 0) {
		struct tdb1_record rec;
		if (tdb->tdb1.io->tdb1_read(tdb, recovery_head, &rec, sizeof(rec), TDB1_DOCONV()) == -1) {
			tdb_logerr(tdb, tdb->last_error, TDB_LOG_ERROR,
				   "tdb1_wipe_all: failed to read recovery record");
			return -1;
		}
		recovery_size = rec.rec_len + sizeof(rec);
	}

	/* wipe the hashes */
	for (i=0;i<tdb->tdb1.header.hash_size;i++) {
		if (tdb1_ofs_write(tdb, TDB1_HASH_TOP(i), &offset) == -1) {
			tdb_logerr(tdb, tdb->last_error, TDB_LOG_ERROR,
				   "tdb1_wipe_all: failed to write hash %d", i);
			goto failed;
		}
	}

	/* wipe the freelist */
	if (tdb1_ofs_write(tdb, TDB1_FREELIST_TOP, &offset) == -1) {
		tdb_logerr(tdb, tdb->last_error, TDB_LOG_ERROR,
			   "tdb1_wipe_all: failed to write freelist");
		goto failed;
	}

	/* add all the rest of the file to the freelist, possibly leaving a gap
	   for the recovery area */
	if (recovery_size == 0) {
		/* the simple case - the whole file can be used as a freelist */
		data_len = (tdb->file->map_size - TDB1_DATA_START(tdb->tdb1.header.hash_size));
		if (tdb1_free_region(tdb, TDB1_DATA_START(tdb->tdb1.header.hash_size), data_len) != 0) {
			goto failed;
		}
	} else {
		/* we need to add two freelist entries - one on either
		   side of the recovery area

		   Note that we cannot shift the recovery area during
		   this operation. Only the transaction.c code may
		   move the recovery area or we risk subtle data
		   corruption
		*/
		data_len = (recovery_head - TDB1_DATA_START(tdb->tdb1.header.hash_size));
		if (tdb1_free_region(tdb, TDB1_DATA_START(tdb->tdb1.header.hash_size), data_len) != 0) {
			goto failed;
		}
		/* and the 2nd free list entry after the recovery area - if any */
		data_len = tdb->file->map_size - (recovery_head+recovery_size);
		if (tdb1_free_region(tdb, recovery_head+recovery_size, data_len) != 0) {
			goto failed;
		}
	}

	tdb1_increment_seqnum_nonblock(tdb);
	tdb_unlockall(tdb);
	return 0;

failed:
	tdb_unlockall(tdb);
	return -1;
}
示例#14
0
static int _tdb1_store(struct tdb_context *tdb, TDB_DATA key,
		       TDB_DATA dbuf, int flag, uint32_t hash)
{
	struct tdb1_record rec;
	tdb1_off_t rec_ptr;
	char *p = NULL;
	int ret = -1;

	/* check for it existing, on insert. */
	if (flag == TDB_INSERT) {
		if (tdb1_exists_hash(tdb, key, hash)) {
			tdb->last_error = TDB_ERR_EXISTS;
			goto fail;
		}
		if (tdb->last_error != TDB_ERR_NOEXIST) {
			goto fail;
		}
	} else {
		/* first try in-place update, on modify or replace. */
		if (tdb1_update_hash(tdb, key, hash, dbuf) == 0) {
			goto done;
		}
		if (tdb->last_error != TDB_SUCCESS) {
			if (tdb->last_error != TDB_ERR_NOEXIST) {
				goto fail;
			}
			if (flag == TDB_MODIFY) {
				/* if the record doesn't exist and we are in TDB1_MODIFY mode then
				   we should fail the store */
				goto fail;
			}
		}
	}
	/* reset the error code potentially set by the tdb1_update() */
	tdb->last_error = 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)
		tdb1_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->last_error = tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
					     "tdb1_store: out of memory"
					     " allocating copy");
		goto fail;
	}

	memcpy(p, key.dptr, key.dsize);
	if (dbuf.dsize)
		memcpy(p+key.dsize, dbuf.dptr, dbuf.dsize);

	if (tdb->tdb1.max_dead_records != 0) {
		/*
		 * Allow for some dead records per hash chain, look if we can
		 * find one that can hold the new record. We need enough space
		 * for key, data and tailer. If we find one, we don't have to
		 * consult the central freelist.
		 */
		rec_ptr = tdb1_find_dead(
			tdb, hash, &rec,
			key.dsize + dbuf.dsize + sizeof(tdb1_off_t));

		if (rec_ptr != 0) {
			rec.key_len = key.dsize;
			rec.data_len = dbuf.dsize;
			rec.full_hash = hash;
			rec.magic = TDB1_MAGIC;
			if (tdb1_rec_write(tdb, rec_ptr, &rec) == -1
			    || tdb->tdb1.io->tdb1_write(
				    tdb, rec_ptr + sizeof(rec),
				    p, key.dsize + dbuf.dsize) == -1) {
				goto fail;
			}
			goto done;
		}
	}

	/*
	 * We have to allocate some space from the freelist, so this means we
	 * have to lock it. Use the chance to purge all the DEAD records from
	 * the hash chain under the freelist lock.
	 */

	if (tdb1_lock(tdb, -1, F_WRLCK) == -1) {
		goto fail;
	}

	if ((tdb->tdb1.max_dead_records != 0)
	    && (tdb1_purge_dead(tdb, hash) == -1)) {
		tdb1_unlock(tdb, -1, F_WRLCK);
		goto fail;
	}

	/* we have to allocate some space */
	rec_ptr = tdb1_allocate(tdb, key.dsize + dbuf.dsize, &rec);

	tdb1_unlock(tdb, -1, F_WRLCK);

	if (rec_ptr == 0) {
		goto fail;
	}

	/* Read hash top into next ptr */
	if (tdb1_ofs_read(tdb, TDB1_HASH_TOP(hash), &rec.next) == -1)
		goto fail;

	rec.key_len = key.dsize;
	rec.data_len = dbuf.dsize;
	rec.full_hash = hash;
	rec.magic = TDB1_MAGIC;

	/* write out and point the top of the hash chain at it */
	if (tdb1_rec_write(tdb, rec_ptr, &rec) == -1
	    || tdb->tdb1.io->tdb1_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+dbuf.dsize)==-1
	    || tdb1_ofs_write(tdb, TDB1_HASH_TOP(hash), &rec_ptr) == -1) {
		/* Need to tdb1_unallocate() here */
		goto fail;
	}

 done:
	ret = 0;
 fail:
	if (ret == 0) {
		tdb1_increment_seqnum(tdb);
	}

	SAFE_FREE(p);
	return ret;
}
示例#15
0
char *tdb1_summary(struct tdb_context *tdb)
{
	tdb1_off_t off, rec_off;
	struct tally freet, keys, data, dead, extra, hash, uncoal;
	struct tdb1_record rec;
	char *ret = NULL;
	bool locked;
	size_t len, unc = 0;
	struct tdb1_record recovery;

	/* We may have a write lock already, so don't lock. */
	if (tdb->file->allrecord_lock.count != 0) {
		locked = false;
	} else {
		if (tdb_lockall_read(tdb) != TDB_SUCCESS)
			return NULL;
		locked = true;
	}

	if (tdb1_recovery_area(tdb, tdb->tdb1.io, &rec_off, &recovery) != 0) {
		goto unlock;
	}

	tally1_init(&freet);
	tally1_init(&keys);
	tally1_init(&data);
	tally1_init(&dead);
	tally1_init(&extra);
	tally1_init(&hash);
	tally1_init(&uncoal);

	for (off = TDB1_DATA_START(tdb->tdb1.header.hash_size);
	     off < tdb->file->map_size - 1;
	     off += sizeof(rec) + rec.rec_len) {
		if (tdb->tdb1.io->tdb1_read(tdb, off, &rec, sizeof(rec),
					   TDB1_DOCONV()) == -1)
			goto unlock;
		switch (rec.magic) {
		case TDB1_MAGIC:
			tally1_add(&keys, rec.key_len);
			tally1_add(&data, rec.data_len);
			tally1_add(&extra, rec.rec_len - (rec.key_len
							 + rec.data_len));
			if (unc > 1)
				tally1_add(&uncoal, unc - 1);
			unc = 0;
			break;
		case TDB1_FREE_MAGIC:
			tally1_add(&freet, rec.rec_len);
			unc++;
			break;
		/* If we crash after ftruncate, we can get zeroes or fill. */
		case TDB1_RECOVERY_INVALID_MAGIC:
		case 0x42424242:
			unc++;
			/* If it's a valid recovery, we can trust rec_len. */
			if (off != rec_off) {
				rec.rec_len = tdb1_dead_space(tdb, off)
					- sizeof(rec);
			}
			/* Fall through */
		case TDB1_DEAD_MAGIC:
			tally1_add(&dead, rec.rec_len);
			break;
		default:
			tdb->last_error = tdb_logerr(tdb, TDB_ERR_CORRUPT,
						TDB_LOG_ERROR,
						"Unexpected record magic 0x%x"
						" at offset %d",
						rec.magic, off);
			goto unlock;
		}
	}
	if (unc > 1)
		tally1_add(&uncoal, unc - 1);

	for (off = 0; off < tdb->tdb1.header.hash_size; off++)
		tally1_add(&hash, get_hash_length(tdb, off));

	/* 20 is max length of a %zu. */
	len = strlen(SUMMARY_FORMAT1) + 35*20 + 1;
	ret = (char *)malloc(len);
	if (!ret)
		goto unlock;

	snprintf(ret, len, SUMMARY_FORMAT1,
		 (tdb1_len_t)tdb->file->map_size, keys.total+data.total,
		 keys.num,
		 keys.min, tally1_mean(&keys), keys.max,
		 data.min, tally1_mean(&data), data.max,
		 extra.min, tally1_mean(&extra), extra.max,
		 dead.num,
		 dead.min, tally1_mean(&dead), dead.max,
		 freet.num,
		 freet.min, tally1_mean(&freet), freet.max,
		 hash.num,
		 hash.min, tally1_mean(&hash), hash.max,
		 uncoal.total,
		 uncoal.min, tally1_mean(&uncoal), uncoal.max,
		 keys.total * 100.0 / tdb->file->map_size,
		 data.total * 100.0 / tdb->file->map_size,
		 extra.total * 100.0 / tdb->file->map_size,
		 freet.total * 100.0 / tdb->file->map_size,
		 dead.total * 100.0 / tdb->file->map_size,
		 (keys.num + freet.num + dead.num)
		 * (sizeof(struct tdb1_record) + sizeof(uint32_t))
		 * 100.0 / tdb->file->map_size,
		 tdb->tdb1.header.hash_size * sizeof(tdb1_off_t)
		 * 100.0 / (tdb1_len_t)tdb->file->map_size);

unlock:
	if (locked) {
		tdb_unlockall_read(tdb);
	}
	return ret;
}
示例#16
0
/* Add an element into the freelist. Merge adjacent records if
   necessary. */
int tdb1_free(struct tdb_context *tdb, tdb1_off_t offset, struct tdb1_record *rec)
{
	/* Allocation and tailer lock */
	if (tdb1_lock(tdb, -1, F_WRLCK) != 0)
		return -1;

	/* set an initial tailer, so if we fail we don't leave a bogus record */
	if (update_tailer(tdb, offset, rec) != 0) {
		tdb_logerr(tdb, tdb->last_error, TDB_LOG_ERROR,
			   "tdb_free: update_tailer failed!\n");
		goto fail;
	}

	tdb->stats.alloc_coalesce_tried++;
	/* Look left */
	if (offset - sizeof(tdb1_off_t) > TDB1_DATA_START(tdb->tdb1.header.hash_size)) {
		tdb1_off_t left = offset - sizeof(tdb1_off_t);
		struct tdb1_record l;
		tdb1_off_t leftsize;

		/* Read in tailer and jump back to header */
		if (tdb1_ofs_read(tdb, left, &leftsize) == -1) {
			tdb_logerr(tdb, tdb->last_error, TDB_LOG_ERROR,
				   "tdb1_free: left offset read failed at %u", left);
			goto update;
		}

		/* it could be uninitialised data */
		if (leftsize == 0 || leftsize == TDB1_PAD_U32) {
			goto update;
		}

		left = offset - leftsize;

		if (leftsize > offset ||
		    left < TDB1_DATA_START(tdb->tdb1.header.hash_size)) {
			goto update;
		}

		/* Now read in the left record */
		if (tdb->tdb1.io->tdb1_read(tdb, left, &l, sizeof(l), TDB1_DOCONV()) == -1) {
			tdb_logerr(tdb, tdb->last_error, TDB_LOG_ERROR,
				   "tdb1_free: left read failed at %u (%u)", left, leftsize);
			goto update;
		}

		/* If it's free, expand to include it. */
		if (l.magic == TDB1_FREE_MAGIC) {
			/* we now merge the new record into the left record, rather than the other
			   way around. This makes the operation O(1) instead of O(n). This change
			   prevents traverse from being O(n^2) after a lot of deletes */
			l.rec_len += sizeof(*rec) + rec->rec_len;
			if (tdb1_rec_write(tdb, left, &l) == -1) {
				tdb_logerr(tdb, tdb->last_error, TDB_LOG_ERROR,
					   "tdb1_free: update_left failed at %u", left);
				goto fail;
			}
			if (update_tailer(tdb, left, &l) == -1) {
				tdb_logerr(tdb, tdb->last_error, TDB_LOG_ERROR,
					   "tdb1_free: update_tailer failed at %u", offset);
				goto fail;
			}
			tdb->stats.alloc_coalesce_succeeded++;
			tdb->stats.alloc_coalesce_num_merged++;
			tdb->stats.frees++;
			tdb1_unlock(tdb, -1, F_WRLCK);
			return 0;
		}
	}

update:

	/* Now, prepend to free list */
	rec->magic = TDB1_FREE_MAGIC;

	if (tdb1_ofs_read(tdb, TDB1_FREELIST_TOP, &rec->next) == -1 ||
	    tdb1_rec_write(tdb, offset, rec) == -1 ||
	    tdb1_ofs_write(tdb, TDB1_FREELIST_TOP, &offset) == -1) {
		tdb_logerr(tdb, tdb->last_error, TDB_LOG_ERROR,
			   "tdb1_free record write failed at offset=%d",
			   offset);
		goto fail;
	}

	/* And we're done. */
	tdb->stats.frees++;
	tdb1_unlock(tdb, -1, F_WRLCK);
	return 0;

 fail:
	tdb1_unlock(tdb, -1, F_WRLCK);
	return -1;
}