Author: metze
Date: 2007-01-13 11:17:27 +0000 (Sat, 13 Jan 2007)
New Revision: 20727

WebSVN: 
http://websvn.samba.org/cgi-bin/viewcvs.cgi?view=rev&root=samba&rev=20727

Log:
implement basic merging of replicated objects when it already exist
in the ldb

metze
Modified:
   branches/SAMBA_4_0/source/dsdb/samdb/ldb_modules/repl_meta_data.c


Changeset:
Modified: branches/SAMBA_4_0/source/dsdb/samdb/ldb_modules/repl_meta_data.c
===================================================================
--- branches/SAMBA_4_0/source/dsdb/samdb/ldb_modules/repl_meta_data.c   
2007-01-13 10:53:12 UTC (rev 20726)
+++ branches/SAMBA_4_0/source/dsdb/samdb/ldb_modules/repl_meta_data.c   
2007-01-13 11:17:27 UTC (rev 20727)
@@ -526,15 +526,257 @@
 #endif
 }
 
+static int replmd_replPropertyMetaData1_attid_compare(struct 
replPropertyMetaData1 *m1,
+                                                     struct 
replPropertyMetaData1 *m2)
+{
+       return m1->attid - m2->attid;
+}
+
+static int replmd_replPropertyMetaData1_conflict_compare(struct 
replPropertyMetaData1 *m1,
+                                                        struct 
replPropertyMetaData1 *m2)
+{
+       int ret;
+
+       if (m1->version != m2->version) {
+               return m1->version - m2->version;
+       }
+
+       if (m1->orginating_time != m2->orginating_time) {
+               return m1->orginating_time - m2->orginating_time;
+       }
+
+       ret = GUID_compare(&m1->orginating_invocation_id, 
&m2->orginating_invocation_id);
+       if (ret != 0) {
+               return ret;
+       }
+
+       return m1->orginating_usn - m2->orginating_usn;
+}
+
+static int replmd_replicated_apply_merge_callback(struct ldb_context *ldb,
+                                                 void *private_data,
+                                                 struct ldb_reply *ares)
+{
+#ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async 
code */ 
+       struct replmd_replicated_request *ar = talloc_get_type(private_data,
+                                              struct 
replmd_replicated_request);
+
+       ret = ldb_next_request(ar->module, ar->sub.change_req);
+       if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
+
+       ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
+       if (ar->sub.change_ret != LDB_SUCCESS) {
+               return replmd_replicated_request_error(ar, ar->sub.change_ret);
+       }
+
+       talloc_free(ar->sub.mem_ctx);
+       ZERO_STRUCT(ar->sub);
+
+       ar->index_current++;
+
+       return LDB_SUCCESS;
+#else
+       return LDB_SUCCESS;
+#endif
+}
+
 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
 {
+       NTSTATUS nt_status;
+       struct ldb_message *msg;
+       struct replPropertyMetaDataBlob *rmd;
+       struct replPropertyMetaDataBlob omd;
+       const struct ldb_val *omd_value;
+       struct replPropertyMetaDataBlob nmd;
+       struct ldb_val nmd_value;
+       uint32_t i,j,ni=0;
+       uint32_t removed_attrs = 0;
+       uint64_t seq_num;
+       int ret;
+
+       msg = ar->objs->objects[ar->index_current].msg;
+       rmd = ar->objs->objects[ar->index_current].meta_data;
+       ZERO_STRUCT(omd);
+       omd.version = 1;
+
+       /*
+        * TODO: add rename conflict handling
+        */
+       if (ldb_dn_compare(msg->dn, ar->sub.search_msg->dn) != 0) {
+               ldb_debug(ar->module->ldb, LDB_DEBUG_FATAL, 
"replmd_replicated_apply_merge[%u]: rename not supported",
+                         ar->index_current);
+               ldb_debug(ar->module->ldb, LDB_DEBUG_FATAL, "%s => %s\n",
+                         ldb_dn_get_linearized(ar->sub.search_msg->dn),
+                         ldb_dn_get_linearized(msg->dn));
+               return replmd_replicated_request_werror(ar, WERR_NOT_SUPPORTED);
+       }
+
+       ret = ldb_sequence_number(ar->module->ldb, LDB_SEQ_NEXT, &seq_num);
+       if (ret != LDB_SUCCESS) {
+               return replmd_replicated_request_error(ar, ret);
+       }
+
+       /* find existing meta data */
+       omd_value = ldb_msg_find_ldb_val(ar->sub.search_msg, 
"replPropertyMetaData");
+       if (omd_value) {
+               nt_status = ndr_pull_struct_blob(omd_value, ar->sub.mem_ctx, 
&omd,
+                                                
(ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
+               if (!NT_STATUS_IS_OK(nt_status)) {
+                       return replmd_replicated_request_werror(ar, 
ntstatus_to_werror(nt_status));
+               }
+
+               if (omd.version != 1) {
+                       return replmd_replicated_request_werror(ar, 
WERR_DS_DRA_INTERNAL_ERROR);
+               }
+       }
+
+       ZERO_STRUCT(nmd);
+       nmd.version = 1;
+       nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
+       nmd.ctr.ctr1.array = talloc_array(ar->sub.mem_ctx,
+                                         struct replPropertyMetaData1,
+                                         nmd.ctr.ctr1.count);
+       if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, 
WERR_NOMEM);
+
+       /* first copy the old meta data */
+       for (i=0; i < omd.ctr.ctr1.count; i++) {
+               nmd.ctr.ctr1.array[ni]  = omd.ctr.ctr1.array[i];
+               ni++;
+       }
+
+       /* now merge in the new meta data */
+       for (i=0; i < rmd->ctr.ctr1.count; i++) {
+               bool found = false;
+
+               rmd->ctr.ctr1.array[i].local_usn = seq_num;
+
+               for (j=0; j < ni; j++) {
+                       int cmp;
+
+                       if (rmd->ctr.ctr1.array[i].attid != 
nmd.ctr.ctr1.array[j].attid) {
+                               continue;
+                       }
+
+                       cmp = 
replmd_replPropertyMetaData1_conflict_compare(&rmd->ctr.ctr1.array[i],
+                                                                           
&nmd.ctr.ctr1.array[j]);
+                       if (cmp > 0) {
+                               /* replace the entry */
+                               nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
+                               found = true;
+                               break;
+                       }
+
+                       /* we don't want to apply this change so remove the 
attribute */
+                       ldb_msg_remove_element(msg, 
&msg->elements[i-removed_attrs]);
+                       removed_attrs++;
+
+                       found = true;
+                       break;
+               }
+
+               if (found) continue;
+
+               nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
+               ni++;
+       }
+
+       /*
+        * finally correct the size of the meta_data array
+        */
+       nmd.ctr.ctr1.count = ni;
+
+       /*
+        * the rdn attribute (the alias for the name attribute),
+        * 'cn' for most objects is the last entry in the meta data array
+        * we have stored
+        *
+        * as it should stay the last one in the new list, we move it to the end
+        */
+       {
+               struct replPropertyMetaData1 *rdn_p, rdn, *last_p;
+               uint32_t rdn_idx = omd.ctr.ctr1.count - 1;
+               uint32_t last_idx = ni - 1;
+
+               rdn_p = &nmd.ctr.ctr1.array[rdn_idx];
+               rdn = *rdn_p;
+               last_p = &nmd.ctr.ctr1.array[last_idx];
+
+               if (last_idx > rdn_idx) {
+                       memmove(rdn_p, rdn_p+1, (last_idx - 
rdn_idx)*sizeof(rdn));
+                       *last_p = rdn;
+               }
+       }
+
+       /*
+        * sort the meta data entries by attid, but skip the last one containing
+        * the rdn attribute
+        */
+       qsort(nmd.ctr.ctr1.array, nmd.ctr.ctr1.count - 1,
+             sizeof(struct replPropertyMetaData1),
+             (comparison_fn_t)replmd_replPropertyMetaData1_attid_compare);
+
+       /* create the meta data value */
+       nt_status = ndr_push_struct_blob(&nmd_value, msg, &nmd,
+                                        
(ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               return replmd_replicated_request_werror(ar, 
ntstatus_to_werror(nt_status));
+       }
+
+       /*
+        * check if some replicated attributes left, otherwise skip the 
ldb_modify() call
+        */
+       if (msg->num_elements == 0) {
+               ldb_debug(ar->module->ldb, LDB_DEBUG_TRACE, 
"replmd_replicated_apply_merge[%u]: skip replace\n",
+                         ar->index_current);
+               goto next_object;
+       }
+
+       ldb_debug(ar->module->ldb, LDB_DEBUG_TRACE, 
"replmd_replicated_apply_merge[%u]: replace %u attributes\n",
+                 ar->index_current, msg->num_elements);
+
+       /*
+        * when we now that we'll modify the record, add the whenChanged, 
uSNChanged
+        * and replPopertyMetaData attributes
+        */
+       ret = ldb_msg_add_string(msg, "whenChanged", 
ar->objs->objects[ar->index_current].when_changed);
+       if (ret != LDB_SUCCESS) {
+               return replmd_replicated_request_error(ar, ret);
+       }
+       ret = samdb_msg_add_uint64(ar->module->ldb, msg, msg, "uSNChanged", 
seq_num);
+       if (ret != LDB_SUCCESS) {
+               return replmd_replicated_request_error(ar, ret);
+       }
+       ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
+       if (ret != LDB_SUCCESS) {
+               return replmd_replicated_request_error(ar, ret);
+       }
+
+       /* we want to replace the old values */
+       for (i=0; i < msg->num_elements; i++) {
+               msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+       }
+
+       ret = ldb_build_mod_req(&ar->sub.change_req,
+                               ar->module->ldb,
+                               ar->sub.mem_ctx,
+                               msg,
+                               NULL,
+                               ar,
+                               replmd_replicated_apply_merge_callback);
+       if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
+
 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async 
code */ 
-#error sorry replmd_replicated_apply_merge not implemented
+       return ldb_next_request(ar->module, ar->sub.change_req);
 #else
-       ldb_debug(ar->module->ldb, LDB_DEBUG_FATAL,
-                 "replmd_replicated_apply_merge: ignore [%u]\n",
-                 ar->index_current);
+       ret = ldb_next_request(ar->module, ar->sub.change_req);
+       if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
 
+       ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
+       if (ar->sub.change_ret != LDB_SUCCESS) {
+               return replmd_replicated_request_error(ar, ar->sub.change_ret);
+       }
+
+next_object:
        talloc_free(ar->sub.mem_ctx);
        ZERO_STRUCT(ar->sub);
 

Reply via email to