From 77bb75ead0b5b13539ab89531542cc2e9813fda4 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Sat, 29 Jul 2006 01:13:53 +0000 Subject: r17299: Improve the partition module to replicate attribute records into all partitions. Test that we do that correctly. Andrew Bartlett (This used to be commit 90c07b88010b848423dee9556a24e8d181c365dd) --- source4/dsdb/samdb/ldb_modules/partition.c | 240 ++++++++++++++++++++++------- testprogs/ejs/ldb.js | 65 ++++++++ 2 files changed, 246 insertions(+), 59 deletions(-) diff --git a/source4/dsdb/samdb/ldb_modules/partition.c b/source4/dsdb/samdb/ldb_modules/partition.c index 92fddca270..a21fabb747 100644 --- a/source4/dsdb/samdb/ldb_modules/partition.c +++ b/source4/dsdb/samdb/ldb_modules/partition.c @@ -42,15 +42,16 @@ struct partition { }; struct partition_private_data { struct partition **partitions; + struct ldb_dn **replicate; }; struct partition_context { struct ldb_module *module; struct ldb_request *orig_req; - struct ldb_request **search_req; - BOOL *finished_search; - int num_searches; + struct ldb_request **down_req; + int num_requests; + int finished_requests; }; static struct ldb_handle *partition_init_handle(struct ldb_request *req, struct ldb_module *module) @@ -118,35 +119,163 @@ struct ldb_module *find_backend(struct ldb_module *module, struct ldb_request *r return module; }; -static int partition_send_search(struct partition_context *ac, struct ldb_module *partition) + +/* + fire the caller's callback for every entry, but only send 'done' once. +*/ +static int partition_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) +{ + struct partition_context *ac; + + if (!context || !ares) { + ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback")); + goto error; + } + + ac = talloc_get_type(context, struct partition_context); + + if (ares->type == LDB_REPLY_ENTRY) { + return ac->orig_req->callback(ldb, ac->orig_req->context, ares); + } else { + ac->finished_requests++; + if (ac->finished_requests == ac->num_requests) { + return ac->orig_req->callback(ldb, ac->orig_req->context, ares); + } else { + talloc_free(ares); + return LDB_SUCCESS; + } + } +error: + talloc_free(ares); + return LDB_ERR_OPERATIONS_ERROR; +} + +/* + only fire the 'last' callback, and only for START-TLS for now +*/ +static int partition_other_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) +{ + struct partition_context *ac; + + if (!context || !ares) { + ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback")); + goto error; + } + + ac = talloc_get_type(context, struct partition_context); + + if (ares->type == LDB_REPLY_EXTENDED && strcmp(ares->response->oid, LDB_EXTENDED_START_TLS_OID)) { + ac->finished_requests++; + if (ac->finished_requests == ac->num_requests) { + return ac->orig_req->callback(ldb, ac->orig_req->context, ares); + } + talloc_free(ares); + return LDB_SUCCESS; + } + ldb_set_errstring(ldb, talloc_asprintf(ldb, "partition_other_callback: Unknown reply type, only supports START_TLS")); +error: + talloc_free(ares); + return LDB_ERR_OPERATIONS_ERROR; +} + + +static int partition_send_request(struct partition_context *ac, struct ldb_module *partition) { int ret; struct ldb_module *next = make_module_for_next_request(ac->module, ac->module->ldb, partition); - ac->search_req = talloc_realloc(ac, ac->search_req, - struct ldb_request *, ac->num_searches + 1); - if (!ac->search_req) { + ac->down_req = talloc_realloc(ac, ac->down_req, + struct ldb_request *, ac->num_requests + 1); + if (!ac->down_req) { ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac->module->ldb, "Out of memory!")); return LDB_ERR_OPERATIONS_ERROR; } - ac->search_req[ac->num_searches] = talloc(ac, struct ldb_request); - if (ac->search_req[ac->num_searches] == NULL) { + ac->down_req[ac->num_requests] = talloc(ac, struct ldb_request); + if (ac->down_req[ac->num_requests] == NULL) { ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac->module->ldb, "Out of memory!")); return LDB_ERR_OPERATIONS_ERROR; } - *ac->search_req[ac->num_searches] = *ac->orig_req; /* copy the request */ + *ac->down_req[ac->num_requests] = *ac->orig_req; /* copy the request */ + if (ac->down_req[ac->num_requests]->operation == LDB_SEARCH) { + ac->down_req[ac->num_requests]->callback = partition_search_callback; + ac->down_req[ac->num_requests]->context = ac; + } else { + ac->down_req[ac->num_requests]->callback = partition_other_callback; + ac->down_req[ac->num_requests]->context = ac; + } + /* Spray off search requests to all backends */ - ret = ldb_next_request(next, ac->search_req[ac->num_searches]); + ret = ldb_next_request(next, ac->down_req[ac->num_requests]); if (ret != LDB_SUCCESS) { return ret; } - ac->num_searches++; + ac->num_requests++; return LDB_SUCCESS; } +/* Send a request down to all the partitions */ +static int partition_send_all(struct ldb_module *module, + struct partition_context *ac, struct ldb_request *req) +{ + int i; + struct partition_private_data *data = talloc_get_type(module->private_data, + struct partition_private_data); + int ret = partition_send_request(ac, module->next); + if (ret != LDB_SUCCESS) { + return ret; + } + for (i=0; data && data->partitions && data->partitions[i]; i++) { + ret = partition_send_request(ac, data->partitions[i]->module); + if (ret != LDB_SUCCESS) { + return ret; + } + } + return LDB_SUCCESS; +} + +/* Figure out which backend a request needs to be aimed at. Some + * requests must be replicated to all backends */ +static int partition_replicate(struct ldb_module *module, struct ldb_request *req, const struct ldb_dn *dn) +{ + int i; + struct ldb_module *backend; + struct partition_private_data *data = talloc_get_type(module->private_data, + struct partition_private_data); + + /* Is this a special DN, we need to replicate to every backend? */ + for (i=0; data->replicate && data->replicate[i]; i++) { + if (ldb_dn_compare(module->ldb, + data->replicate[i], + dn) == 0) { + struct ldb_handle *h; + struct partition_context *ac; + + h = partition_init_handle(req, module); + if (!h) { + return LDB_ERR_OPERATIONS_ERROR; + } + /* return our own handle to deal with this call */ + req->handle = h; + + ac = talloc_get_type(h->private_data, struct partition_context); + + return partition_send_all(module, ac, req); + } + } + + /* Otherwise, we need to find the backend to fire it to */ + + /* Find backend */ + backend = find_backend(module, req, req->op.add.message->dn); + + /* issue request */ + return ldb_next_request(backend, req); + +} + /* search */ static int partition_search(struct ldb_module *module, struct ldb_request *req) { @@ -171,15 +300,12 @@ static int partition_search(struct ldb_module *module, struct ldb_request *req) ac = talloc_get_type(h->private_data, struct partition_context); - ac->orig_req = req; - ac->num_searches = 0; - for (i=0; data && data->partitions && data->partitions[i]; i++) { /* Find all partitions under the search base */ if (ldb_dn_compare_base(module->ldb, req->op.search.base, data->partitions[i]->dn) == 0) { - ret = partition_send_search(ac, data->partitions[i]->module); + ret = partition_send_request(ac, data->partitions[i]->module); if (ret != LDB_SUCCESS) { return ret; } @@ -187,23 +313,10 @@ static int partition_search(struct ldb_module *module, struct ldb_request *req) } /* Perhaps we didn't match any partitions. Try the main partition, then all partitions */ - if (ac->num_searches == 0) { - ret = partition_send_search(ac, module->next); - if (ret != LDB_SUCCESS) { - return ret; - } - for (i=0; data && data->partitions && data->partitions[i]; i++) { - ret = partition_send_search(ac, data->partitions[i]->module); - if (ret != LDB_SUCCESS) { - return ret; - } - } + if (ac->num_requests == 0) { + return partition_send_all(module, ac, req); } - ac->finished_search = talloc_zero_array(ac, BOOL, ac->num_searches); - if (!ac->finished_search) { - return LDB_ERR_OPERATIONS_ERROR; - } return LDB_SUCCESS; } else { struct ldb_module *backend = find_backend(module, req, req->op.search.base); @@ -215,34 +328,19 @@ static int partition_search(struct ldb_module *module, struct ldb_request *req) /* add */ static int partition_add(struct ldb_module *module, struct ldb_request *req) { - /* Find backend */ - struct ldb_module *backend = find_backend(module, req, req->op.add.message->dn); - - /* issue request */ - - return ldb_next_request(backend, req); + return partition_replicate(module, req, req->op.add.message->dn); } /* modify */ static int partition_modify(struct ldb_module *module, struct ldb_request *req) { - /* Find backend */ - struct ldb_module *backend = find_backend(module, req, req->op.mod.message->dn); - - /* issue request */ - - return ldb_next_request(backend, req); + return partition_replicate(module, req, req->op.mod.message->dn); } /* delete */ static int partition_delete(struct ldb_module *module, struct ldb_request *req) { - /* Find backend */ - struct ldb_module *backend = find_backend(module, req, req->op.del.dn); - - /* issue request */ - - return ldb_next_request(backend, req); + return partition_replicate(module, req, req->op.del.dn); } /* rename */ @@ -256,10 +354,7 @@ static int partition_rename(struct ldb_module *module, struct ldb_request *req) return LDB_ERR_AFFECTS_MULTIPLE_DSAS; } - /* issue request */ - - /* (later) consider if we should be searching multiple partitions */ - return ldb_next_request(backend, req); + return partition_replicate(module, req, req->op.rename.olddn); } /* start a transaction */ @@ -400,10 +495,11 @@ static int partition_init(struct ldb_module *module) { int ret, i; TALLOC_CTX *mem_ctx = talloc_new(module); - static const char *attrs[] = { "partition", NULL }; + static const char *attrs[] = { "partition", "replicateEntries", NULL }; struct ldb_result *res; struct ldb_message *msg; struct ldb_message_element *partition_attributes; + struct ldb_message_element *replicate_attributes; struct partition_private_data *data; @@ -511,6 +607,32 @@ static int partition_init(struct ldb_module *module) talloc_free(req); } + replicate_attributes = ldb_msg_find_element(msg, "replicateEntries"); + if (!replicate_attributes) { + ldb_set_errstring(module->ldb, + talloc_asprintf(module, "partition_init: " + "no entries to replicate specified")); + data->replicate = NULL; + } else { + data->replicate = talloc_array(data, struct ldb_dn *, replicate_attributes->num_values + 1); + if (!data->replicate) { + talloc_free(mem_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + + for (i=0; i < replicate_attributes->num_values; i++) { + data->replicate[i] = ldb_dn_explode(data->replicate[i], replicate_attributes->values[i].data); + if (!data->replicate[i]) { + ldb_set_errstring(module->ldb, + talloc_asprintf(module, "partition_init: " + "invalid DN in partition replicate record: %s", + replicate_attributes->values[i].data)); + return LDB_ERR_CONSTRAINT_VIOLATION; + } + } + data->replicate[i] = NULL; + } + module->private_data = data; talloc_steal(module, data); @@ -536,19 +658,19 @@ static int partition_wait_none(struct ldb_handle *handle) { ac = talloc_get_type(handle->private_data, struct partition_context); - for (i=0; i < ac->num_searches; i++) { - ret = ldb_wait(ac->search_req[i]->handle, LDB_WAIT_NONE); + for (i=0; i < ac->num_requests; i++) { + ret = ldb_wait(ac->down_req[i]->handle, LDB_WAIT_NONE); if (ret != LDB_SUCCESS) { handle->status = ret; goto done; } - if (ac->search_req[i]->handle->status != LDB_SUCCESS) { - handle->status = ac->search_req[i]->handle->status; + if (ac->down_req[i]->handle->status != LDB_SUCCESS) { + handle->status = ac->down_req[i]->handle->status; goto done; } - if (ac->search_req[i]->handle->state != LDB_ASYNC_DONE) { + if (ac->down_req[i]->handle->state != LDB_ASYNC_DONE) { return LDB_SUCCESS; } } diff --git a/testprogs/ejs/ldb.js b/testprogs/ejs/ldb.js index 0d2de577d3..fb1a2d4b0d 100755 --- a/testprogs/ejs/ldb.js +++ b/testprogs/ejs/ldb.js @@ -101,6 +101,16 @@ partition: cn=Sub,cn=Sub,cn=PartTest:" + prefix + "testsubsub.ldb function modules_test(ldb) { println("Running modules tests"); + + ok = ldb.add(" +dn: @ATTRIBUTES +caseattr: CASE_INSENSITIVE +"); + if (!ok) { + println("Failed to add: " + ldb.errstring()); + assert(ok); + } + ok = ldb.add(" dn: cn=x8,cn=PartTest objectClass: foo @@ -207,6 +217,57 @@ x: 11 assert(res8[0].name == "x11"); assert(res8[0].cn == "x11"); + ok = ldb.add(" +dn: caseattr=XY,cn=PartTest +objectClass: foo +x: Y +"); + if (!ok) { + println("Failed to add: " + ldb.errstring()); + assert(ok); + } + + ok = ldb.add(" +dn: caseattr=XZ,cn=PartTest +objectClass: foo +x: Z +caseattr: XZ +"); + if (!ok) { + println("Failed to add: " + ldb.errstring()); + assert(ok); + } + + ok = ldb.add(" +dn: caseattr2=XZ,cn=PartTest +objectClass: foo +x: Z +caseattr2: XZ +"); + if (!ok) { + println("Failed to add: " + ldb.errstring()); + assert(ok); + } + + var resX = ldb.search("caseattr=xz", "cn=parttest", ldb.SCOPE_DEFAULT, attrs); + assert(resX.length == 1); + assert(resX[0].objectGUID != undefined); + assert(resX[0].createTimestamp != undefined); + assert(resX[0].whenCreated != undefined); + assert(resX[0].name == "XZ"); + + var rescount = ldb.search("(|(caseattr=*)(cn=*))", "cn=parttest", ldb.SCOPE_DEFAULT, attrs); + assert(rescount.length == 5); + + /* Check this attribute is *not* case sensitive */ + var resXcount = ldb.search("caseattr=x*", "cn=parttest", ldb.SCOPE_DEFAULT, attrs); + assert(resXcount.length == 2); + + /* Check that this attribute *is* case sensitive */ + var resXcount2 = ldb.search("caseattr2=xz", "cn=parttest", ldb.SCOPE_DEFAULT, attrs); + assert(resXcount2.length == 0); + + /* Now abort the transaction to show that even with * partitions, it is aborted everywhere */ ok = ldb.transaction_cancel(); @@ -229,6 +290,10 @@ x: 11 var res11 = ldb.search("x=10", "cn=parttest", ldb.SCOPE_DEFAULT, attrs); assert(res11.length == 0); + var attrs = new Array("*"); + var res12 = ldb.search("caseattr=*", "cn=parttest", ldb.SCOPE_DEFAULT, attrs); + assert(res12.length == 0); + } sys = sys_init(); -- cgit