From c6647bfd17539b9cca96e42b6871d16ef2fa728f Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Fri, 11 Sep 2009 19:07:38 +1000 Subject: s4-repl: on every ldb modify we need to update replPropertyMetaData Every time we change a ldb object with the repl_meta_data module loaded we need to update the replPropertyMetaData attribute to fix the timestamps and USNs of the attributes being changed. --- source4/dsdb/samdb/ldb_modules/repl_meta_data.c | 179 ++++++++++++++++++++++-- 1 file changed, 171 insertions(+), 8 deletions(-) (limited to 'source4/dsdb/samdb/ldb_modules/repl_meta_data.c') diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c index 6a2e6f2fc0..c469936645 100644 --- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c +++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c @@ -466,6 +466,163 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req) return ldb_next_request(module, down_req); } + +/* + * update the replPropertyMetaData for one element + */ +static int replmd_update_rpmd_element(struct ldb_context *ldb, + struct ldb_message *msg, + struct ldb_message_element *el, + struct replPropertyMetaDataBlob *omd, + struct dsdb_schema *schema, + uint64_t seq_num, + const struct GUID *our_invocation_id, + NTTIME now, + bool *modified) +{ + int i; + const struct dsdb_attribute *a; + struct replPropertyMetaData1 *md1; + + a = dsdb_attribute_by_lDAPDisplayName(schema, el->name); + if (a == NULL) { + DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n", + el->name)); + return LDB_ERR_OPERATIONS_ERROR; + } + + for (i=0; ictr.ctr1.count; i++) { + if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break; + } + if (i == omd->ctr.ctr1.count) { + /* we need to add a new one */ + omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array, + struct replPropertyMetaData1, omd->ctr.ctr1.count+1); + if (omd->ctr.ctr1.array == NULL) { + ldb_oom(ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + omd->ctr.ctr1.count++; + } + + md1 = &omd->ctr.ctr1.array[i]; + md1->version = 1; + md1->attid = a->attributeID_id; + md1->originating_change_time = now; + md1->originating_invocation_id = *our_invocation_id; + md1->originating_usn = seq_num; + md1->local_usn = seq_num; + + *modified = true; + return LDB_SUCCESS; +} + +/* + * update the replPropertyMetaData object each time we modify an + * object. This is needed for DRS replication, as the merge on the + * client is based on this object + */ +static int replmd_update_rpmd(struct ldb_context *ldb, struct ldb_message *msg, + uint64_t seq_num) +{ + const struct ldb_val *omd_value; + enum ndr_err_code ndr_err; + struct replPropertyMetaDataBlob omd; + int i; + bool modified = false; + struct dsdb_schema *schema; + time_t t = time(NULL); + NTTIME now; + const struct GUID *our_invocation_id; + int ret; + const char *attrs[] = { "replPropertyMetaData" , NULL }; + struct ldb_result *res; + + our_invocation_id = samdb_ntds_invocation_id(ldb); + if (!our_invocation_id) { + ldb_debug_set(ldb, LDB_DEBUG_ERROR, + __location__ ": replmd_update_rpmd: unable to find invocationId\n"); + return LDB_ERR_OPERATIONS_ERROR; + } + + 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) { + DEBUG(0,(__location__ ": Object %s failed to find replPropertyMetaData\n", + ldb_dn_get_linearized(msg->dn))); + return LDB_ERR_OPERATIONS_ERROR; + } + + + omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData"); + if (!omd_value) { + DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n", + ldb_dn_get_linearized(msg->dn))); + return LDB_ERR_OPERATIONS_ERROR; + } + + ndr_err = ndr_pull_struct_blob(omd_value, msg, + lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &omd, + (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n", + ldb_dn_get_linearized(msg->dn))); + return LDB_ERR_OPERATIONS_ERROR; + } + + if (omd.version != 1) { + DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n", + omd.version, ldb_dn_get_linearized(msg->dn))); + return LDB_ERR_OPERATIONS_ERROR; + } + + schema = dsdb_get_schema(ldb); + + for (i=0; inum_elements; i++) { + ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], &omd, schema, seq_num, + our_invocation_id, now, &modified); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + if (modified) { + struct ldb_val *md_value; + struct ldb_message_element *el; + + md_value = talloc(msg, struct ldb_val); + if (md_value == NULL) { + ldb_oom(ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + + ndr_err = ndr_push_struct_blob(md_value, msg, + lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), + &omd, + (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n", + ldb_dn_get_linearized(msg->dn))); + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el); + if (ret != LDB_SUCCESS) { + DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n", + ldb_dn_get_linearized(msg->dn))); + return ret; + } + + el->num_values = 1; + el->values = md_value; + } + + return LDB_SUCCESS; +} + + static int replmd_modify(struct ldb_module *module, struct ldb_request *req) { struct ldb_context *ldb; @@ -516,21 +673,27 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req) * if the caller set values to the same value * ignore the attribute, return success when no * attribute was changed - * - calculate the new replPropertyMetaData attribute */ + /* Get a sequence number from the backend */ + ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num); + if (ret != LDB_SUCCESS) { + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = replmd_update_rpmd(ldb, msg, seq_num); + if (ret != LDB_SUCCESS) { + return ret; + } + if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) { talloc_free(ac); return LDB_ERR_OPERATIONS_ERROR; } - /* Get a sequence number from the backend */ - ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num); - if (ret == LDB_SUCCESS) { - if (add_uint64_element(msg, "uSNChanged", seq_num) != LDB_SUCCESS) { - talloc_free(ac); - return LDB_ERR_OPERATIONS_ERROR; - } + if (add_uint64_element(msg, "uSNChanged", seq_num) != LDB_SUCCESS) { + talloc_free(ac); + return LDB_ERR_OPERATIONS_ERROR; } /* TODO: -- cgit