From: Simo Sorce Date: Sat, 30 Jan 2010 05:33:22 +0000 (-0500) Subject: s4:ldb add support for permissive modify control X-Git-Tag: tdb-1.2.1~82 X-Git-Url: http://git.samba.org/samba.git/?p=ira%2Fwip.git;a=commitdiff_plain;h=df7be036d7b4e08b18bd03399847dad41b78be50;hp=1876b5a7e33a1376a5e275a52f8fbab69fa82ab6 s4:ldb add support for permissive modify control --- diff --git a/source4/lib/ldb/ldb_tdb/ldb_tdb.c b/source4/lib/ldb/ldb_tdb/ldb_tdb.c index b8b4d399ef3..568fc2a5a69 100644 --- a/source4/lib/ldb/ldb_tdb/ldb_tdb.c +++ b/source4/lib/ldb/ldb_tdb/ldb_tdb.c @@ -512,7 +512,7 @@ static int msg_delete_attribute(struct ldb_module *module, el = ldb_msg_find_element(msg, name); if (el == NULL) { - return -1; + return LDB_ERR_NO_SUCH_ATTRIBUTE; } i = el - msg->elements; @@ -529,13 +529,13 @@ static int msg_delete_attribute(struct ldb_module *module, msg->elements = talloc_realloc(msg, msg->elements, struct ldb_message_element, msg->num_elements); - return 0; + return LDB_SUCCESS; } /* delete all elements matching an attribute name/value - return 0 on success, -1 on failure + return LDB Error on failure */ static int msg_delete_element(struct ldb_module *module, struct ldb_message *msg, @@ -550,7 +550,7 @@ static int msg_delete_element(struct ldb_module *module, found = find_element(msg, name); if (found == -1) { - return -1; + return LDB_ERR_NO_SUCH_ATTRIBUTE; } el = &msg->elements[found]; @@ -566,7 +566,7 @@ static int msg_delete_element(struct ldb_module *module, ret = ltdb_index_del_value(module, msg->dn, el, i); if (ret != LDB_SUCCESS) { - return -1; + return ret; } if (inum_values-1) { @@ -578,12 +578,12 @@ static int msg_delete_element(struct ldb_module *module, /* per definition we find in a canonicalised message an attribute value only once. So we are finished here */ - return 0; + return LDB_SUCCESS; } } /* Not found */ - return -1; + return LDB_ERR_NO_SUCH_ATTRIBUTE; } @@ -605,8 +605,14 @@ int ltdb_modify_internal(struct ldb_module *module, struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private); TDB_DATA tdb_key, tdb_data; struct ldb_message *msg2; - unsigned i, j; + unsigned i, j, k; int ret = LDB_SUCCESS, idx; + struct ldb_control *control_permissive = NULL; + + if (req) { + control_permissive = ldb_request_get_control(req, + LDB_CONTROL_PERMISSIVE_MODIFY_OID); + } tdb_key = ltdb_key(module, msg->dn); if (!tdb_key.dptr) { @@ -652,11 +658,31 @@ int ltdb_modify_internal(struct ldb_module *module, switch (msg->elements[i].flags & LDB_FLAG_MOD_MASK) { case LDB_FLAG_MOD_ADD: - if (el->num_values == 0) { - ldb_asprintf_errstring(ldb, "attribute %s on %s specified, but with 0 values (illigal)", - el->name, ldb_dn_get_linearized(msg2->dn)); - ret = LDB_ERR_CONSTRAINT_VIOLATION; - goto done; + + /* make a copy of the array so that a permissive + * control can remove duplicates without changing the + * original values, but do not copy data as we do not + * need to keep it around once the operation is + * finished */ + if (control_permissive) { + el = talloc(msg2, struct ldb_message_element); + if (!el) { + ret = LDB_ERR_OTHER; + goto done; + } + el->name = msg->elements[i].name; + el->num_values = msg->elements[i].num_values; + el->values = talloc_array(el, struct ldb_val, el->num_values); + for (j = 0; j < el->num_values; j++) { + el->values[j] = msg->elements[i].values[j]; + } + + if (el->num_values == 0) { + ldb_asprintf_errstring(ldb, "attribute %s on %s specified, but with 0 values (illigal)", + el->name, ldb_dn_get_linearized(msg2->dn)); + ret = LDB_ERR_CONSTRAINT_VIOLATION; + goto done; + } } if (a && a->flags & LDB_ATTR_FLAG_SINGLE_VALUE) { @@ -693,8 +719,19 @@ int ltdb_modify_internal(struct ldb_module *module, /* Check that values don't exist yet on multi- valued attributes or aren't provided twice */ - for (j=0; jnum_values; j++) { + for (j = 0; j < el->num_values; j++) { if (ldb_msg_find_val(el2, &el->values[j]) != NULL) { + if (control_permissive) { + /* remove this one as if it was never added */ + el->num_values--; + for (k = j; k < el->num_values; k++) { + el->values[k] = el->values[k + 1]; + } + j--; /* rewind */ + + continue; + } + ldb_asprintf_errstring(ldb, "%s: value #%d already exists", el->name, j); ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; goto done; @@ -734,6 +771,7 @@ int ltdb_modify_internal(struct ldb_module *module, break; case LDB_FLAG_MOD_REPLACE: + if (a && a->flags & LDB_ATTR_FLAG_SINGLE_VALUE) { /* the RELAX control overrides this check for replace. This is needed as @@ -797,23 +835,33 @@ int ltdb_modify_internal(struct ldb_module *module, if (msg->elements[i].num_values == 0) { /* Delete the whole attribute */ - if (msg_delete_attribute(module, ldb, msg2, - msg->elements[i].name) != 0) { + ret = msg_delete_attribute(module, ldb, msg2, + msg->elements[i].name); + if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && + control_permissive) { + ret = LDB_SUCCESS; + } else { ldb_asprintf_errstring(ldb, "No such attribute: %s for delete on %s", msg->elements[i].name, dn); - ret = LDB_ERR_NO_SUCH_ATTRIBUTE; + } + if (ret != LDB_SUCCESS) { goto done; } } else { /* Delete specified values from an attribute */ for (j=0; j < msg->elements[i].num_values; j++) { - if (msg_delete_element(module, - msg2, - msg->elements[i].name, - &msg->elements[i].values[j]) != 0) { + ret = msg_delete_element(module, + msg2, + msg->elements[i].name, + &msg->elements[i].values[j]); + if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && + control_permissive) { + ret = LDB_SUCCESS; + } else { ldb_asprintf_errstring(ldb, "No matching attribute value when deleting attribute: %s on %s", msg->elements[i].name, dn); - ret = LDB_ERR_NO_SUCH_ATTRIBUTE; + } + if (ret != LDB_SUCCESS) { goto done; } } @@ -1244,6 +1292,7 @@ static int ltdb_request_destructor(void *ptr) static int ltdb_handle_request(struct ldb_module *module, struct ldb_request *req) { + struct ldb_control *control_permissive; struct ldb_context *ldb; struct tevent_context *ev; struct ltdb_context *ac; @@ -1253,8 +1302,12 @@ static int ltdb_handle_request(struct ldb_module *module, ldb = ldb_module_get_ctx(module); + control_permissive = ldb_request_get_control(req, + LDB_CONTROL_PERMISSIVE_MODIFY_OID); + for (i = 0; req->controls && req->controls[i]; i++) { - if (req->controls[i]->critical) { + if (req->controls[i]->critical && + req->controls[i] != control_permissive) { ldb_asprintf_errstring(ldb, "Unsupported critical extension %s", req->controls[i]->oid); return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION; @@ -1306,8 +1359,27 @@ static int ltdb_handle_request(struct ldb_module *module, return LDB_SUCCESS; } +static int ltdb_init_rootdse(struct ldb_module *module) +{ + struct ldb_context *ldb; + int ret; + + ldb = ldb_module_get_ctx(module); + + ret = ldb_mod_register_control(module, + LDB_CONTROL_PERMISSIVE_MODIFY_OID); + if (ret != LDB_SUCCESS) { + ldb_debug(ldb, LDB_DEBUG_WARNING, "ldb_tdb: " + "Unable to register control with rootdse!"); + } + + /* there can be no module beyond the backend, just return */ + return LDB_SUCCESS; +} + static const struct ldb_module_ops ltdb_ops = { .name = "tdb", + .init_context = ltdb_init_rootdse, .search = ltdb_handle_request, .add = ltdb_handle_request, .modify = ltdb_handle_request, @@ -1401,5 +1473,5 @@ static int ltdb_connect(struct ldb_context *ldb, const char *url, const struct ldb_backend_ops ldb_tdb_backend_ops = { .name = "tdb", - .connect_fn = ltdb_connect + .connect_fn = ltdb_connect, };