X-Git-Url: http://git.samba.org/samba.git/?p=ira%2Fwip.git;a=blobdiff_plain;f=source4%2Fdsdb%2Fsamdb%2Fldb_modules%2Frepl_meta_data.c;h=a0dd111bdf4e83dddf6e6c706ee04e544a98c969;hp=f9411fe95f2993a25328ca77fb285a6ef838b319;hb=8ea2a8b7858866689c95d6a9d158b392b560e6b5;hpb=3dd404abad878fa7e760ba50ce84fc80b82ea159 diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c index f9411fe95f2..a0dd111bdf4 100644 --- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c +++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c @@ -76,6 +76,8 @@ struct replmd_replicated_request { uint32_t index_current; struct ldb_message *search_msg; + + uint64_t seq_num; }; @@ -363,11 +365,33 @@ static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMeta return m1->attid - m2->attid; } -static void replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1, - const uint32_t *rdn_attid) +static int replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1, + const struct dsdb_schema *schema, + struct ldb_dn *dn) { + const char *rdn_name; + const struct dsdb_attribute *rdn_sa; + + rdn_name = ldb_dn_get_rdn_name(dn); + if (!rdn_name) { + DEBUG(0,(__location__ ": No rDN for %s?\n", ldb_dn_get_linearized(dn))); + return LDB_ERR_OPERATIONS_ERROR; + } + + rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name); + if (rdn_sa == NULL) { + DEBUG(0,(__location__ ": No sa found for rDN %s for %s\n", rdn_name, ldb_dn_get_linearized(dn))); + return LDB_ERR_OPERATIONS_ERROR; + } + + DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n", + rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn))); + ldb_qsort(ctr1->array, ctr1->count, sizeof(struct replPropertyMetaData1), - discard_const_p(void, rdn_attid), (ldb_qsort_cmp_fn_t)replmd_replPropertyMetaData1_attid_sort); + discard_const_p(void, &rdn_sa->attributeID_id), + (ldb_qsort_cmp_fn_t)replmd_replPropertyMetaData1_attid_sort); + + return LDB_SUCCESS; } static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1, @@ -407,6 +431,7 @@ static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares) { struct ldb_context *ldb; struct replmd_replicated_request *ac; + int ret; ac = talloc_get_type(req->context, struct replmd_replicated_request); ldb = ldb_module_get_ctx(ac->module); @@ -428,6 +453,11 @@ static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares) LDB_ERR_OPERATIONS_ERROR); } + ret = replmd_notify(ac->module, req->op.add.message->dn, ac->seq_num); + if (ret != LDB_SUCCESS) { + return ret; + } + return ldb_module_done(ac->req, ares->controls, ares->response, LDB_SUCCESS); } @@ -435,23 +465,32 @@ static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares) static int replmd_add(struct ldb_module *module, struct ldb_request *req) { struct ldb_context *ldb; + struct ldb_control *control; + struct ldb_control **saved_controls; struct replmd_replicated_request *ac; const struct dsdb_schema *schema; enum ndr_err_code ndr_err; struct ldb_request *down_req; struct ldb_message *msg; - const struct dsdb_attribute *rdn_attr = NULL; + const DATA_BLOB *guid_blob; struct GUID guid; struct ldb_val guid_value; struct replPropertyMetaDataBlob nmd; struct ldb_val nmd_value; - uint64_t seq_num; const struct GUID *our_invocation_id; time_t t = time(NULL); NTTIME now; char *time_str; int ret; uint32_t i, ni=0; + bool allow_add_guid = false; + bool remove_current_guid = false; + + /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */ + control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID); + if (control) { + allow_add_guid = 1; + } /* do not manipulate our control entries */ if (ldb_dn_is_special(req->op.add.message->dn)) { @@ -466,6 +505,7 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req) if (!schema) { ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_add: no dsdb_schema loaded"); + DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb))); return LDB_ERR_CONSTRAINT_VIOLATION; } @@ -476,26 +516,43 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req) ac->schema = schema; - if (ldb_msg_find_element(req->op.add.message, "objectGUID") != NULL) { - ldb_debug_set(ldb, LDB_DEBUG_ERROR, + guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID"); + if ( guid_blob != NULL ) { + if( !allow_add_guid ) { + ldb_debug_set(ldb, LDB_DEBUG_ERROR, "replmd_add: it's not allowed to add an object with objectGUID\n"); - return LDB_ERR_UNWILLING_TO_PERFORM; + talloc_free(ac); + return LDB_ERR_UNWILLING_TO_PERFORM; + } else { + NTSTATUS status = GUID_from_data_blob(guid_blob,&guid); + if ( !NT_STATUS_IS_OK(status)) { + ldb_debug_set(ldb, LDB_DEBUG_ERROR, + "replmd_add: Unable to parse as a GUID the attribute objectGUID\n"); + talloc_free(ac); + return LDB_ERR_UNWILLING_TO_PERFORM; + } + /* we remove this attribute as it can be a string and will not be treated + correctly and then we will readd it latter on in the good format*/ + remove_current_guid = true; + } + } else { + /* a new GUID */ + guid = GUID_random(); } /* Get a sequence number from the backend */ - ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num); + ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num); if (ret != LDB_SUCCESS) { + talloc_free(ac); return ret; } - /* a new GUID */ - guid = GUID_random(); - /* get our invocationId */ our_invocation_id = samdb_ntds_invocation_id(ldb); if (!our_invocation_id) { ldb_debug_set(ldb, LDB_DEBUG_ERROR, "replmd_add: unable to find invocationId\n"); + talloc_free(ac); return LDB_ERR_OPERATIONS_ERROR; } @@ -503,6 +560,7 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req) msg = ldb_msg_copy_shallow(ac, req->op.add.message); if (msg == NULL) { ldb_oom(ldb); + talloc_free(ac); return LDB_ERR_OPERATIONS_ERROR; } @@ -510,8 +568,13 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req) unix_to_nt_time(&now, t); time_str = ldb_timestring(msg, t); if (!time_str) { + ldb_oom(ldb); + talloc_free(ac); return LDB_ERR_OPERATIONS_ERROR; } + if (remove_current_guid) { + ldb_msg_remove_attr(msg,"objectGUID"); + } /* * remove autogenerated attributes @@ -522,13 +585,23 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req) ldb_msg_remove_attr(msg, "uSNChanged"); ldb_msg_remove_attr(msg, "replPropertyMetaData"); + if (!ldb_msg_find_element(req->op.add.message, "instanceType")) { + ret = ldb_msg_add_fmt(msg, "instanceType", "%u", INSTANCE_TYPE_WRITE); + if (ret != LDB_SUCCESS) { + ldb_oom(ldb); + talloc_free(ac); + return ret; + } + } + /* * readd replicated attributes */ ret = ldb_msg_add_string(msg, "whenCreated", time_str); if (ret != LDB_SUCCESS) { ldb_oom(ldb); - return LDB_ERR_OPERATIONS_ERROR; + talloc_free(ac); + return ret; } /* build the replication meta_data */ @@ -540,6 +613,7 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req) nmd.ctr.ctr1.count); if (!nmd.ctr.ctr1.array) { ldb_oom(ldb); + talloc_free(ac); return LDB_ERR_OPERATIONS_ERROR; } @@ -555,10 +629,11 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req) ldb_debug_set(ldb, LDB_DEBUG_ERROR, "replmd_add: attribute '%s' not defined in schema\n", e->name); + talloc_free(ac); return LDB_ERR_NO_SUCH_ATTRIBUTE; } - if ((sa->systemFlags & 0x00000001) || (sa->systemFlags & 0x00000004)) { + if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) { /* if the attribute is not replicated (0x00000001) * or constructed (0x00000004) it has no metadata */ @@ -569,13 +644,9 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req) m->version = 1; m->originating_change_time = now; m->originating_invocation_id = *our_invocation_id; - m->originating_usn = seq_num; - m->local_usn = seq_num; + m->originating_usn = ac->seq_num; + m->local_usn = ac->seq_num; ni++; - - if (ldb_attr_cmp(e->name, ldb_dn_get_rdn_name(msg->dn))) { - rdn_attr = sa; - } } /* fix meta data count */ @@ -584,7 +655,11 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req) /* * sort meta data array, and move the rdn attribute entry to the end */ - replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, &rdn_attr->attributeID_id); + ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, schema, msg->dn); + if (ret != LDB_SUCCESS) { + talloc_free(ac); + return ret; + } /* generated NDR encoded values */ ndr_err = ndr_push_struct_blob(&guid_value, msg, @@ -593,6 +668,7 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req) (ndr_push_flags_fn_t)ndr_push_GUID); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { ldb_oom(ldb); + talloc_free(ac); return LDB_ERR_OPERATIONS_ERROR; } ndr_err = ndr_push_struct_blob(&nmd_value, msg, @@ -601,6 +677,7 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req) (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { ldb_oom(ldb); + talloc_free(ac); return LDB_ERR_OPERATIONS_ERROR; } @@ -610,27 +687,32 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req) ret = ldb_msg_add_value(msg, "objectGUID", &guid_value, NULL); if (ret != LDB_SUCCESS) { ldb_oom(ldb); - return LDB_ERR_OPERATIONS_ERROR; + talloc_free(ac); + return ret; } ret = ldb_msg_add_string(msg, "whenChanged", time_str); if (ret != LDB_SUCCESS) { ldb_oom(ldb); - return LDB_ERR_OPERATIONS_ERROR; + talloc_free(ac); + return ret; } - ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", seq_num); + ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num); if (ret != LDB_SUCCESS) { ldb_oom(ldb); - return LDB_ERR_OPERATIONS_ERROR; + talloc_free(ac); + return ret; } - ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", seq_num); + ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num); if (ret != LDB_SUCCESS) { ldb_oom(ldb); - return LDB_ERR_OPERATIONS_ERROR; + talloc_free(ac); + return ret; } ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL); if (ret != LDB_SUCCESS) { ldb_oom(ldb); - return LDB_ERR_OPERATIONS_ERROR; + talloc_free(ac); + return ret; } /* @@ -644,12 +726,14 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req) ac, replmd_op_callback, req); if (ret != LDB_SUCCESS) { + talloc_free(ac); return ret; } - ret = replmd_notify(module, msg->dn, seq_num); - if (ret != LDB_SUCCESS) { - return ret; + /* if a control is there remove if from the modified request */ + if (control && !save_controls(control, down_req, &saved_controls)) { + talloc_free(ac); + return LDB_ERR_OPERATIONS_ERROR; } /* go on with the call chain */ @@ -680,10 +764,7 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb, return LDB_ERR_OPERATIONS_ERROR; } - if ((a->systemFlags & 0x00000001) || (a->systemFlags & 0x00000004)) { - /* if the attribute is not replicated (0x00000001) - * or constructed (0x00000004) it has no metadata - */ + if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) { return LDB_SUCCESS; } @@ -758,8 +839,8 @@ static int replmd_update_rpmd(struct ldb_module *module, unix_to_nt_time(&now, t); /* search for the existing replPropertyMetaDataBlob */ - ret = ldb_search(ldb, msg, &res, msg->dn, LDB_SCOPE_BASE, attrs, NULL); - if (ret != LDB_SUCCESS || res->count < 1) { + ret = dsdb_search_dn_with_deleted(ldb, msg, &res, msg->dn, attrs); + if (ret != LDB_SUCCESS || res->count != 1) { DEBUG(0,(__location__ ": Object %s failed to find replPropertyMetaData\n", ldb_dn_get_linearized(msg->dn))); return LDB_ERR_OPERATIONS_ERROR; @@ -812,6 +893,11 @@ static int replmd_update_rpmd(struct ldb_module *module, return LDB_ERR_OPERATIONS_ERROR; } + ret = replmd_replPropertyMetaDataCtr1_sort(&omd.ctr.ctr1, schema, msg->dn); + if (ret != LDB_SUCCESS) { + return ret; + } + ndr_err = ndr_push_struct_blob(md_value, msg, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &omd, @@ -829,11 +915,6 @@ static int replmd_update_rpmd(struct ldb_module *module, return ret; } - ret = replmd_notify(module, msg->dn, *seq_num); - if (ret != LDB_SUCCESS) { - return ret; - } - el->num_values = 1; el->values = md_value; } @@ -849,9 +930,10 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req) const struct dsdb_schema *schema; struct ldb_request *down_req; struct ldb_message *msg; - int ret; + struct ldb_result *res; time_t t = time(NULL); uint64_t seq_num = 0; + int ret; /* do not manipulate our control entries */ if (ldb_dn_is_special(req->op.mod.message->dn)) { @@ -866,6 +948,7 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req) if (!schema) { ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_modify: no dsdb_schema loaded"); + DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb))); return LDB_ERR_CONSTRAINT_VIOLATION; } @@ -879,13 +962,12 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req) /* we have to copy the message as the caller might have it as a const */ msg = ldb_msg_copy_shallow(ac, req->op.mod.message); if (msg == NULL) { + ldb_oom(ldb); talloc_free(ac); return LDB_ERR_OPERATIONS_ERROR; } /* TODO: - * - get the whole old object - * - if the old object doesn't exist report an error * - give an error when a readonly attribute should * be modified * - merge the changed into the old object @@ -894,13 +976,19 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req) * attribute was changed */ - ret = replmd_update_rpmd(module, msg, &seq_num); + ret = dsdb_search_dn_with_deleted(ldb, msg, &res, msg->dn, NULL); if (ret != LDB_SUCCESS) { + talloc_free(ac); + return ret; + } + + ret = replmd_update_rpmd(module, msg, &ac->seq_num); + if (ret != LDB_SUCCESS) { + talloc_free(ac); return ret; } /* TODO: - * - sort the attributes by attid with replmd_ldb_message_sort() * - replace the old object with the newly constructed one */ @@ -910,6 +998,7 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req) ac, replmd_op_callback, req); if (ret != LDB_SUCCESS) { + talloc_free(ac); return ret; } talloc_steal(down_req, msg); @@ -919,12 +1008,12 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req) if (seq_num != 0) { if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) { talloc_free(ac); - return LDB_ERR_OPERATIONS_ERROR; + return ret; } if (add_uint64_element(msg, "uSNChanged", seq_num) != LDB_SUCCESS) { talloc_free(ac); - return LDB_ERR_OPERATIONS_ERROR; + return ret; } } @@ -1133,6 +1222,19 @@ static int replmd_replicated_apply_add(struct replmd_replicated_request *ar) return replmd_replicated_request_error(ar, ret); } + /* remove any message elements that have zero values */ + for (i=0; inum_elements; i++) { + if (msg->elements[i].num_values == 0) { + DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n", + msg->elements[i].name)); + memmove(&msg->elements[i], + &msg->elements[i+1], + sizeof(msg->elements[i])*(msg->num_elements - (i+1))); + msg->num_elements--; + i--; + } + } + /* * the meta data array is already sorted by the caller */ @@ -1344,22 +1446,9 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar) * * sort the new meta data array */ - { - struct replPropertyMetaData1 *rdn_p; - uint32_t rdn_idx = omd.ctr.ctr1.count - 1; - - rdn_p = &nmd.ctr.ctr1.array[rdn_idx]; - replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, &rdn_p->attid); - } - - /* create the meta data value */ - ndr_err = ndr_push_struct_blob(&nmd_value, msg, - lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), - &nmd, - (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err); - return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status)); + ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ar->schema, msg->dn); + if (ret != LDB_SUCCESS) { + return ret; } /* @@ -1385,6 +1474,16 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar) nmd.ctr.ctr1.array[i].local_usn = seq_num; } + /* create the meta data value */ + ndr_err = ndr_push_struct_blob(&nmd_value, msg, + lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), + &nmd, + (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err); + return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status)); + } + /* * when we know that we'll modify the record, add the whenChanged, uSNChanged * and replPopertyMetaData attributes @@ -1510,6 +1609,13 @@ static int replmd_replicated_apply_next(struct replmd_replicated_request *ar) ar, replmd_replicated_apply_search_callback, ar->req); + + ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_DELETED_OID, true, NULL); + if (ret != LDB_SUCCESS) { + return ret; + } + + if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret); return ldb_next_request(ar->module, search_req); @@ -1543,12 +1649,6 @@ static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req, return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS); } -static int replmd_drsuapi_DsReplicaCursor2_compare(const struct drsuapi_DsReplicaCursor2 *c1, - const struct drsuapi_DsReplicaCursor2 *c2) -{ - return GUID_compare(&c1->source_dsa_invocation_id, &c2->source_dsa_invocation_id); -} - static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar) { struct ldb_context *ldb; @@ -1703,7 +1803,7 @@ static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *a */ qsort(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count, sizeof(struct drsuapi_DsReplicaCursor2), - (comparison_fn_t)replmd_drsuapi_DsReplicaCursor2_compare); + (comparison_fn_t)drsuapi_DsReplicaCursor2_compare); /* * create the change ldb_message @@ -1946,6 +2046,7 @@ static int replmd_extended_replicated_objects(struct ldb_module *module, struct if (!ar->schema) { ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n"); talloc_free(ar); + DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb))); return LDB_ERR_CONSTRAINT_VIOLATION; } @@ -2033,10 +2134,11 @@ static int replmd_process_linked_attribute(struct ldb_module *module, struct ldb_message_element *ret_el; TALLOC_CTX *tmp_ctx = talloc_new(la_entry); enum ndr_err_code ndr_err; - char *target_dn; struct ldb_request *mod_req; int ret; const struct dsdb_attribute *attr; + struct ldb_dn *target_dn; + uint64_t seq_num = 0; /* linked_attributes[0]: @@ -2123,7 +2225,9 @@ linked_attributes[0]: talloc_free(tmp_ctx); return ret; } - ret_el->values = talloc_array(msg, struct ldb_val, 1); + /* we allocate two entries here, in case we need a remove/add + pair */ + ret_el->values = talloc_array(msg, struct ldb_val, 2); if (!ret_el->values) { ldb_oom(ldb); talloc_free(tmp_ctx); @@ -2131,16 +2235,37 @@ linked_attributes[0]: } ret_el->num_values = 1; - target_dn = talloc_asprintf(tmp_ctx, ";;%s", - GUID_string(tmp_ctx, &target.guid), - dom_sid_string(tmp_ctx, &target.sid), - target.dn); - if (target_dn == NULL) { - ldb_oom(ldb); + ret = dsdb_find_dn_by_guid(ldb, tmp_ctx, GUID_string(tmp_ctx, &target.guid), &target_dn); + if (ret != LDB_SUCCESS) { + DEBUG(0,(__location__ ": Failed to map GUID %s to DN\n", GUID_string(tmp_ctx, &target.guid))); talloc_free(tmp_ctx); return LDB_ERR_OPERATIONS_ERROR; } - ret_el->values[0] = data_blob_string_const(target_dn); + + ret_el->values[0].data = (uint8_t *)ldb_dn_get_extended_linearized(tmp_ctx, target_dn, 1); + ret_el->values[0].length = strlen((char *)ret_el->values[0].data); + + ret = replmd_update_rpmd(module, msg, &seq_num); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + + /* we only change whenChanged and uSNChanged if the seq_num + has changed */ + if (seq_num != 0) { + time_t t = time(NULL); + + if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + + if (add_uint64_element(msg, "uSNChanged", seq_num) != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + } ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx, msg, @@ -2168,10 +2293,48 @@ linked_attributes[0]: ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL); } + if (ret == LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS) { + /* the link destination exists, we need to update it + * by deleting the old one for the same DN then adding + * the new one */ + msg->elements = talloc_realloc(msg, msg->elements, + struct ldb_message_element, + msg->num_elements+1); + if (msg->elements == NULL) { + ldb_oom(ldb); + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + /* this relies on the backend matching the old entry + only by the DN portion of the extended DN */ + msg->elements[1] = msg->elements[0]; + msg->elements[0].flags = LDB_FLAG_MOD_DELETE; + msg->num_elements++; + + ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx, + msg, + NULL, + NULL, + ldb_op_default_callback, + NULL); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + + /* Run the new request */ + ret = ldb_next_request(module, mod_req); + + if (ret == LDB_SUCCESS) { + ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL); + } + } + if (ret != LDB_SUCCESS) { ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s' %s\n", ldb_errstring(ldb), ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg)); + ret = LDB_SUCCESS; } talloc_free(tmp_ctx); @@ -2234,6 +2397,9 @@ static int replmd_prepare_commit(struct ldb_module *module) DLIST_REMOVE(replmd_private->la_list, la); ret = replmd_process_linked_attribute(module, la); if (ret != LDB_SUCCESS) { + talloc_free(replmd_private->la_ctx); + replmd_private->la_list = NULL; + replmd_private->la_ctx = NULL; return ret; } }