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; }
/* 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; }
/* 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; }
_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; }
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; }
_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; }