From: Andrew Bartlett Date: Mon, 21 Mar 2016 00:32:55 +0000 (+1300) Subject: dsdb: Split rename case out of replmd_op_possible_conflict_callback X-Git-Tag: tdb-1.3.10~973 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8938f384b7aaeea5131eb854307787805b648f34;p=thirdparty%2Fsamba.git dsdb: Split rename case out of replmd_op_possible_conflict_callback This avoids running this code path, originally written for the add case, in a semi-async manner in the rename case, which caused both bugs and complexity. This does create a deal of duplicated code, but it is easier to follow because there are no longer special cases for ADD and RENAME in the "common" code and the behaviour of ldb_module_done() and the callbacks is well defined and expected Signed-off-by: Andrew Bartlett Reviewed-by: Garming Sam %s\n", ldb_dn_get_linearized(ar->search_msg->dn), ldb_dn_get_linearized(msg->dn))); - res = talloc_zero(tmp_ctx, struct ldb_result); - if (!res) { + ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn, + DSDB_FLAG_NEXT_MODULE, ar->req); + + if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) { talloc_free(tmp_ctx); - return ldb_oom(ldb_module_get_ctx(ar->module)); + ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s", + ldb_dn_get_linearized(ar->search_msg->dn), + ldb_dn_get_linearized(msg->dn), + ldb_errstring(ldb_module_get_ctx(ar->module))); + return ret; } - /* pass rename to the next module - * so it doesn't appear as an originating update */ - ret = ldb_build_rename_req(&req, ldb_module_get_ctx(ar->module), tmp_ctx, - ar->search_msg->dn, msg->dn, - NULL, - ar, - replmd_op_rename_callback, - parent); - LDB_REQ_SET_LOCATION(req); + ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc); if (ret != LDB_SUCCESS) { - talloc_free(tmp_ctx); - return ret; + ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), + "Failed to determine if we are an RODC when attempting to form conflict DN: %s", + ldb_errstring(ldb_module_get_ctx(ar->module))); + return LDB_ERR_OPERATIONS_ERROR; + } + /* + * we have a conflict, and need to decide if we will keep the + * new record or the old record + */ + + conflict_dn = msg->dn; + + if (rodc) { + /* + * We are on an RODC, or were a GC for this + * partition, so we have to fail this until + * someone who owns the partition sorts it + * out + */ + ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), + "Conflict adding object '%s' from incoming replication but we are read only for the partition. \n" + " - We must fail the operation until a master for this partition resolves the conflict", + ldb_dn_get_linearized(conflict_dn)); + goto failed; } - ret = dsdb_request_add_controls(req, DSDB_MODIFY_RELAX); + /* + * first we need the replPropertyMetaData attribute from the + * old record + */ + ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn, + attrs, + DSDB_FLAG_NEXT_MODULE | + DSDB_SEARCH_SHOW_DELETED | + DSDB_SEARCH_SHOW_RECYCLED, ar->req); if (ret != LDB_SUCCESS) { - talloc_free(tmp_ctx); - return ret; + DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n", + ldb_dn_get_linearized(conflict_dn))); + goto failed; + } + + omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData"); + if (omd_value == NULL) { + DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n", + ldb_dn_get_linearized(conflict_dn))); + goto failed; + } + + ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd, + (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n", + ldb_dn_get_linearized(conflict_dn))); + goto failed; + } + + rmd = ar->objs->objects[ar->index_current].meta_data; + + /* we decide which is newer based on the RPMD on the name + attribute. See [MS-DRSR] ResolveNameConflict */ + rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name); + omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name); + if (!rmd_name || !omd_name) { + DEBUG(0,(__location__ ": Failed to find name attribute in replPropertyMetaData for %s\n", + ldb_dn_get_linearized(conflict_dn))); + goto failed; + } + + rename_incoming_record = !(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) && + !replmd_replPropertyMetaData1_is_newer(omd_name, rmd_name); + + if (rename_incoming_record) { + + new_dn = replmd_conflict_dn(msg, msg->dn, + &ar->objs->objects[ar->index_current].object_guid); + if (new_dn == NULL) { + ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), + "Failed to form conflict DN for %s\n", + ldb_dn_get_linearized(msg->dn)); + + return replmd_replicated_request_werror(ar, WERR_NOMEM); + } + + ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn, + DSDB_FLAG_NEXT_MODULE, ar->req); + if (ret != LDB_SUCCESS) { + ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), + "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n", + ldb_dn_get_linearized(conflict_dn), + ldb_dn_get_linearized(ar->search_msg->dn), + ldb_dn_get_linearized(new_dn), + ldb_errstring(ldb_module_get_ctx(ar->module))); + return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR); + } + + msg->dn = new_dn; + *renamed = true; + return LDB_SUCCESS; } - ret = ldb_next_request(ar->module, req); + /* we are renaming the existing record */ - if (ret == LDB_SUCCESS) { - ret = ldb_wait(req->handle, LDB_WAIT_ALL); + guid = samdb_result_guid(res->msgs[0], "objectGUID"); + if (GUID_all_zero(&guid)) { + DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n", + ldb_dn_get_linearized(conflict_dn))); + goto failed; + } + + new_dn = replmd_conflict_dn(tmp_ctx, conflict_dn, &guid); + if (new_dn == NULL) { + DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n", + ldb_dn_get_linearized(conflict_dn))); + goto failed; + } + + DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n", + ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn))); + + ret = dsdb_module_rename(ar->module, conflict_dn, new_dn, + DSDB_FLAG_OWN_MODULE, ar->req); + if (ret != LDB_SUCCESS) { + DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n", + ldb_dn_get_linearized(conflict_dn), + ldb_dn_get_linearized(new_dn), + ldb_errstring(ldb_module_get_ctx(ar->module)))); + goto failed; } + /* + * now we need to ensure that the rename is seen as an + * originating update. We do that with a modify. + */ + ret = replmd_name_modify(ar, ar->req, new_dn); + if (ret != LDB_SUCCESS) { + goto failed; + } + + DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n", + ldb_dn_get_linearized(ar->search_msg->dn), + ldb_dn_get_linearized(msg->dn))); + + + ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn, + DSDB_FLAG_NEXT_MODULE, ar->req); + if (ret != LDB_SUCCESS) { + DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n", + ldb_dn_get_linearized(ar->search_msg->dn), + ldb_dn_get_linearized(msg->dn), + ldb_errstring(ldb_module_get_ctx(ar->module)))); + goto failed; + } +failed: + + /* + * On failure make the caller get the error + * This means replication will stop with an error, + * but there is not much else we can do. In the + * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is + * needed. + */ + talloc_free(tmp_ctx); return ret; } @@ -4489,44 +4632,10 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar) * replmd_replicated_apply_search_callback()) */ renamed = true; - ret = replmd_replicated_handle_rename(ar, msg, ar->req); + ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed); } - /* - * This particular error code means that we already tried the - * conflict algrorithm, and the existing record name was newer, so we - * need to rename the incoming record - */ - if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) { - struct ldb_dn *new_dn; - - new_dn = replmd_conflict_dn(msg, msg->dn, - &ar->objs->objects[ar->index_current].object_guid); - if (new_dn == NULL) { - ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), - "Failed to form conflict DN for %s\n", - ldb_dn_get_linearized(msg->dn)); - - return replmd_replicated_request_werror(ar, WERR_NOMEM); - } - - ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn, - DSDB_FLAG_NEXT_MODULE, ar->req); - if (ret != LDB_SUCCESS) { - ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), - "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n", - ldb_dn_get_linearized(msg->dn), - ldb_dn_get_linearized(ar->search_msg->dn), - ldb_dn_get_linearized(new_dn), - ldb_errstring(ldb_module_get_ctx(ar->module))); - return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR); - } - - /* Set the callback to one that will fix up the name to be a conflict DN */ - callback = replmd_op_name_modify_callback; - msg->dn = new_dn; - renamed = true; - } else if (ret != LDB_SUCCESS) { + if (ret != LDB_SUCCESS) { ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_replicated_request rename %s => %s failed - %s\n", ldb_dn_get_linearized(ar->search_msg->dn), @@ -4535,6 +4644,14 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar) return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR); } + if (renamed == true) { + /* + * Set the callback to one that will fix up the name + * metadata on the new conflict DN + */ + callback = replmd_op_name_modify_callback; + } + ZERO_STRUCT(nmd); nmd.version = 1; nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;