X-Git-Url: http://git.samba.org/samba.git/?a=blobdiff_plain;f=source4%2Flib%2Fldb%2Fldb_tdb%2Fldb_tdb.c;h=7427b9816323e9a9981e538dfa3e9626bf2adcec;hb=2f211daa47d16eddb807f8cde00509f62be7fd1f;hp=d6276c4b8600c5c75cd546aed949096b933b08df;hpb=380874ef863866c94c999ef53252b9d30df65e88;p=samba.git diff --git a/source4/lib/ldb/ldb_tdb/ldb_tdb.c b/source4/lib/ldb/ldb_tdb/ldb_tdb.c index d6276c4b860..7427b981632 100644 --- a/source4/lib/ldb/ldb_tdb/ldb_tdb.c +++ b/source4/lib/ldb/ldb_tdb/ldb_tdb.c @@ -254,7 +254,7 @@ static int ltdb_add_internal(struct ldb_module *module, const struct ldb_message *msg) { struct ldb_context *ldb = ldb_module_get_ctx(module); - int ret; + int ret, i; ret = ltdb_check_special_dn(module, msg); if (ret != LDB_SUCCESS) { @@ -265,6 +265,24 @@ static int ltdb_add_internal(struct ldb_module *module, return LDB_ERR_OPERATIONS_ERROR; } + for (i=0;inum_elements;i++) { + struct ldb_message_element *el = &msg->elements[i]; + const struct ldb_schema_attribute *a = ldb_schema_attribute_by_name(ldb, el->name); + + if (el->num_values == 0) { + ldb_asprintf_errstring(ldb, "attribute %s on %s specified, but with 0 values (illegal)", + el->name, ldb_dn_get_linearized(msg->dn)); + return LDB_ERR_CONSTRAINT_VIOLATION; + } + if (a && a->flags & LDB_ATTR_FLAG_SINGLE_VALUE) { + if (el->num_values > 1) { + ldb_asprintf_errstring(ldb, "SINGLE-VALUE attribute %s on %s speicified more than once", + el->name, ldb_dn_get_linearized(msg->dn)); + return LDB_ERR_CONSTRAINT_VIOLATION; + } + } + } + ret = ltdb_store(module, msg, TDB_INSERT); if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) { @@ -602,15 +620,34 @@ int ltdb_modify_internal(struct ldb_module *module, struct ldb_message_element *el2; struct ldb_val *vals; const char *dn; + const struct ldb_schema_attribute *a = ldb_schema_attribute_by_name(ldb, el->name); - switch (msg->elements[i].flags & LDB_FLAG_MOD_MASK) { + if (ldb_attr_cmp(el->name, "distinguishedName") == 0) { + ldb_asprintf_errstring(ldb, "it is not permitted to perform a modify on distinguishedName (use rename instead): %s", + ldb_dn_get_linearized(msg->dn)); + return LDB_ERR_UNWILLING_TO_PERFORM; + } + switch (msg->elements[i].flags & LDB_FLAG_MOD_MASK) { case LDB_FLAG_MOD_ADD: + /* add this element to the message. fail if it already exists */ idx = find_element(msg2, el->name); + if (el->num_values == 0) { + ldb_asprintf_errstring(ldb, "attribute %s on %s speicified, but with 0 values (illigal)", + el->name, ldb_dn_get_linearized(msg->dn)); + return LDB_ERR_CONSTRAINT_VIOLATION; + } if (idx == -1) { + if (a && a->flags & LDB_ATTR_FLAG_SINGLE_VALUE) { + if (el->num_values > 1) { + ldb_asprintf_errstring(ldb, "SINGLE-VALUE attribute %s on %s speicified more than once", + el->name, ldb_dn_get_linearized(msg->dn)); + return LDB_ERR_CONSTRAINT_VIOLATION; + } + } if (msg_add_element(ldb, msg2, el) != 0) { ret = LDB_ERR_OTHER; goto failed; @@ -618,6 +655,13 @@ int ltdb_modify_internal(struct ldb_module *module, continue; } + /* If this is an add, then if it already + * exists in the object, then we violoate the + * single-value rule */ + if (a && a->flags & LDB_ATTR_FLAG_SINGLE_VALUE) { + return LDB_ERR_CONSTRAINT_VIOLATION; + } + el2 = &msg2->elements[idx]; /* An attribute with this name already exists, @@ -657,6 +701,13 @@ int ltdb_modify_internal(struct ldb_module *module, break; case LDB_FLAG_MOD_REPLACE: + if (a && a->flags & LDB_ATTR_FLAG_SINGLE_VALUE) { + if (el->num_values > 1) { + ldb_asprintf_errstring(ldb, "SINGLE-VALUE attribute %s on %s speicified more than once", + el->name, ldb_dn_get_linearized(msg->dn)); + return LDB_ERR_CONSTRAINT_VIOLATION; + } + } /* replace all elements of this attribute name with the elements listed. The attribute not existing is not an error */ msg_delete_attribute(module, ldb, msg2, el->name); @@ -805,37 +856,18 @@ static int ltdb_rename(struct ltdb_context *ctx) return LDB_ERR_OPERATIONS_ERROR; } - if (ldb_dn_compare(req->op.rename.olddn, req->op.rename.newdn) == 0) { - /* The rename operation is apparently only changing case - - the DNs are the same. Delete the old DN before adding - the new one to avoid a TDB_ERR_EXISTS error. - - The only drawback to this is that if the delete - succeeds but the add fails, we rely on the - transaction to roll this all back. */ - tret = ltdb_delete_internal(module, req->op.rename.olddn); - if (tret != LDB_SUCCESS) { - return tret; - } - - tret = ltdb_add_internal(module, msg); - if (tret != LDB_SUCCESS) { - return tret; - } - } else { - /* The rename operation is changing DNs. Try to add the new - DN first to avoid clobbering another DN not related to - this rename operation. */ - tret = ltdb_add_internal(module, msg); - if (tret != LDB_SUCCESS) { - return tret; - } + /* Always delete first then add, to avoid conflicts with + * unique indexes. We rely on the transaction to make this + * atomic + */ + tret = ltdb_delete_internal(module, req->op.rename.olddn); + if (tret != LDB_SUCCESS) { + return tret; + } - tret = ltdb_delete_internal(module, req->op.rename.olddn); - if (tret != LDB_SUCCESS) { - ltdb_delete_internal(module, req->op.rename.newdn); - return LDB_ERR_OPERATIONS_ERROR; - } + tret = ltdb_add_internal(module, msg); + if (tret != LDB_SUCCESS) { + return tret; } return LDB_SUCCESS; @@ -857,17 +889,46 @@ static int ltdb_start_trans(struct ldb_module *module) return LDB_SUCCESS; } -static int ltdb_end_trans(struct ldb_module *module) +static int ltdb_prepare_commit(struct ldb_module *module) { void *data = ldb_module_get_private(module); struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private); - ltdb->in_transaction--; + if (ltdb->in_transaction != 1) { + return LDB_SUCCESS; + } if (ltdb_index_transaction_commit(module) != 0) { + tdb_transaction_cancel(ltdb->tdb); + ltdb->in_transaction--; + return ltdb_err_map(tdb_error(ltdb->tdb)); + } + + if (tdb_transaction_prepare_commit(ltdb->tdb) != 0) { + ltdb->in_transaction--; return ltdb_err_map(tdb_error(ltdb->tdb)); } + ltdb->prepared_commit = true; + + return LDB_SUCCESS; +} + +static int ltdb_end_trans(struct ldb_module *module) +{ + void *data = ldb_module_get_private(module); + struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private); + + if (!ltdb->prepared_commit) { + int ret = ltdb_prepare_commit(module); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + ltdb->in_transaction--; + ltdb->prepared_commit = false; + if (tdb_transaction_commit(ltdb->tdb) != 0) { return ltdb_err_map(tdb_error(ltdb->tdb)); } @@ -883,6 +944,7 @@ static int ltdb_del_trans(struct ldb_module *module) ltdb->in_transaction--; if (ltdb_index_transaction_cancel(module) != 0) { + tdb_transaction_cancel(ltdb->tdb); return ltdb_err_map(tdb_error(ltdb->tdb)); } @@ -1017,7 +1079,16 @@ static void ltdb_timeout(struct tevent_context *ev, struct ltdb_context *ctx; ctx = talloc_get_type(private_data, struct ltdb_context); - ltdb_request_done(ctx, LDB_ERR_TIME_LIMIT_EXCEEDED); + if (!ctx->request_terminated) { + /* request is done now */ + ltdb_request_done(ctx, LDB_ERR_TIME_LIMIT_EXCEEDED); + } + + if (!ctx->request_terminated) { + /* neutralize the spy */ + ctx->spy->ctx = NULL; + } + talloc_free(ctx); } static void ltdb_request_extended_done(struct ltdb_context *ctx, @@ -1076,6 +1147,10 @@ static void ltdb_callback(struct tevent_context *ev, ctx = talloc_get_type(private_data, struct ltdb_context); + if (ctx->request_terminated) { + goto done; + } + switch (ctx->req->operation) { case LDB_SEARCH: ret = ltdb_search(ctx); @@ -1094,17 +1169,34 @@ static void ltdb_callback(struct tevent_context *ev, break; case LDB_EXTENDED: ltdb_handle_extended(ctx); - return; + goto done; default: /* no other op supported */ ret = LDB_ERR_UNWILLING_TO_PERFORM; } - if (!ctx->callback_failed) { - /* Once we are done, we do not need timeout events */ - talloc_free(ctx->timeout_event); + if (!ctx->request_terminated) { + /* request is done now */ ltdb_request_done(ctx, ret); } + +done: + if (!ctx->request_terminated) { + /* neutralize the spy */ + ctx->spy->ctx = NULL; + } + talloc_free(ctx); +} + +static int ltdb_request_destructor(void *ptr) +{ + struct ltdb_req_spy *spy = talloc_get_type(ptr, struct ltdb_req_spy); + + if (spy->ctx != NULL) { + spy->ctx->request_terminated = true; + } + + return 0; } static int ltdb_handle_request(struct ldb_module *module, @@ -1129,7 +1221,7 @@ static int ltdb_handle_request(struct ldb_module *module, ev = ldb_get_event_context(ldb); - ac = talloc_zero(req, struct ltdb_context); + ac = talloc_zero(ldb, struct ltdb_context); if (ac == NULL) { ldb_set_errstring(ldb, "Out of Memory"); return LDB_ERR_OPERATIONS_ERROR; @@ -1142,15 +1234,28 @@ static int ltdb_handle_request(struct ldb_module *module, tv.tv_usec = 0; te = tevent_add_timer(ev, ac, tv, ltdb_callback, ac); if (NULL == te) { + talloc_free(ac); return LDB_ERR_OPERATIONS_ERROR; } tv.tv_sec = req->starttime + req->timeout; ac->timeout_event = tevent_add_timer(ev, ac, tv, ltdb_timeout, ac); if (NULL == ac->timeout_event) { + talloc_free(ac); return LDB_ERR_OPERATIONS_ERROR; } + /* set a spy so that we do not try to use the request context + * if it is freed before ltdb_callback fires */ + ac->spy = talloc(req, struct ltdb_req_spy); + if (NULL == ac->spy) { + talloc_free(ac); + return LDB_ERR_OPERATIONS_ERROR; + } + ac->spy->ctx = ac; + + talloc_set_destructor((TALLOC_CTX *)ac->spy, ltdb_request_destructor); + return LDB_SUCCESS; } @@ -1164,6 +1269,7 @@ static const struct ldb_module_ops ltdb_ops = { .extended = ltdb_handle_request, .start_transaction = ltdb_start_trans, .end_transaction = ltdb_end_trans, + .prepare_commit = ltdb_prepare_commit, .del_transaction = ltdb_del_trans, }; @@ -1221,7 +1327,7 @@ static int ltdb_connect(struct ldb_context *ldb, const char *url, ldb_get_create_perms(ldb), ldb); if (!ltdb->tdb) { ldb_debug(ldb, LDB_DEBUG_ERROR, - "Unable to open tdb '%s'\n", path); + "Unable to open tdb '%s'", path); talloc_free(ltdb); return -1; }