[SCM] Samba Shared Repository - branch master updated

Stefan Metzmacher metze at samba.org
Tue May 28 10:12:02 MDT 2013


The branch, master has been updated
       via  277831e dsdb-repl_meta_data: Move TODO comment about conflicts and missing parents
       via  2d38009 torture: Add tests of rename behaviour to replica_sync.py
       via  6960f8e dsdb-repl_meta_data: Handle renames better, considering only the RDN as given, and then the parent as given
       via  54e092b dsdb-linked_attributes: Do not crash if the target GUID can not be found
      from  3bd686c tdb: fix logging of offets and lengths.

http://gitweb.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit 277831eaf140c06bb25a3549434bc4acd4f533ec
Author: Andrew Bartlett <abartlet at samba.org>
Date:   Thu May 16 17:51:56 2013 +1000

    dsdb-repl_meta_data: Move TODO comment about conflicts and missing parents
    
    Reviewed-by: Stefan Metzmacher <metze at samba.org>
    
    Autobuild-User(master): Stefan Metzmacher <metze at samba.org>
    Autobuild-Date(master): Tue May 28 18:11:00 CEST 2013 on sn-devel-104

commit 2d38009852487ab3572b66817ed0492333b5ec7d
Author: Andrew Bartlett <abartlet at samba.org>
Date:   Thu May 16 15:24:25 2013 +1000

    torture: Add tests of rename behaviour to replica_sync.py
    
    Reviewed-by: Stefan Metzmacher <metze at samba.org>

commit 6960f8e4c3d683103580aa91605f951bc227dbed
Author: Andrew Bartlett <abartlet at samba.org>
Date:   Thu May 16 15:19:20 2013 +1000

    dsdb-repl_meta_data: Handle renames better, considering only the RDN as given, and then the parent as given
    
    This ignores the full DN as given, because the parent compents might be out of date.
    
    Andrew Bartlett
    
    Reviewed-by: Stefan Metzmacher <metze at samba.org>

commit 54e092bb31001879f4d7a49e36a226c27b4d9843
Author: Andrew Bartlett <abartlet at samba.org>
Date:   Thu May 16 13:52:51 2013 +1000

    dsdb-linked_attributes: Do not crash if the target GUID can not be found
    
    Note that we must not give an error when we cannot find the object
    that should hold the backlink, there really isn't anything we can do
    in this case.
    
    Andrew Bartlett
    
    Reviewed-by: Stefan Metzmacher <metze at samba.org>

-----------------------------------------------------------------------

Summary of changes:
 source4/dsdb/samdb/ldb_modules/linked_attributes.c |   15 ++
 source4/dsdb/samdb/ldb_modules/repl_meta_data.c    |  230 ++++++++++++-------
 source4/torture/drs/python/replica_sync.py         |  104 +++++++++
 3 files changed, 264 insertions(+), 85 deletions(-)


Changeset truncated at 500 lines:

diff --git a/source4/dsdb/samdb/ldb_modules/linked_attributes.c b/source4/dsdb/samdb/ldb_modules/linked_attributes.c
index a55ec87..eb57f91 100644
--- a/source4/dsdb/samdb/ldb_modules/linked_attributes.c
+++ b/source4/dsdb/samdb/ldb_modules/linked_attributes.c
@@ -4,6 +4,7 @@
    Copyright (C) Andrew Bartlett <abartlet at samba.org> 2007
    Copyright (C) Simo Sorce <idra at samba.org> 2008
    Copyright (C) Matthieu Patou <mat at matws.net> 2011
+   Copyright (C) Andrew Tridgell 2009
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -729,6 +730,20 @@ static int linked_attributes_fix_links(struct ldb_module *module,
 			talloc_free(tmp_ctx);
 			return ret;
 		}
