r20975: - implement handling of meta data an on originating add
authorStefan Metzmacher <metze@samba.org>
Tue, 23 Jan 2007 16:18:45 +0000 (16:18 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 19:44:06 +0000 (14:44 -0500)
there're a few things TODO, but it's a good start

we need to research if an originating change causes the replUpToDateVector
attribute to change...(I assume it, but needs testing)

metze
(This used to be commit fde0aabd9ae79fcefbcba34e6f9143f93ffcf96c)

source4/dsdb/samdb/ldb_modules/repl_meta_data.c

index 0ea73dcbd4cf37ca01f129291e9bd036d4fbe5c8..78e7aca92f1785d3e5cc9b7ce5fbfb039e37b553 100644 (file)
@@ -44,6 +44,7 @@
 #include "lib/ldb/include/ldb_errors.h"
 #include "lib/ldb/include/ldb_private.h"
 #include "dsdb/samdb/samdb.h"
+#include "dsdb/common/flags.h"
 #include "librpc/gen_ndr/ndr_misc.h"
 #include "librpc/gen_ndr/ndr_drsuapi.h"
 #include "librpc/gen_ndr/ndr_drsblobs.h"
@@ -114,19 +115,6 @@ static struct replmd_replicated_request *replmd_replicated_init_handle(struct ld
        return ar;
 }
 
-static struct ldb_message_element *replmd_find_attribute(const struct ldb_message *msg, const char *name)
-{
-       int i;
-
-       for (i = 0; i < msg->num_elements; i++) {
-               if (ldb_attr_cmp(name, msg->elements[i].name) == 0) {
-                       return &msg->elements[i];
-               }
-       }
-
-       return NULL;
-}
-
 /*
   add a time element to a record
 */
@@ -304,68 +292,283 @@ static int replmd_add_originating(struct ldb_module *module,
                                  const struct dsdb_schema *schema,
                                  const struct dsdb_control_current_partition *partition)
 {
+       NTSTATUS nt_status;
        struct ldb_request *down_req;
-       struct ldb_message_element *attribute;
        struct ldb_message *msg;
-       struct ldb_val v;
+       uint32_t instance_type;
+       struct ldb_dn *new_dn;
+       const char *rdn_name;
+       const char *rdn_name_upper;
+       const struct ldb_val *rdn_value = NULL;
+       const struct dsdb_attribute *rdn_attr = NULL;
        struct GUID guid;
+       struct ldb_val guid_value;
+       struct replPropertyMetaDataBlob nmd;
+       struct ldb_val nmd_value;
        uint64_t seq_num;
-       NTSTATUS nt_status;
-       int ret;
+       const struct GUID *our_invocation_id;
        time_t t = time(NULL);
+       NTTIME now;
+       char *time_str;
+       int ret;
+       uint32_t i, ni=0;
 
        ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_add_originating\n");
 
-       if ((attribute = replmd_find_attribute(req->op.add.message, "objectGUID")) != NULL ) {
-               return ldb_next_request(module, req);
+       if (ldb_msg_find_element(req->op.add.message, "objectGUID")) {
+               ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
+                             "replmd_add_originating: it's not allowed to add an object with objectGUID\n");
+               return LDB_ERR_UNWILLING_TO_PERFORM;
+       }
+
+       if (ldb_msg_find_element(req->op.add.message, "instanceType")) {
+               ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
+                             "replmd_add_originating: it's not allowed to add an object with instanceType\n");
+               return LDB_ERR_UNWILLING_TO_PERFORM;
        }
 
+       /* Get a sequence number from the backend */
+       ret = ldb_sequence_number(module->ldb, LDB_SEQ_NEXT, &seq_num);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       /* a new GUID */
+       guid = GUID_random();
+
+       /* get our invicationId */
+       our_invocation_id = samdb_ntds_invocation_id(module->ldb);
+       if (!our_invocation_id) {
+               ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
+                             "replmd_add_originating: unable to find invocationId\n");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       /* create a copy of the request */
        down_req = talloc(req, struct ldb_request);
        if (down_req == NULL) {
+               ldb_oom(module->ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
-
        *down_req = *req;
 
        /* we have to copy the message as the caller might have it as a const */
        down_req->op.add.message = msg = ldb_msg_copy_shallow(down_req, req->op.add.message);
        if (msg == NULL) {
                talloc_free(down_req);
+               ldb_oom(module->ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       /* a new GUID */
-       guid = GUID_random();
+       /* generated times */
+       unix_to_nt_time(&now, t);
+       time_str = ldb_timestring(msg, t);
+       if (!time_str) {
+               talloc_free(down_req);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
 
-       nt_status = ndr_push_struct_blob(&v, msg, &guid, 
-                                        (ndr_push_flags_fn_t)ndr_push_GUID);
-       if (!NT_STATUS_IS_OK(nt_status)) {
+       /*
+        * get details of the rdn name
+        */
+       rdn_name        = ldb_dn_get_rdn_name(msg->dn);
+       if (!rdn_name) {
+               talloc_free(down_req);
+               ldb_oom(module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       rdn_attr        = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
+       if (!rdn_attr) {
+               talloc_free(down_req);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       rdn_value       = ldb_dn_get_rdn_val(msg->dn);
+       if (!rdn_value) {
                talloc_free(down_req);
+               ldb_oom(module->ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       ret = ldb_msg_add_value(msg, "objectGUID", &v, NULL);
-       if (ret) {
+       /* 
+        * remove autogenerated attributes
+        */
+       ldb_msg_remove_attr(msg, rdn_name);
+       ldb_msg_remove_attr(msg, "name");
+       ldb_msg_remove_attr(msg, "whenCreated");
+       ldb_msg_remove_attr(msg, "whenChanged");
+       ldb_msg_remove_attr(msg, "uSNCreated");
+       ldb_msg_remove_attr(msg, "uSNChanged");
+       ldb_msg_remove_attr(msg, "replPropertyMetaData");
+
+       /*
+        * TODO: construct a new DN out of:
+        *       - the parent DN
+        *       - the upper case of rdn_attr->LDAPDisplayName
+        *       - rdn_value
+        */
+       new_dn = ldb_dn_copy(msg, msg->dn);
+       if (!new_dn) {
                talloc_free(down_req);
-               return ret;
+               ldb_oom(module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
        }
-       
-       if (add_time_element(msg, "whenCreated", t) != 0 ||
-           add_time_element(msg, "whenChanged", t) != 0) {
+       rdn_name_upper = strupper_talloc(msg, rdn_attr->lDAPDisplayName);
+       if (!rdn_name_upper) {
+               talloc_free(down_req);
+               ldb_oom(module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ret = ldb_dn_set_component(new_dn, 0, rdn_name_upper, *rdn_value);
+       if (ret != LDB_SUCCESS) {
                talloc_free(down_req);
+               ldb_oom(module->ldb);
                return LDB_ERR_OPERATIONS_ERROR;
        }
+       msg->dn = new_dn;
 
-       /* Get a sequence number from the backend */
-       ret = ldb_sequence_number(module->ldb, LDB_SEQ_NEXT, &seq_num);
-       if (ret == LDB_SUCCESS) {
-               if (add_uint64_element(msg, "uSNCreated", seq_num) != 0 ||
-                   add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
+       /*
+        * TODO: calculate correct instance type
+        */
+       instance_type = INSTANCE_TYPE_WRITE;
+       if (ldb_dn_compare(partition->dn, msg->dn) == 0) {
+               instance_type |= INSTANCE_TYPE_IS_NC_HEAD;
+               if (ldb_dn_compare(msg->dn, samdb_base_dn(module->ldb)) != 0) {
+                       instance_type |= INSTANCE_TYPE_NC_ABOVE;
+               }
+       }
+
+       /*
+        * readd replicated attributes
+        */
+       ret = ldb_msg_add_value(msg, rdn_attr->lDAPDisplayName, rdn_value, NULL);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(down_req);
+               ldb_oom(module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ret = ldb_msg_add_value(msg, "name", rdn_value, NULL);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(down_req);
+               ldb_oom(module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ret = ldb_msg_add_string(msg, "whenCreated", time_str);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(down_req);
+               ldb_oom(module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ret = ldb_msg_add_fmt(msg, "instanceType", "%u", instance_type);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(down_req);
+               ldb_oom(module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       /* build the replication meta_data */
+       ZERO_STRUCT(nmd);
+       nmd.version             = 1;
+       nmd.ctr.ctr1.count      = msg->num_elements;
+       nmd.ctr.ctr1.array      = talloc_array(msg,
+                                              struct replPropertyMetaData1,
+                                              nmd.ctr.ctr1.count);
+       if (!nmd.ctr.ctr1.array) {
+               talloc_free(down_req);
+               ldb_oom(module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       for (i=0; i < msg->num_elements; i++) {
+               struct ldb_message_element *e = &msg->elements[i];
+               struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
+               const struct dsdb_attribute *sa;
+
+               sa = dsdb_attribute_by_lDAPDisplayName(schema, e->name);
+               if (!sa) {
+                       ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
+                                     "replmd_add_originating: attribute '%s' not defined in schema\n",
+                                     e->name);
                        talloc_free(down_req);
-                       return LDB_ERR_OPERATIONS_ERROR;
+                       return LDB_ERR_NO_SUCH_ATTRIBUTE;
                }
+
+               if (sa->systemFlags & 0x00000001) {
+                       /* attribute is not replicated so it has no meta data */
+                       continue;
+               }
+
+               m->attid                        = sa->attributeID_id;
+               m->version                      = 1;
+               m->orginating_time              = now;
+               m->orginating_invocation_id     = *our_invocation_id;
+               m->orginating_usn               = seq_num;
+               m->local_usn                    = seq_num;
+               ni++;
        }
 
+       /* fix meta data count */
+       nmd.ctr.ctr1.count = ni;
+
+       /*
+        * sort meta data array, and move the rdn attribute entry to the end
+        */
+       replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, &rdn_attr->attributeID_id);
+
+       /* generated NDR encoded values */
+       nt_status = ndr_push_struct_blob(&guid_value, msg, &guid, 
+                                        (ndr_push_flags_fn_t)ndr_push_GUID);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               talloc_free(down_req);
+               ldb_oom(module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       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)) {
+               talloc_free(down_req);
+               ldb_oom(module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       /*
+        * add the autogenerated values
+        */
+       ret = ldb_msg_add_value(msg, "objectGUID", &guid_value, NULL);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(down_req);
+               ldb_oom(module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ret = ldb_msg_add_string(msg, "whenChanged", time_str);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(down_req);
+               ldb_oom(module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ret = samdb_msg_add_uint64(module->ldb, msg, msg, "uSNCreated", seq_num);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(down_req);
+               ldb_oom(module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ret = samdb_msg_add_uint64(module->ldb, msg, msg, "uSNChanged", seq_num);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(down_req);
+               ldb_oom(module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(down_req);
+               ldb_oom(module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       /*
+        * sort the attributes by attid before storing the object
+        */
+       replmd_ldb_message_sort(msg, schema);
+
        ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
 
        /* go on with the call chain */