Example #1
0
static PyObject *obj_lockall_read(PyTdbObject *self)
{
	int ret;
	PyErr_TDB_RAISE_IF_CLOSED(self);
	ret = tdb_lockall_read(self->ctx);
	PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
	Py_RETURN_NONE;
}
Example #2
0
/*
  lock the database for read - use by ltdb_search and ltdb_sequence_number
*/
int ltdb_lock_read(struct ldb_module *module)
{
	void *data = ldb_module_get_private(module);
	struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
	if (ltdb->in_transaction == 0) {
		return tdb_lockall_read(ltdb->tdb);
	}
	return 0;
}
Example #3
0
/*
  lock the database for read - use by ltdb_search and ltdb_sequence_number
*/
int ltdb_lock_read(struct ldb_module *module)
{
	void *data = ldb_module_get_private(module);
	struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
	int ret = 0;

	if (ltdb->in_transaction == 0 &&
	    ltdb->read_lock_count == 0) {
		ret = tdb_lockall_read(ltdb->tdb);
	}
	if (ret == 0) {
		ltdb->read_lock_count++;
	}
	return ret;
}
Example #4
0
_PUBLIC_ int tdb_check(struct tdb_context *tdb,
	      int (*check)(TDB_DATA key, TDB_DATA data, void *private_data),
	      void *private_data)
{
	unsigned int h;
	unsigned char **hashes;
	tdb_off_t off, recovery_start;
	struct tdb_record rec;
	bool found_recovery = false;
	tdb_len_t dead;
	bool locked;

	/* Read-only databases use no locking at all: it's best-effort.
	 * We may have a write lock already, so skip that case too. */
	if (tdb->read_only || tdb->allrecord_lock.count != 0) {
		locked = false;
	} else {
		if (tdb_lockall_read(tdb) == -1)
			return -1;
		locked = true;
	}

	/* Make sure we know true size of the underlying file. */
	tdb->methods->tdb_oob(tdb, tdb->map_size, 1, 1);

	/* Header must be OK: also gets us the recovery ptr, if any. */
	if (!tdb_check_header(tdb, &recovery_start))
		goto unlock;

	/* We should have the whole header, too. */
	if (tdb->map_size < TDB_DATA_START(tdb->hash_size)) {
		tdb->ecode = TDB_ERR_CORRUPT;
		TDB_LOG((tdb, TDB_DEBUG_ERROR, "File too short for hashes\n"));
		goto unlock;
	}

	/* One big malloc: pointers then bit arrays. */
	hashes = (unsigned char **)calloc(
			1, sizeof(hashes[0]) * (1+tdb->hash_size)
			+ BITMAP_BITS / CHAR_BIT * (1+tdb->hash_size));
	if (!hashes) {
		tdb->ecode = TDB_ERR_OOM;
		goto unlock;
	}

	/* Initialize pointers */
	hashes[0] = (unsigned char *)(&hashes[1+tdb->hash_size]);
	for (h = 1; h < 1+tdb->hash_size; h++)
		hashes[h] = hashes[h-1] + BITMAP_BITS / CHAR_BIT;

	/* Freelist and hash headers are all in a row: read them. */
	for (h = 0; h < 1+tdb->hash_size; h++) {
		if (tdb_ofs_read(tdb, FREELIST_TOP + h*sizeof(tdb_off_t),
				 &off) == -1)
			goto free;
		if (off)
			record_offset(hashes[h], off);
	}

	/* For each record, read it in and check it's ok. */
	for (off = TDB_DATA_START(tdb->hash_size);
	     off < tdb->map_size;
	     off += sizeof(rec) + rec.rec_len) {
		if (tdb->methods->tdb_read(tdb, off, &rec, sizeof(rec),
					   DOCONV()) == -1)
			goto free;
		switch (rec.magic) {
		case TDB_MAGIC:
		case TDB_DEAD_MAGIC:
			if (!tdb_check_used_record(tdb, off, &rec, hashes,
						   check, private_data))
				goto free;
			break;
		case TDB_FREE_MAGIC:
			if (!tdb_check_free_record(tdb, off, &rec, hashes))
				goto free;
			break;
		/* If we crash after ftruncate, we can get zeroes or fill. */
		case TDB_RECOVERY_INVALID_MAGIC:
		case 0x42424242:
			if (recovery_start == off) {
				found_recovery = true;
				break;
			}
			dead = tdb_dead_space(tdb, off);
			if (dead < sizeof(rec))
				goto corrupt;

			TDB_LOG((tdb, TDB_DEBUG_ERROR,
				 "Dead space at %d-%d (of %u)\n",
				 off, off + dead, tdb->map_size));
			rec.rec_len = dead - sizeof(rec);
			break;
		case TDB_RECOVERY_MAGIC:
			if (recovery_start != off) {
				TDB_LOG((tdb, TDB_DEBUG_ERROR,
					 "Unexpected recovery record at offset %d\n",
					 off));
				goto free;
			}
			found_recovery = true;
			break;
		default: ;
		corrupt:
			tdb->ecode = TDB_ERR_CORRUPT;
			TDB_LOG((tdb, TDB_DEBUG_ERROR,
				 "Bad magic 0x%x at offset %d\n",
				 rec.magic, off));
			goto free;
		}
	}

	/* Now, hashes should all be empty: each record exists and is referred
	 * to by one other. */
	for (h = 0; h < 1+tdb->hash_size; h++) {
		unsigned int i;
		for (i = 0; i < BITMAP_BITS / CHAR_BIT; i++) {
			if (hashes[h][i] != 0) {
				tdb->ecode = TDB_ERR_CORRUPT;
				TDB_LOG((tdb, TDB_DEBUG_ERROR,
					 "Hashes do not match records\n"));
				goto free;
			}
		}
	}

	/* We must have found recovery area if there was one. */
	if (recovery_start != 0 && !found_recovery) {
		TDB_LOG((tdb, TDB_DEBUG_ERROR,
			 "Expected a recovery area at %u\n",
			 recovery_start));
		goto free;
	}

	free(hashes);
	if (locked) {
		tdb_unlockall_read(tdb);
	}
	return 0;

free:
	free(hashes);
unlock:
	if (locked) {
		tdb_unlockall_read(tdb);
	}
	return -1;
}
Example #5
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;
}
Example #6
0
_PUBLIC_ char *tdb_summary(struct tdb_context *tdb)
{
	off_t file_size;
	tdb_off_t off, rec_off;
	struct tally freet, keys, data, dead, extra, hashval, uncoal;
	struct tdb_record rec;
	char *ret = NULL;
	bool locked;
	size_t unc = 0;
	int len;
	struct tdb_record recovery;

	/* Read-only databases use no locking at all: it's best-effort.
	 * We may have a write lock already, so skip that case too. */
	if (tdb->read_only || tdb->allrecord_lock.count != 0) {
		locked = false;
	} else {
		if (tdb_lockall_read(tdb) == -1)
			return NULL;
		locked = true;
	}

	if (tdb_recovery_area(tdb, tdb->methods, &rec_off, &recovery) != 0) {
		goto unlock;
	}

	tally_init(&freet);
	tally_init(&keys);
	tally_init(&data);
	tally_init(&dead);
	tally_init(&extra);
	tally_init(&hashval);
	tally_init(&uncoal);

	for (off = TDB_DATA_START(tdb->hash_size);
	     off < tdb->map_size - 1;
	     off += sizeof(rec) + rec.rec_len) {
		if (tdb->methods->tdb_read(tdb, off, &rec, sizeof(rec),
					   DOCONV()) == -1)
			goto unlock;
		switch (rec.magic) {
		case TDB_MAGIC:
			tally_add(&keys, rec.key_len);
			tally_add(&data, rec.data_len);
			tally_add(&extra, rec.rec_len - (rec.key_len
							 + rec.data_len));
			if (unc > 1)
				tally_add(&uncoal, unc - 1);
			unc = 0;
			break;
		case TDB_FREE_MAGIC:
			tally_add(&freet, rec.rec_len);
			unc++;
			break;
		/* If we crash after ftruncate, we can get zeroes or fill. */
		case TDB_RECOVERY_INVALID_MAGIC:
		case 0x42424242:
			unc++;
			/* If it's a valid recovery, we can trust rec_len. */
			if (off != rec_off) {
				rec.rec_len = tdb_dead_space(tdb, off)
					- sizeof(rec);
			}
			/* Fall through */
		case TDB_DEAD_MAGIC:
			tally_add(&dead, rec.rec_len);
			break;
		default:
			TDB_LOG((tdb, TDB_DEBUG_ERROR,
				 "Unexpected record magic 0x%x at offset %u\n",
				 rec.magic, off));
			goto unlock;
		}
	}
	if (unc > 1)
		tally_add(&uncoal, unc - 1);

	for (off = 0; off < tdb->hash_size; off++)
		tally_add(&hashval, get_hash_length(tdb, off));

	file_size = tdb->hdr_ofs + tdb->map_size;

	len = asprintf(&ret, SUMMARY_FORMAT,
		 (unsigned long long)file_size, keys.total+data.total,
		 (size_t)tdb->hdr_ofs, (size_t)tdb->map_size,
		 keys.num,
		 (tdb->hash_fn == tdb_jenkins_hash)?"yes":"no",
		 (unsigned)tdb->feature_flags, TDB_SUPPORTED_FEATURE_FLAGS,
		 (tdb->feature_flags & TDB_FEATURE_FLAG_MUTEX)?"yes":"no",
		 keys.min, tally_mean(&keys), keys.max,
		 data.min, tally_mean(&data), data.max,
		 extra.min, tally_mean(&extra), extra.max,
		 dead.num,
		 dead.min, tally_mean(&dead), dead.max,
		 freet.num,
		 freet.min, tally_mean(&freet), freet.max,
		 hashval.num,
		 hashval.min, tally_mean(&hashval), hashval.max,
		 uncoal.total,
		 uncoal.min, tally_mean(&uncoal), uncoal.max,
		 keys.total * 100.0 / file_size,
		 data.total * 100.0 / file_size,
		 extra.total * 100.0 / file_size,
		 freet.total * 100.0 / file_size,
		 dead.total * 100.0 / file_size,
		 (keys.num + freet.num + dead.num)
		 * (sizeof(struct tdb_record) + sizeof(uint32_t))
		 * 100.0 / file_size,
		 tdb->hash_size * sizeof(tdb_off_t)
		 * 100.0 / file_size);
	if (len == -1) {
		goto unlock;
	}

unlock:
	if (locked) {
		tdb_unlockall_read(tdb);
	}
	return ret;
}