#define LDB_UNPACK_DATA_FLAG_NO_DN 0x0002
#define LDB_UNPACK_DATA_FLAG_NO_VALUES_ALLOC 0x0004
#define LDB_UNPACK_DATA_FLAG_NO_ATTRS 0x0008
+#define LDB_UNPACK_DATA_FLAG_READ_LOCKED 0x0010
/**
Forces a specific ldb handle to use the global event context.
void *ctx);
struct kv_db_ops {
+ uint32_t options;
+
int (*store)(struct ldb_kv_private *ldb_kv,
struct ldb_val key,
struct ldb_val data,
#define LDB_KV_GUID_SIZE 16
#define LDB_KV_GUID_KEY_SIZE (LDB_KV_GUID_SIZE + sizeof(LDB_KV_GUID_KEY_PREFIX) - 1)
+/* LDB KV options */
+/*
+ * This allows pointers to be referenced after the callback to any variant of
+ * iterate or fetch_and_parse -- as long as an overall read lock is held.
+ */
+#define LDB_KV_OPTION_STABLE_READ_LOCK 0x00000001
+
/*
* The following definitions come from lib/ldb/ldb_key_value/ldb_kv_cache.c
*/
dn,
msg,
LDB_UNPACK_DATA_FLAG_NO_DATA_ALLOC |
- LDB_UNPACK_DATA_FLAG_NO_DN);
+ LDB_UNPACK_DATA_FLAG_NO_DN |
+ /*
+ * The entry point ldb_kv_search_indexed is
+ * only called from the read-locked
+ * ldb_kv_search.
+ */
+ LDB_UNPACK_DATA_FLAG_READ_LOCKED);
if (ret != LDB_SUCCESS) {
talloc_free(msg);
return ret;
keys[i],
msg,
LDB_UNPACK_DATA_FLAG_NO_DATA_ALLOC |
- LDB_UNPACK_DATA_FLAG_NO_VALUES_ALLOC);
+ LDB_UNPACK_DATA_FLAG_NO_VALUES_ALLOC |
+ /*
+ * The entry point ldb_kv_search_indexed is
+ * only called from the read-locked
+ * ldb_kv_search.
+ */
+ LDB_UNPACK_DATA_FLAG_READ_LOCKED);
if (ret == LDB_ERR_NO_SUCH_OBJECT) {
/*
* the record has disappeared? yes, this can
struct ldb_context *ldb = ldb_module_get_ctx(ctx->module);
struct ldb_val data_parse = data;
- if (ctx->unpack_flags & LDB_UNPACK_DATA_FLAG_NO_DATA_ALLOC) {
- /*
- * If we got LDB_UNPACK_DATA_FLAG_NO_DATA_ALLOC
- * we need at least do a memdup on the whole
- * data buffer as that may change later
- * and the caller needs a stable result.
- */
- data_parse.data = talloc_memdup(ctx->msg,
- data.data,
- data.length);
- if (data_parse.data == NULL) {
- ldb_debug(ldb, LDB_DEBUG_ERROR,
- "Unable to allocate data(%d) for %*.*s\n",
- (int)data.length,
- (int)key.length, (int)key.length, key.data);
- return LDB_ERR_OPERATIONS_ERROR;
+ struct ldb_kv_private *ldb_kv =
+ talloc_get_type(ldb_module_get_private(ctx->module), struct ldb_kv_private);
+
+ if ((ctx->unpack_flags & LDB_UNPACK_DATA_FLAG_NO_DATA_ALLOC)) {
+ if ((ldb_kv->kv_ops->options & LDB_KV_OPTION_STABLE_READ_LOCK) &&
+ (ctx->unpack_flags & LDB_UNPACK_DATA_FLAG_READ_LOCKED) &&
+ !ldb_kv->kv_ops->transaction_active(ldb_kv)) {
+ /*
+ * In the case where no transactions are active and
+ * we're in a read-lock, we can point directly into
+ * database memory.
+ *
+ * The database can't be changed underneath us and we
+ * will duplicate this data in the call to filter.
+ *
+ * This is seen in:
+ * - ldb_kv_index_filter
+ * - ldb_kv_search_and_return_base
+ */
+ } else {
+ /*
+ * In every other case, if we got
+ * LDB_UNPACK_DATA_FLAG_NO_DATA_ALLOC we need at least
+ * do a memdup on the whole data buffer as that may
+ * change later and the caller needs a stable result.
+ *
+ * During transactions, pointers could change and in
+ * TDB, there just aren't the same guarantees.
+ */
+ data_parse.data = talloc_memdup(ctx->msg,
+ data.data,
+ data.length);
+ if (data_parse.data == NULL) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR,
+ "Unable to allocate data(%d) for %*.*s\n",
+ (int)data.length,
+ (int)key.length, (int)key.length, key.data);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
}
}
ctx->base,
msg,
LDB_UNPACK_DATA_FLAG_NO_DATA_ALLOC |
- LDB_UNPACK_DATA_FLAG_NO_VALUES_ALLOC);
+ LDB_UNPACK_DATA_FLAG_NO_VALUES_ALLOC |
+ LDB_UNPACK_DATA_FLAG_READ_LOCKED);
if (ret == LDB_ERR_NO_SUCH_OBJECT) {
if (ldb_kv->check_base == false) {
return stats.ms_entries;
}
-
-
static struct kv_db_ops lmdb_key_value_ops = {
+ .options = LDB_KV_OPTION_STABLE_READ_LOCK,
+
.store = lmdb_store,
.delete = lmdb_delete,
.iterate = lmdb_traverse_fn,
}
static const struct kv_db_ops key_value_ops = {
- .store = ltdb_store,
- .delete = ltdb_delete,
- .iterate = ltdb_traverse_fn,
- .update_in_iterate = ltdb_update_in_iterate,
- .fetch_and_parse = ltdb_parse_record,
- .iterate_range = ltdb_iterate_range,
- .lock_read = ltdb_lock_read,
- .unlock_read = ltdb_unlock_read,
- .begin_write = ltdb_transaction_start,
- .prepare_write = ltdb_transaction_prepare_commit,
- .finish_write = ltdb_transaction_commit,
- .abort_write = ltdb_transaction_cancel,
- .error = ltdb_error,
- .errorstr = ltdb_errorstr,
- .name = ltdb_name,
- .has_changed = ltdb_changed,
- .transaction_active = ltdb_transaction_active,
- .get_size = ltdb_get_size,
+ /* No support for any additional features */
+ .options = 0,
+
+ .store = ltdb_store,
+ .delete = ltdb_delete,
+ .iterate = ltdb_traverse_fn,
+ .update_in_iterate = ltdb_update_in_iterate,
+ .fetch_and_parse = ltdb_parse_record,
+ .iterate_range = ltdb_iterate_range,
+ .lock_read = ltdb_lock_read,
+ .unlock_read = ltdb_unlock_read,
+ .begin_write = ltdb_transaction_start,
+ .prepare_write = ltdb_transaction_prepare_commit,
+ .finish_write = ltdb_transaction_commit,
+ .abort_write = ltdb_transaction_cancel,
+ .error = ltdb_error,
+ .errorstr = ltdb_errorstr,
+ .name = ltdb_name,
+ .has_changed = ltdb_changed,
+ .transaction_active = ltdb_transaction_active,
+ .get_size = ltdb_get_size,
};
/*