+		if (res->count == 0) {
+			/* Forward link without backlink object remaining - nothing to do here */
+			continue;
+		}
+		if (res->count != 1) {
+			ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - target GUID %s found more than once!",
+					       el->name, target->lDAPDisplayName,
+					       ldb_dn_get_linearized(old_dn),
+					       ldb_dn_get_linearized(dsdb_dn->dn),
+					       GUID_string(tmp_ctx, &link_guid));
+			talloc_free(tmp_ctx);
+			return LDB_ERR_OPERATIONS_ERROR;
+		}
+
 		msg = res->msgs[0];
 
 		if (msg->num_elements == 0) {
diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c
index 651cdf1..98e60d7 100644
--- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c
+++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c
@@ -94,6 +94,8 @@ struct replmd_replicated_request {
 	bool is_urgent;
 };
 
+static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar);
+
 enum urgent_situation {
 	REPL_URGENT_ON_CREATE = 1,
 	REPL_URGENT_ON_UPDATE = 2,
@@ -3656,15 +3658,6 @@ static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
 	int ret;
 	bool remote_isDeleted = false;
 
-	/*
-	 * TODO: check if the parent object exist
-	 */
-
-	/*
-	 * TODO: handle the conflict case where an object with the
-	 *       same name exist
-	 */
-
 	ldb = ldb_module_get_ctx(ar->module);
 	msg = ar->objs->objects[ar->index_current].msg;
 	md = ar->objs->objects[ar->index_current].meta_data;
@@ -3787,6 +3780,10 @@ static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request
 	}
 	if (ares->error != LDB_SUCCESS &&
 	    ares->error != LDB_ERR_NO_SUCH_OBJECT) {
+		/*
+		 * TODO: deal with the above error that the parent object doesn't exist
+		 */
+
 		return ldb_module_done(ar->req, ares->controls,
 					ares->response, ares->error);
 	}
@@ -3866,7 +3863,11 @@ static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request
 		break;
 
 	case LDB_REPLY_DONE:
-		ret = replmd_replicated_apply_add(ar);
+		if (ar->search_msg != NULL) {
+			ret = replmd_replicated_apply_merge(ar);
+		} else {
+			ret = replmd_replicated_apply_add(ar);
+		}
 		if (ret != LDB_SUCCESS) {
 			return ldb_module_done(ar->req, NULL, NULL, ret);
 		}
@@ -3892,7 +3893,11 @@ static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_re
 	ldb = ldb_module_get_ctx(ar->module);
 
 	if (!ar->objs->objects[ar->index_current].parent_guid_value.data) {
-		return replmd_replicated_apply_add(ar);
+		if (ar->search_msg != NULL) {
+			return replmd_replicated_apply_merge(ar);
+		} else {
+			return replmd_replicated_apply_add(ar);
+		}
 	}
 
 	tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].parent_guid_value);
@@ -3931,84 +3936,52 @@ static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_re
  */
 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
 					   struct ldb_message *msg,
-					   struct replPropertyMetaDataBlob *rmd,
-					   struct replPropertyMetaDataBlob *omd,
-					   struct ldb_request *parent,
-					   bool *renamed)
+					   struct ldb_request *parent)
 {
-	struct replPropertyMetaData1 *md_remote;
-	struct replPropertyMetaData1 *md_local;
-
-	*renamed = true;
-
-	if (ldb_dn_compare(msg->dn, ar->search_msg->dn) == 0) {
-		/* no rename */
-		return LDB_SUCCESS;
-	}
-
-	/* now we need to check for double renames. We could have a
-	 * local rename pending which our replication partner hasn't
-	 * received yet. We choose which one wins by looking at the
-	 * attribute stamps on the two objects, the newer one wins
-	 */
-	md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
-	md_local  = replmd_replPropertyMetaData1_find_attid(omd, DRSUAPI_ATTID_name);
-	/* if there is no name attribute then we have to assume the
-	   object we've received is in fact newer */
-	if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING ||
-	    !md_remote || !md_local ||
-	    replmd_replPropertyMetaData1_is_newer(md_local, md_remote)) {
-		struct ldb_request *req;
-		int ret;
-		TALLOC_CTX *tmp_ctx = talloc_new(msg);
-		struct ldb_result *res;
-
-		DEBUG(4,("replmd_replicated_request rename %s => %s\n",
-			 ldb_dn_get_linearized(ar->search_msg->dn),
-			 ldb_dn_get_linearized(msg->dn)));
-
+	struct ldb_request *req;
+	int ret;
+	TALLOC_CTX *tmp_ctx = talloc_new(msg);
+	struct ldb_result *res;
 
-		res = talloc_zero(tmp_ctx, struct ldb_result);
-		if (!res) {
-			talloc_free(tmp_ctx);
-			return ldb_oom(ldb_module_get_ctx(ar->module));
-		}
-
-		/* 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);
-		if (ret != LDB_SUCCESS) {
-			talloc_free(tmp_ctx);
-			return ret;
-		}
+	DEBUG(4,("replmd_replicated_request rename %s => %s\n",
+		 ldb_dn_get_linearized(ar->search_msg->dn),
+		 ldb_dn_get_linearized(msg->dn)));
 
-		ret = dsdb_request_add_controls(req, DSDB_MODIFY_RELAX);
-		if (ret != LDB_SUCCESS) {
-			talloc_free(tmp_ctx);
-			return ret;
-		}
 
-		ret = ldb_next_request(ar->module, req);
+	res = talloc_zero(tmp_ctx, struct ldb_result);
+	if (!res) {
+		talloc_free(tmp_ctx);
+		return ldb_oom(ldb_module_get_ctx(ar->module));
+	}
 
-		if (ret == LDB_SUCCESS) {
-			ret = ldb_wait(req->handle, LDB_WAIT_ALL);
-		}
+	/* 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);
+	if (ret != LDB_SUCCESS) {
+		talloc_free(tmp_ctx);
+		return ret;
+	}
 
+	ret = dsdb_request_add_controls(req, DSDB_MODIFY_RELAX);
+	if (ret != LDB_SUCCESS) {
 		talloc_free(tmp_ctx);
 		return ret;
 	}
 
-	/* we're going to keep our old object */
-	DEBUG(4,(__location__ ": Keeping object %s and rejecting older rename to %s\n",
-		 ldb_dn_get_linearized(ar->search_msg->dn),
-		 ldb_dn_get_linearized(msg->dn)));
-	return LDB_SUCCESS;
+	ret = ldb_next_request(ar->module, req);
+
+	if (ret == LDB_SUCCESS) {
+		ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+	}
+
+	talloc_free(tmp_ctx);
+	return ret;
 }
 
 
