s4:dsdb Set 'notification' after the success of a change.
[ira/wip.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
index f9411fe95f2993a25328ca77fb285a6ef838b319..a0dd111bdf4e83dddf6e6c706ee04e544a98c969 100644 (file)
@@ -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; i<msg->num_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, "<GUID=%s>;<SID=%s>;%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;
                }
        }