@@ -4062,9 +4035,22 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
 	remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
 						     "isDeleted", false);
 
-	/* handle renames that come in over DRS */
-	ret = replmd_replicated_handle_rename(ar, msg, rmd, &omd,
-					      ar->req, &renamed);
+	if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0) {
+		ret = LDB_SUCCESS;
+	} else {
+		/*
+		 * handle renames, even just by case that come in over
+		 * DRS.  Changes in the parent DN don't hit us here,
+		 * because the search for a parent will clean up those
+		 * components.
+		 *
+		 * We also have already filtered out the case where
+		 * the peer has an older name to what we have (see
+		 * replmd_replicated_apply_search_callback())
+		 */
+		renamed = true;
+		ret = replmd_replicated_handle_rename(ar, msg, ar->req);
+	}
 
 	/*
 	 * This particular error code means that we already tried the
@@ -4348,17 +4334,91 @@ static int replmd_replicated_apply_search_callback(struct ldb_request *req,
 		break;
 
 	case LDB_REPLY_DONE:
+	{
+		struct replPropertyMetaData1 *md_remote;
+		struct replPropertyMetaData1 *md_local;
+
+		struct replPropertyMetaDataBlob omd;
+		const struct ldb_val *omd_value;
+		struct replPropertyMetaDataBlob *rmd;
+		struct ldb_message *msg;
+
 		ar->objs->objects[ar->index_current].last_known_parent = NULL;
 
-		if (ar->search_msg != NULL) {
-			ret = replmd_replicated_apply_merge(ar);
-		} else {
+		/*
+		 * This is the ADD case, find the appropriate parent,
+		 * as this object doesn't exist locally:
+		 */
+		if (ar->search_msg == NULL) {
 			ret = replmd_replicated_apply_search_for_parent(ar);
+			if (ret != LDB_SUCCESS) {
+				return ldb_module_done(ar->req, NULL, NULL, ret);
+			}
+			talloc_free(ares);
+			return LDB_SUCCESS;
+		}
+
+		/*
+		 * Otherwise, in the MERGE case, work out if we are
+		 * attempting a rename, and if so find the parent the
+		 * newly renamed object wants to belong under (which
+		 * may not be the parent in it's attached string DN
+		 */
+		rmd = ar->objs->objects[ar->index_current].meta_data;
+		ZERO_STRUCT(omd);
+		omd.version = 1;
+
+		/* find existing meta data */
+		omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
+		if (omd_value) {
+			enum ndr_err_code ndr_err;
+			ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
+						       (ndr_pull_flags_fn_t)ndr_pull_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));
+			}
+
+			if (omd.version != 1) {
+				return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
+			}
+		}
+
+		/*
+		 * now we need to check for double renames. We could have a
+		 * local rename pending which our replication partner hasn't
+		 * received yet. We choose which one wins by looking at the
+		 * attribute stamps on the two objects, the newer one wins
+		 */
+		md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
+		md_local  = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
+		/* if there is no name attribute then we have to assume the
+		   object we've received is in fact newer */
+		if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING ||
+		    !md_remote || !md_local ||
+		    replmd_replPropertyMetaData1_is_newer(md_local, md_remote)) {
+			ret = replmd_replicated_apply_search_for_parent(ar);
+		} else {
+			msg = ar->objs->objects[ar->index_current].msg;
+
+			/* Otherwise, just merge on the existing object, force no rename */
+			DEBUG(4,(__location__ ": Keeping object %s and rejecting older rename to %s\n",
+				 ldb_dn_get_linearized(ar->search_msg->dn),
+				 ldb_dn_get_linearized(msg->dn)));
+
+			/*
+			 * This assignment ensures that the strcmp()
+			 * in replmd_replicated_apply_merge() avoids
+			 * the rename call
+			 */
+			msg->dn = ar->search_msg->dn;
+			ret = replmd_replicated_apply_merge(ar);
 		}
 		if (ret != LDB_SUCCESS) {
 			return ldb_module_done(ar->req, NULL, NULL, ret);
 		}
 	}
+	}
 
 	talloc_free(ares);
 	return LDB_SUCCESS;
diff --git a/source4/torture/drs/python/replica_sync.py b/source4/torture/drs/python/replica_sync.py
index 8839421..63221a5 100644
--- a/source4/torture/drs/python/replica_sync.py
+++ b/source4/torture/drs/python/replica_sync.py
@@ -370,3 +370,107 @@ objectClass: organizationalUnit
         self._check_deleted(self.ldb_dc2, ou2)
         self._check_deleted(self.ldb_dc2, ou1_child)
         self._check_deleted(self.ldb_dc2, ou2_child)
+
+    def test_ReplRenames(self):
+        """Tests that objects created under a OU deleted eleswhere end up in lostAndFound"""
+        self._disable_inbound_repl(self.dnsname_dc1)
+        self._disable_inbound_repl(self.dnsname_dc2)
+
+        # Create two OUs on DC2
+        ou1 = self._create_ou(self.ldb_dc2, "OU=Original parent")
+        ou2 = self._create_ou(self.ldb_dc2, "OU=Original parent 2")
+
+        # replicate them from DC2 to DC1
+        self._enable_inbound_repl(self.dnsname_dc1)
+        self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True, full_sync=False)
+        self._disable_inbound_repl(self.dnsname_dc1)
+
+        # Create children on DC1
+        ou1_child = self._create_ou(self.ldb_dc1, "OU=Test Child,OU=Original parent")
+        ou2_child = self._create_ou(self.ldb_dc1, "OU=Test Child 2,OU=Original parent")
+        ou3_child = self._create_ou(self.ldb_dc1, "OU=Test Case Child,OU=Original parent")
+
+        # replicate them from DC1 to DC2
+        self._enable_inbound_repl(self.dnsname_dc2)
+        self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True, full_sync=False)
+        self._disable_inbound_repl(self.dnsname_dc2)
+
+        self.ldb_dc1.rename("<GUID=%s>" % ou2_child, "OU=Test Child 3,OU=Original parent 2,%s" % self.domain_dn)
+        self.ldb_dc1.rename("<GUID=%s>" % ou1_child, "OU=Test Child 2,OU=Original parent 2,%s" % self.domain_dn)
+        self.ldb_dc1.rename("<GUID=%s>" % ou2_child, "OU=Test Child,OU=Original parent 2,%s" % self.domain_dn)
+        self.ldb_dc1.rename("<GUID=%s>" % ou3_child, "OU=Test CASE Child,OU=Original parent,%s" % self.domain_dn)
+        self.ldb_dc2.rename("<GUID=%s>" % ou2, "OU=Original parent 3,%s" % self.domain_dn)
+        self.ldb_dc2.rename("<GUID=%s>" % ou1, "OU=Original parent 2,%s" % self.domain_dn)
+
+        # replicate them from DC1 to DC2
+        self._enable_inbound_repl(self.dnsname_dc2)
+        self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True, full_sync=False)
+        self._disable_inbound_repl(self.dnsname_dc2)
+
+        # Check the sub-OUs are now under Original Parent 3 (original
+        # parent 2 for Test CASE Child), and both have the right names
+
+        # Check that DC2 got the DC1 object, and the renames are all correct
+        res1 = self.ldb_dc2.search(base="<GUID=%s>" % ou1_child,
+                                  scope=SCOPE_BASE, attrs=["name"])
+        res2 = self.ldb_dc2.search(base="<GUID=%s>" % ou2_child,
+                                  scope=SCOPE_BASE, attrs=["name"])
+        res3 = self.ldb_dc2.search(base="<GUID=%s>" % ou3_child,
+                                  scope=SCOPE_BASE, attrs=["name"])
+        print res1[0].dn
+        print res2[0].dn
+        print res3[0].dn
+        self.assertEqual('Test Child 2', res1[0]["name"][0])
+        self.assertEqual('Test Child', res2[0]["name"][0])
+        self.assertEqual('Test CASE Child', res3[0]["name"][0])
+        self.assertEqual(str(res1[0].dn), "OU=Test Child 2,OU=Original parent 3,%s" % self.domain_dn)
+        self.assertEqual(str(res2[0].dn), "OU=Test Child,OU=Original parent 3,%s" % self.domain_dn)
+        self.assertEqual(str(res3[0].dn), "OU=Test CASE Child,OU=Original parent 2,%s" % self.domain_dn)
+
+        # replicate them from DC2 to DC1
+        self._enable_inbound_repl(self.dnsname_dc1)
+        self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True, full_sync=False)
+        self._disable_inbound_repl(self.dnsname_dc1)
+
+        # Check that DC1 got the DC2 object, and the renames are all correct
+        res1 = self.ldb_dc1.search(base="<GUID=%s>" % ou1_child,
+                                  scope=SCOPE_BASE, attrs=["name"])
+        res2 = self.ldb_dc1.search(base="<GUID=%s>" % ou2_child,
+                                  scope=SCOPE_BASE, attrs=["name"])
+        res3 = self.ldb_dc1.search(base="<GUID=%s>" % ou3_child,
+                                  scope=SCOPE_BASE, attrs=["name"])
+        print res1[0].dn
+        print res2[0].dn
+        print res3[0].dn
+        self.assertEqual('Test Child 2', res1[0]["name"][0])
+        self.assertEqual('Test Child', res2[0]["name"][0])
+        self.assertEqual('Test CASE Child', res3[0]["name"][0])
+        self.assertEqual(str(res1[0].dn), "OU=Test Child 2,OU=Original parent 3,%s" % self.domain_dn)
+        self.assertEqual(str(res2[0].dn), "OU=Test Child,OU=Original parent 3,%s" % self.domain_dn)
+        self.assertEqual(str(res3[0].dn), "OU=Test CASE Child,OU=Original parent 2,%s" % self.domain_dn)
+
+        # Delete all objects by GUID on DC1
+
+        self.ldb_dc1.delete('<GUID=%s>' % ou1_child)
+        self.ldb_dc1.delete('<GUID=%s>' % ou2_child)
+        self.ldb_dc1.delete('<GUID=%s>' % ou3_child)
+        self.ldb_dc1.delete('<GUID=%s>' % ou1)
+        self.ldb_dc1.delete('<GUID=%s>' % ou2)
+
+        self._enable_inbound_repl(self.dnsname_dc1)
+        self._enable_inbound_repl(self.dnsname_dc2)
+        self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True, full_sync=False)
+
+
+        # Check all deleted on DC1
+        self._check_deleted(self.ldb_dc1, ou1)
+        self._check_deleted(self.ldb_dc1, ou2)
+        self._check_deleted(self.ldb_dc1, ou1_child)
+        self._check_deleted(self.ldb_dc1, ou2_child)
+        self._check_deleted(self.ldb_dc1, ou3_child)
+        # Check all deleted on DC2
+        self._check_deleted(self.ldb_dc2, ou1)
+        self._check_deleted(self.ldb_dc2, ou2)
+        self._check_deleted(self.ldb_dc2, ou1_child)
+        self._check_deleted(self.ldb_dc2, ou2_child)
+        self._check_deleted(self.ldb_dc2, ou3_child)


-- 
Samba Shared Repository


More information about the samba-cvs mailing list