Blame SOURCES/0039-Ticket-47931-memberOf-retrocl-deadlocks.patch

a2f18f
From 9ba3240a177c156e365f22c721432321bb0a679e Mon Sep 17 00:00:00 2001
b161c9
From: Mark Reynolds <mreynolds@redhat.com>
b161c9
Date: Tue, 4 Aug 2015 12:19:31 -0400
a2f18f
Subject: [PATCH 39/39] Ticket 47931 - memberOf & retrocl deadlocks
b161c9
b161c9
Bug Description:  When concurrently updating multiple backends the
b161c9
                  memberOf and retrocl plugins can deadlock on each
b161c9
                  other.  This is caused by the required retrocl lock,
b161c9
                  and the db lock on the changenumber index in the
b161c9
                  retrocl db.
b161c9
b161c9
Fix Description:  Added scoping to the retrocl that allows subtrees/suffixes
b161c9
                  to be included or excluded.  Also moved the existing
b161c9
                  memberOf scoping outside of its global lock.
b161c9
b161c9
                  Also improved the memberOf config copying to be consistent
b161c9
                  and more efficient.  Improved the memberOf scoping attributes
b161c9
                  to be multivalued.  And, properly valdiated new config
b161c9
                  settings in the preop valdiation function, instead of the
b161c9
                  "apply config" function.
b161c9
b161c9
https://fedorahosted.org/389/ticket/47931
b161c9
b161c9
Valgrind: passed
b161c9
b161c9
Reviewed by: nhosoi(Thanks!)
b161c9
b161c9
(cherry picked from commit fd959ac864d6d86d24928bc2c6f097d1a6031ecd)
b161c9
(cherry picked from commit d8108476d3bedbcc03f6c61bfb3d50e921faaf42)
b161c9
---
a2f18f
 ldap/servers/plugins/memberof/memberof.c        | 217 +++++++++++++--------
b161c9
 ldap/servers/plugins/memberof/memberof.h        |   8 +-
a2f18f
 ldap/servers/plugins/memberof/memberof_config.c | 249 +++++++++++++++++-------
b161c9
 ldap/servers/plugins/retrocl/retrocl.c          | 183 +++++++++++++++--
b161c9
 ldap/servers/plugins/retrocl/retrocl.h          |   4 +
b161c9
 ldap/servers/plugins/retrocl/retrocl_po.c       |  41 +++-
a2f18f
 6 files changed, 516 insertions(+), 186 deletions(-)
b161c9
b161c9
diff --git a/ldap/servers/plugins/memberof/memberof.c b/ldap/servers/plugins/memberof/memberof.c
a2f18f
index da52bc8..9b577b9 100644
b161c9
--- a/ldap/servers/plugins/memberof/memberof.c
b161c9
+++ b/ldap/servers/plugins/memberof/memberof.c
a2f18f
@@ -116,7 +116,7 @@ static int memberof_compare(MemberOfConfig *config, const void *a, const void *b
b161c9
 static int memberof_qsort_compare(const void *a, const void *b);
b161c9
 static void memberof_load_array(Slapi_Value **array, Slapi_Attr *attr);
b161c9
 static int memberof_del_dn_from_groups(Slapi_PBlock *pb, MemberOfConfig *config, Slapi_DN *sdn);
b161c9
-static int memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_DN *sdn,
b161c9
+static int memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_DN *sdn, MemberOfConfig *config,
b161c9
 	char **types, plugin_search_entry_callback callback,  void *callback_data);
b161c9
 static int memberof_is_direct_member(MemberOfConfig *config, Slapi_Value *groupdn,
b161c9
 	Slapi_Value *memberdn);
a2f18f
@@ -144,7 +144,7 @@ static const char *fetch_attr(Slapi_Entry *e, const char *attrname,
b161c9
 static void memberof_fixup_task_thread(void *arg);
b161c9
 static int memberof_fix_memberof(MemberOfConfig *config, char *dn, char *filter_str);
b161c9
 static int memberof_fix_memberof_callback(Slapi_Entry *e, void *callback_data);
b161c9
-
b161c9
+static int memberof_entry_in_scope(MemberOfConfig *config, Slapi_DN *sdn);
b161c9
 
b161c9
 /*** implementation ***/
b161c9
 
a2f18f
@@ -489,7 +489,8 @@ memberof_get_plugin_area()
b161c9
 int memberof_postop_del(Slapi_PBlock *pb)
b161c9
 {
b161c9
 	int ret = SLAPI_PLUGIN_SUCCESS;
b161c9
-	MemberOfConfig configCopy = {0, 0, 0, 0};
b161c9
+	MemberOfConfig *mainConfig = NULL;
b161c9
+	MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
b161c9
 	Slapi_DN *sdn;
b161c9
 	void *caller_id = NULL;
b161c9
 
a2f18f
@@ -509,12 +510,13 @@ int memberof_postop_del(Slapi_PBlock *pb)
b161c9
 		struct slapi_entry *e = NULL;
b161c9
 
b161c9
 		slapi_pblock_get( pb, SLAPI_ENTRY_PRE_OP, &e );
b161c9
-
b161c9
-		/* We need to get the config lock first.  Trying to get the
b161c9
-		 * config lock after we already hold the op lock can cause
b161c9
-		 * a deadlock. */
b161c9
 		memberof_rlock_config();
b161c9
-		/* copy config so it doesn't change out from under us */
b161c9
+		mainConfig = memberof_get_config();
b161c9
+		if(!memberof_entry_in_scope(mainConfig, slapi_entry_get_sdn(e))){
b161c9
+			/* The entry is not in scope, bail...*/
b161c9
+			memberof_unlock_config();
b161c9
+			goto bail;
b161c9
+		}
b161c9
 		memberof_copy_config(&configCopy, memberof_get_config());
b161c9
 		memberof_unlock_config();
b161c9
 
a2f18f
@@ -529,7 +531,6 @@ int memberof_postop_del(Slapi_PBlock *pb)
b161c9
 			                "memberof_postop_del: error deleting dn (%s) from group. Error (%d)\n",
b161c9
 			                slapi_sdn_get_dn(sdn),ret);
b161c9
 			memberof_unlock();
b161c9
-			memberof_free_config(&configCopy);
b161c9
 			goto bail;
b161c9
 		}
b161c9
 
a2f18f
@@ -554,10 +555,10 @@ int memberof_postop_del(Slapi_PBlock *pb)
b161c9
 			}
b161c9
 		}
b161c9
 		memberof_unlock();
b161c9
+bail:
b161c9
 		memberof_free_config(&configCopy);
b161c9
 	}
b161c9
 
b161c9
-bail:
b161c9
 	if(ret){
b161c9
 		slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ret;;
b161c9
 		ret = SLAPI_PLUGIN_FAILURE;
a2f18f
@@ -591,7 +592,7 @@ memberof_del_dn_from_groups(Slapi_PBlock *pb, MemberOfConfig *config, Slapi_DN *
b161c9
 
b161c9
 		groupattrs[0] = config->groupattrs[i];
b161c9
 
b161c9
-		rc = memberof_call_foreach_dn(pb, sdn, groupattrs,
b161c9
+		rc = memberof_call_foreach_dn(pb, sdn, config, groupattrs,
b161c9
 		                              memberof_del_dn_type_callback, &data);
b161c9
 	}
b161c9
 
a2f18f
@@ -641,6 +642,20 @@ memberof_del_dn_type_callback(Slapi_Entry *e, void *callback_data)
b161c9
 	return rc;
b161c9
 }
b161c9
 
b161c9
+/* Check if the the entry include scope is a child of the sdn */
b161c9
+static Slapi_DN*
b161c9
+memberof_scope_is_child_of_dn(MemberOfConfig *config, Slapi_DN *sdn)
b161c9
+{
b161c9
+	int i = 0;
b161c9
+
b161c9
+	while(config->entryScopes && config->entryScopes[i]){
b161c9
+		if(slapi_sdn_issuffix(config->entryScopes[i], sdn)){
b161c9
+			return config->entryScopes[i];
b161c9
+		}
b161c9
+		i++;
b161c9
+	}
b161c9
+	return NULL;
b161c9
+}
b161c9
 /*
b161c9
  * Does a callback search of "type=dn" under the db suffix that "dn" is in,
b161c9
  * unless all_backends is set, then we look at all the backends.  If "dn"
a2f18f
@@ -649,7 +664,7 @@ memberof_del_dn_type_callback(Slapi_Entry *e, void *callback_data)
b161c9
  */
b161c9
 int
b161c9
 memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_DN *sdn,
b161c9
-	char **types, plugin_search_entry_callback callback, void *callback_data)
b161c9
+	MemberOfConfig *config, char **types, plugin_search_entry_callback callback, void *callback_data)
b161c9
 {
b161c9
 	Slapi_PBlock *search_pb = NULL;
b161c9
 	Slapi_DN *base_sdn = NULL;
a2f18f
@@ -657,9 +672,7 @@ memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_DN *sdn,
b161c9
 	char *escaped_filter_val;
b161c9
 	char *filter_str = NULL;
b161c9
 	char *cookie = NULL;
b161c9
-	int all_backends = memberof_config_get_all_backends();
b161c9
-	Slapi_DN *entry_scope = memberof_config_get_entry_scope();
a2f18f
-	Slapi_DN *entry_scope_exclude_subtree = memberof_config_get_entry_scope_exclude_subtree();
b161c9
+	int all_backends = config->allBackends;
b161c9
 	int types_name_len = 0;
b161c9
 	int num_types = 0;
b161c9
 	int dn_len = slapi_sdn_get_ndn_len(sdn);
a2f18f
@@ -667,11 +680,7 @@ memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_DN *sdn,
b161c9
 	int rc = 0;
b161c9
 	int i = 0;
b161c9
 
b161c9
-	if (entry_scope && !slapi_sdn_issuffix(sdn, entry_scope)) {
b161c9
-		return (rc);
b161c9
-	}
b161c9
-        
a2f18f
-	if (entry_scope_exclude_subtree && slapi_sdn_issuffix(sdn, entry_scope_exclude_subtree)) {
b161c9
+	if (!memberof_entry_in_scope(config, sdn)) {
b161c9
 		return (rc);
b161c9
 	}
b161c9
 
a2f18f
@@ -728,6 +737,8 @@ memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_DN *sdn,
b161c9
 	search_pb = slapi_pblock_new();
b161c9
 	be = slapi_get_first_backend(&cookie);
b161c9
 	while(be){
b161c9
+		Slapi_DN *scope_sdn = NULL;
b161c9
+
b161c9
 		if(!all_backends){
b161c9
 			be = slapi_be_select(sdn);
b161c9
 			if(be == NULL){
a2f18f
@@ -743,13 +754,14 @@ memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_DN *sdn,
b161c9
 				continue;
b161c9
 			}
b161c9
 		}
b161c9
-		if (entry_scope) {
b161c9
-			if (slapi_sdn_issuffix(base_sdn, entry_scope)) {
b161c9
+
b161c9
+		if (config->entryScopes || config->entryScopeExcludeSubtrees) {
b161c9
+			if (memberof_entry_in_scope(config, base_sdn)) {
b161c9
 				/* do nothing, entry scope is spanning 
b161c9
 				 * multiple suffixes, start at suffix */
b161c9
-			} else if (slapi_sdn_issuffix(entry_scope, base_sdn)) {
b161c9
+			} else if ((scope_sdn = memberof_scope_is_child_of_dn(config, base_sdn))) {
b161c9
 				/* scope is below suffix, set search base */
b161c9
-				base_sdn = entry_scope;
b161c9
+				base_sdn = scope_sdn;
b161c9
 			} else if(!all_backends){
b161c9
 				break;
b161c9
 			} else {
a2f18f
@@ -767,7 +779,6 @@ memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_DN *sdn,
b161c9
 			break;
b161c9
 		}
b161c9
 
b161c9
-
b161c9
 		if(!all_backends){
b161c9
 			break;
b161c9
 		}
a2f18f
@@ -792,10 +803,7 @@ int memberof_postop_modrdn(Slapi_PBlock *pb)
b161c9
 {
b161c9
 	int ret = SLAPI_PLUGIN_SUCCESS;
b161c9
 	void *caller_id = NULL;
b161c9
-	Slapi_DN *entry_scope = NULL;
b161c9
-        Slapi_DN *entry_scope_exclude_subtree = memberof_config_get_entry_scope_exclude_subtree();
b161c9
 
b161c9
-	entry_scope = memberof_config_get_entry_scope();
b161c9
 	slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM,
b161c9
 		     "--> memberof_postop_modrdn\n" );
b161c9
 
a2f18f
@@ -810,7 +818,7 @@ int memberof_postop_modrdn(Slapi_PBlock *pb)
b161c9
 	if(memberof_oktodo(pb))
b161c9
 	{
b161c9
 		MemberOfConfig *mainConfig = 0;
b161c9
-		MemberOfConfig configCopy = {0, 0, 0, 0};
b161c9
+		MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
b161c9
 		struct slapi_entry *pre_e = NULL;
b161c9
 		struct slapi_entry *post_e = NULL;
b161c9
 		Slapi_DN *pre_sdn = 0;
a2f18f
@@ -818,7 +826,6 @@ int memberof_postop_modrdn(Slapi_PBlock *pb)
b161c9
 
b161c9
 		slapi_pblock_get( pb, SLAPI_ENTRY_PRE_OP, &pre_e );
b161c9
 		slapi_pblock_get( pb, SLAPI_ENTRY_POST_OP, &post_e );
b161c9
-		
b161c9
 		if(pre_e && post_e)
b161c9
 		{
b161c9
 			pre_sdn = slapi_entry_get_sdn(pre_e);
a2f18f
@@ -831,11 +838,19 @@ int memberof_postop_modrdn(Slapi_PBlock *pb)
b161c9
 		memberof_copy_config(&configCopy, mainConfig);
b161c9
 		memberof_unlock_config();
b161c9
 
b161c9
+		/* Need to check both the pre/post entries */
b161c9
+		if((pre_sdn && !memberof_entry_in_scope(&configCopy, pre_sdn)) &&
b161c9
+		   (post_sdn && !memberof_entry_in_scope(&configCopy, post_sdn)))
b161c9
+		{
b161c9
+			/* The entry is not in scope */
b161c9
+			goto bail;
b161c9
+		}
b161c9
+
b161c9
 		memberof_lock();
b161c9
 
b161c9
 		/*  update any downstream members */
b161c9
 		if(pre_sdn && post_sdn && configCopy.group_filter &&
b161c9
-			0 == slapi_filter_test_simple(post_e, configCopy.group_filter))
b161c9
+		   0 == slapi_filter_test_simple(post_e, configCopy.group_filter))
b161c9
 		{
b161c9
 			int i = 0;
b161c9
 			Slapi_Attr *attr = 0;
a2f18f
@@ -847,7 +862,7 @@ int memberof_postop_modrdn(Slapi_PBlock *pb)
b161c9
 				if(0 == slapi_entry_attr_find(post_e, configCopy.groupattrs[i], &attr))
b161c9
 				{
b161c9
 					if((ret = memberof_moddn_attr_list(pb, &configCopy, pre_sdn,
b161c9
-					                            post_sdn, attr) != 0))
b161c9
+					                                   post_sdn, attr) != 0))
b161c9
 					{
b161c9
 						slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
b161c9
 							"memberof_postop_modrdn - update failed for (%s), error (%d)\n",
a2f18f
@@ -862,49 +877,49 @@ int memberof_postop_modrdn(Slapi_PBlock *pb)
b161c9
 		 * of other group entries.  We need to update any member
b161c9
 		 * attributes to refer to the new name. */
b161c9
 		if (ret == LDAP_SUCCESS && pre_sdn && post_sdn) {
b161c9
-			if ((entry_scope && !slapi_sdn_issuffix(post_sdn, entry_scope)) || 
b161c9
-                            (entry_scope_exclude_subtree && slapi_sdn_issuffix(post_sdn, entry_scope_exclude_subtree))) {
b161c9
+			if (!memberof_entry_in_scope(&configCopy, post_sdn)){
b161c9
 				if((ret = memberof_del_dn_from_groups(pb, &configCopy, pre_sdn))){
b161c9
 					slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
b161c9
 						"memberof_postop_modrdn - delete dn failed for (%s), error (%d)\n",
b161c9
 						slapi_sdn_get_dn(pre_sdn), ret);
b161c9
 				}
b161c9
 				if(ret == LDAP_SUCCESS && pre_e && configCopy.group_filter &&
b161c9
-						0 == slapi_filter_test_simple(pre_e, configCopy.group_filter)) {
b161c9
+				   0 == slapi_filter_test_simple(pre_e, configCopy.group_filter))
b161c9
+				{
b161c9
 					/* is the entry of interest as a group? */
b161c9
-						int i = 0;
b161c9
-						Slapi_Attr *attr = 0;
b161c9
-
b161c9
-						/* Loop through to find each grouping attribute separately. */
b161c9
-						for (i = 0; configCopy.groupattrs[i] && ret == LDAP_SUCCESS; i++) {
b161c9
-							if (0 == slapi_entry_attr_find(pre_e, configCopy.groupattrs[i], &attr)) {
b161c9
-								if((ret = memberof_del_attr_list(pb, &configCopy, pre_sdn, attr))){
b161c9
-									slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
b161c9
-									"memberof_postop_modrdn: error deleting attr list - dn (%s). Error (%d)\n",
b161c9
-									slapi_sdn_get_dn(pre_sdn),ret);
b161c9
-								}
b161c9
+					int i = 0;
b161c9
+					Slapi_Attr *attr = 0;
b161c9
 
b161c9
+					/* Loop through to find each grouping attribute separately. */
b161c9
+					for (i = 0; configCopy.groupattrs[i] && ret == LDAP_SUCCESS; i++) {
b161c9
+						if (0 == slapi_entry_attr_find(pre_e, configCopy.groupattrs[i], &attr)) {
b161c9
+							if((ret = memberof_del_attr_list(pb, &configCopy, pre_sdn, attr))){
b161c9
+								slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
b161c9
+								"memberof_postop_modrdn: error deleting attr list - dn (%s). Error (%d)\n",
b161c9
+								slapi_sdn_get_dn(pre_sdn),ret);
b161c9
 							}
b161c9
+
b161c9
 						}
b161c9
-					} 
b161c9
+					}
b161c9
+				}
b161c9
 				if(ret == LDAP_SUCCESS) {
b161c9
-						memberof_del_dn_data del_data = {0, configCopy.memberof_attr};
b161c9
-						if((ret = memberof_del_dn_type_callback(post_e, &del_data))){
b161c9
-							slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
b161c9
-								"memberof_postop_modrdn - delete dn callback failed for (%s), error (%d)\n",
b161c9
-								slapi_entry_get_dn(post_e), ret);
b161c9
-						}
b161c9
+					memberof_del_dn_data del_data = {0, configCopy.memberof_attr};
b161c9
+					if((ret = memberof_del_dn_type_callback(post_e, &del_data))){
b161c9
+						slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
b161c9
+							"memberof_postop_modrdn - delete dn callback failed for (%s), error (%d)\n",
b161c9
+							slapi_entry_get_dn(post_e), ret);
b161c9
 					}
b161c9
+				}
b161c9
 			} else {
b161c9
 				if((ret = memberof_replace_dn_from_groups(pb, &configCopy, pre_sdn, post_sdn))){
b161c9
 					slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
b161c9
-						"memberof_postop_modrdn - replace dne failed for (%s), error (%d)\n",
b161c9
+						"memberof_postop_modrdn - replace dn failed for (%s), error (%d)\n",
b161c9
 						slapi_sdn_get_dn(pre_sdn), ret);
b161c9
 				}
b161c9
 			}
b161c9
 		}
b161c9
-
b161c9
 		memberof_unlock();
b161c9
+bail:
b161c9
 		memberof_free_config(&configCopy);
b161c9
 	}
b161c9
 
a2f18f
@@ -946,7 +961,7 @@ memberof_replace_dn_from_groups(Slapi_PBlock *pb, MemberOfConfig *config,
b161c9
 
b161c9
 		groupattrs[0] = config->groupattrs[i];
b161c9
 
b161c9
-		if((ret = memberof_call_foreach_dn(pb, pre_sdn, groupattrs,
b161c9
+		if((ret = memberof_call_foreach_dn(pb, pre_sdn, config, groupattrs,
b161c9
 		                                   memberof_replace_dn_type_callback,
b161c9
 		                                   &data)))
b161c9
 		{
a2f18f
@@ -1064,12 +1079,11 @@ int memberof_postop_modify(Slapi_PBlock *pb)
b161c9
 		goto done;
b161c9
 	}
b161c9
 
b161c9
-
b161c9
-	if(memberof_oktodo(pb) && (sdn = memberof_getsdn(pb)))
b161c9
+	if(memberof_oktodo(pb))
b161c9
 	{
b161c9
 		int config_copied = 0;
b161c9
 		MemberOfConfig *mainConfig = 0;
b161c9
-		MemberOfConfig configCopy = {0, 0, 0, 0};
b161c9
+		MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
b161c9
 
b161c9
 		/* get the mod set */
b161c9
 		slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods;;
a2f18f
@@ -1088,19 +1102,22 @@ int memberof_postop_modify(Slapi_PBlock *pb)
b161c9
 			 * only copy the config the first time it's needed so
b161c9
 			 * it remains the same for all mods in the operation,
b161c9
 			 * despite any config changes that may be made. */
b161c9
-			if (!config_copied)
b161c9
-			{
b161c9
+			if (!config_copied){
b161c9
 				memberof_rlock_config();
b161c9
 				mainConfig = memberof_get_config();
b161c9
 
b161c9
 				if (memberof_is_grouping_attr(type, mainConfig))
b161c9
 				{
b161c9
 					interested = 1;
b161c9
+					if (!memberof_entry_in_scope(mainConfig, sdn)){
b161c9
+						/* Entry is not in scope */
b161c9
+						memberof_unlock_config();
b161c9
+						goto bail;
b161c9
+					}
b161c9
 					/* copy config so it doesn't change out from under us */
b161c9
 					memberof_copy_config(&configCopy, mainConfig);
b161c9
 					config_copied = 1;
b161c9
 				}
b161c9
-
b161c9
 				memberof_unlock_config();
b161c9
 			} else {
b161c9
 				if (memberof_is_grouping_attr(type, &configCopy))
a2f18f
@@ -1197,8 +1214,7 @@ int memberof_postop_modify(Slapi_PBlock *pb)
b161c9
 		}
b161c9
 
b161c9
 bail:
b161c9
-		if (config_copied)
b161c9
-		{
b161c9
+		if (config_copied){
b161c9
 			memberof_free_config(&configCopy);
b161c9
 		}
b161c9
 
a2f18f
@@ -1244,22 +1260,25 @@ int memberof_postop_add(Slapi_PBlock *pb)
b161c9
 
b161c9
 	if(memberof_oktodo(pb) && (sdn = memberof_getsdn(pb)))
b161c9
 	{
b161c9
-		MemberOfConfig *mainConfig = 0;
b161c9
-		MemberOfConfig configCopy = {0, 0, 0, 0};
b161c9
 		struct slapi_entry *e = NULL;
b161c9
-
b161c9
+		MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
b161c9
+		MemberOfConfig *mainConfig;
b161c9
 		slapi_pblock_get( pb, SLAPI_ENTRY_POST_OP, &e );
b161c9
-		
b161c9
 
b161c9
 		/* is the entry of interest? */
b161c9
 		memberof_rlock_config();
b161c9
 		mainConfig = memberof_get_config();
b161c9
 		if(e && mainConfig && mainConfig->group_filter &&
b161c9
 		   0 == slapi_filter_test_simple(e, mainConfig->group_filter))
b161c9
+
b161c9
 		{
b161c9
 			interested = 1;
b161c9
-			/* copy config so it doesn't change out from under us */
b161c9
-			memberof_copy_config(&configCopy, mainConfig);
b161c9
+			if(!memberof_entry_in_scope(mainConfig, slapi_entry_get_sdn(e))){
b161c9
+				/* Entry is not in scope */
b161c9
+				memberof_unlock_config();
b161c9
+				goto bail;
b161c9
+			}
b161c9
+			memberof_copy_config(&configCopy, memberof_get_config());
b161c9
 		}
b161c9
 		memberof_unlock_config();
b161c9
 
a2f18f
@@ -1284,11 +1303,11 @@ int memberof_postop_add(Slapi_PBlock *pb)
b161c9
 			}
b161c9
 
b161c9
 			memberof_unlock();
b161c9
-
b161c9
 			memberof_free_config(&configCopy);
b161c9
 		}
b161c9
 	}
b161c9
 
b161c9
+bail:
b161c9
 	if(ret){
b161c9
 		slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ret;;
b161c9
 		ret = SLAPI_PLUGIN_FAILURE;
a2f18f
@@ -1326,26 +1345,61 @@ int memberof_oktodo(Slapi_PBlock *pb)
b161c9
 	}
b161c9
 
b161c9
 	if(slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &oprc) != 0) 
b161c9
-        {
b161c9
+	{
b161c9
 		slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
b161c9
 			"memberof_postop_oktodo: could not get parameters\n" );
b161c9
 		ret = -1;
b161c9
 	}
b161c9
 
b161c9
-        /* this plugin should only execute if the operation succeeded
b161c9
-	*/
b161c9
-        if(oprc != 0)
b161c9
+	/* this plugin should only execute if the operation succeeded */
b161c9
+	if(oprc != 0)
b161c9
 	{
b161c9
 		ret = 0;
b161c9
 	}
b161c9
-	
b161c9
+
b161c9
+bail:
b161c9
 	slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM,
b161c9
 		     "<-- memberof_postop_oktodo\n" );
b161c9
 
b161c9
-bail:
b161c9
 	return ret;
b161c9
 }
b161c9
 
b161c9
+/*
b161c9
+ * Return 1 if the entry is in the scope.
b161c9
+ * For MODRDN the caller should check both the preop
b161c9
+ * and postop entries.  If we are moving out of, or
b161c9
+ * into scope, we should process it.
b161c9
+ */
b161c9
+static int
b161c9
+memberof_entry_in_scope(MemberOfConfig *config, Slapi_DN *sdn)
b161c9
+{
b161c9
+    if (config->entryScopeExcludeSubtrees){
b161c9
+        int i = 0;
b161c9
+
b161c9
+        /* check the excludes */
b161c9
+        while(config->entryScopeExcludeSubtrees[i]){
b161c9
+            if (slapi_sdn_issuffix(sdn, config->entryScopeExcludeSubtrees[i])){
b161c9
+                return 0;
b161c9
+            }
b161c9
+            i++;
b161c9
+        }
b161c9
+    }
b161c9
+    if (config->entryScopes){
b161c9
+        int i = 0;
b161c9
+
b161c9
+        /* check the excludes */
b161c9
+        while(config->entryScopes[i]){
b161c9
+            if (slapi_sdn_issuffix(sdn, config->entryScopes[i])){
b161c9
+                return 1;
b161c9
+            }
b161c9
+            i++;
b161c9
+        }
b161c9
+        return 0;
b161c9
+    }
b161c9
+
b161c9
+    return 1;
b161c9
+}
b161c9
+
b161c9
 static Slapi_DN *
b161c9
 memberof_getsdn(Slapi_PBlock *pb)
b161c9
 {
a2f18f
@@ -2013,7 +2067,7 @@ memberof_get_groups_r(MemberOfConfig *config, Slapi_DN *member_sdn,
b161c9
 {
b161c9
 	/* Search for any grouping attributes that point to memberdn.
b161c9
 	 * For each match, add it to the list, recurse and do same search */
b161c9
-	return memberof_call_foreach_dn(NULL, member_sdn, config->groupattrs,
b161c9
+	return memberof_call_foreach_dn(NULL, member_sdn, config, config->groupattrs,
b161c9
 		memberof_get_groups_callback, data);
b161c9
 }
b161c9
 
a2f18f
@@ -2030,7 +2084,6 @@ int memberof_get_groups_callback(Slapi_Entry *e, void *callback_data)
a2f18f
 	Slapi_Value *group_dn_val = 0;
b161c9
 	Slapi_ValueSet *groupvals = *((memberof_get_groups_data*)callback_data)->groupvals;
a2f18f
 	Slapi_ValueSet *group_norm_vals = *((memberof_get_groups_data*)callback_data)->group_norm_vals;
a2f18f
-	Slapi_DN *entry_scope_exclude_subtree = memberof_config_get_entry_scope_exclude_subtree();
a2f18f
 	MemberOfConfig *config = ((memberof_get_groups_data*)callback_data)->config;
b161c9
 	int rc = 0;
b161c9
 
a2f18f
@@ -2086,7 +2139,7 @@ int memberof_get_groups_callback(Slapi_Entry *e, void *callback_data)
b161c9
 	}
b161c9
 
a2f18f
 	/* if the group does not belong to an excluded subtree, adds it to the valueset */
a2f18f
-	if (!(entry_scope_exclude_subtree && slapi_sdn_issuffix(group_sdn, entry_scope_exclude_subtree))) {
b161c9
+	if (memberof_entry_in_scope(config, group_sdn)) {
a2f18f
 			/* Push group_dn_val into the valueset.  This memory is now owned
a2f18f
 			 * by the valueset. */
a2f18f
 			group_dn_val = slapi_value_new_string(group_dn);
a2f18f
@@ -2188,8 +2241,8 @@ memberof_test_membership(Slapi_PBlock *pb, MemberOfConfig *config,
b161c9
 {
b161c9
 	char *attrs[2] = {config->memberof_attr, 0};
b161c9
 
b161c9
-	return memberof_call_foreach_dn(pb, group_sdn, attrs, 
b161c9
-		memberof_test_membership_callback , config);
b161c9
+	return memberof_call_foreach_dn(pb, group_sdn, config, attrs,
b161c9
+		memberof_test_membership_callback, config);
b161c9
 }
b161c9
 
b161c9
 /*
b161c9
diff --git a/ldap/servers/plugins/memberof/memberof.h b/ldap/servers/plugins/memberof/memberof.h
a2f18f
index 5a70400..9d9d158 100644
b161c9
--- a/ldap/servers/plugins/memberof/memberof.h
b161c9
+++ b/ldap/servers/plugins/memberof/memberof.h
a2f18f
@@ -52,8 +52,10 @@ typedef struct memberofconfig {
b161c9
 	char **groupattrs;
b161c9
 	char *memberof_attr;
b161c9
 	int allBackends;
b161c9
-	Slapi_DN *entryScope;
b161c9
-	Slapi_DN *entryScopeExcludeSubtree;
b161c9
+	Slapi_DN **entryScopes;
b161c9
+	int entryScopeCount;
b161c9
+	Slapi_DN **entryScopeExcludeSubtrees;
b161c9
+	int entryExcludeScopeCount;
b161c9
 	Slapi_Filter *group_filter;
b161c9
 	Slapi_Attr **group_slapiattrs;
a2f18f
 	int skip_nested;
a2f18f
@@ -74,8 +76,6 @@ void memberof_rlock_config();
b161c9
 void memberof_wlock_config();
b161c9
 void memberof_unlock_config();
b161c9
 int memberof_config_get_all_backends();
b161c9
-Slapi_DN * memberof_config_get_entry_scope();
b161c9
-Slapi_DN * memberof_config_get_entry_scope_exclude_subtree();
b161c9
 void memberof_set_config_area(Slapi_DN *sdn);
b161c9
 Slapi_DN * memberof_get_config_area();
b161c9
 void memberof_set_plugin_area(Slapi_DN *sdn);
b161c9
diff --git a/ldap/servers/plugins/memberof/memberof_config.c b/ldap/servers/plugins/memberof/memberof_config.c
a2f18f
index ac2d045..b4cc941 100644
b161c9
--- a/ldap/servers/plugins/memberof/memberof_config.c
b161c9
+++ b/ldap/servers/plugins/memberof/memberof_config.c
a2f18f
@@ -48,7 +48,7 @@ static int memberof_search (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_En
b161c9
 /* This is the main configuration which is updated from dse.ldif.  The
b161c9
  * config will be copied when it is used by the plug-in to prevent it
b161c9
  * being changed out from under a running memberOf operation. */
b161c9
-static MemberOfConfig theConfig = {NULL, NULL,0, NULL, NULL, NULL, NULL};
b161c9
+static MemberOfConfig theConfig = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
b161c9
 static Slapi_RWLock *memberof_config_lock = 0;
b161c9
 static int inited = 0;
b161c9
 
a2f18f
@@ -60,6 +60,19 @@ static int dont_allow_that(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Ent
b161c9
 	return SLAPI_DSE_CALLBACK_ERROR;
b161c9
 }
b161c9
 
b161c9
+static void
b161c9
+memberof_free_scope(Slapi_DN **scopes, int *count)
b161c9
+{
b161c9
+	int i = 0;
b161c9
+
b161c9
+	while(scopes && scopes[i]){
b161c9
+		slapi_sdn_free(&scopes[i]);
b161c9
+		i++;
b161c9
+	}
b161c9
+	slapi_ch_free((void**)&scopes);
b161c9
+	*count = 0;
b161c9
+}
b161c9
+
b161c9
 /*
b161c9
  * memberof_config()
b161c9
  *
a2f18f
@@ -155,17 +168,22 @@ memberof_release_config()
b161c9
  *
b161c9
  * Validate the pending changes in the e entry.
b161c9
  */
b161c9
-static int 
b161c9
+int
b161c9
 memberof_validate_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, 
b161c9
 	int *returncode, char *returntext, void *arg)
b161c9
 {
b161c9
 	Slapi_Attr *memberof_attr = NULL;
b161c9
 	Slapi_Attr *group_attr = NULL;
b161c9
 	Slapi_DN *config_sdn = NULL;
b161c9
+	Slapi_DN **include_dn = NULL;
b161c9
+	Slapi_DN **exclude_dn = NULL;
b161c9
 	char *syntaxoid = NULL;
b161c9
 	char *config_dn = NULL;
a2f18f
 	char *skip_nested = NULL;
b161c9
+	char **entry_scopes = NULL;
b161c9
+	char **entry_exclude_scopes = NULL;
b161c9
 	int not_dn_syntax = 0;
b161c9
+	int num_vals = 0;
b161c9
 
b161c9
 	*returncode = LDAP_UNWILLING_TO_PERFORM; /* be pessimistic */
b161c9
 
a2f18f
@@ -283,8 +301,112 @@ memberof_validate_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entr
b161c9
 			*returncode = LDAP_UNWILLING_TO_PERFORM;
b161c9
 		}
b161c9
 	}
b161c9
+	/*
b161c9
+	 * Check the entry scopes
b161c9
+	 */
b161c9
+	entry_scopes = slapi_entry_attr_get_charray_ext(e, MEMBEROF_ENTRY_SCOPE_ATTR, &num_vals);
b161c9
+	if(entry_scopes){
b161c9
+		int i = 0;
b161c9
+
b161c9
+		/* Validate the syntax before we create our DN array */
b161c9
+		for (i = 0;i < num_vals; i++){
b161c9
+			if(slapi_dn_syntax_check(pb, entry_scopes[i], 1)){
b161c9
+				/* invalid dn syntax */
b161c9
+				PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
b161c9
+					"%s: Invalid DN (%s) for include suffix.",
b161c9
+					MEMBEROF_PLUGIN_SUBSYSTEM, entry_scopes[i]);
b161c9
+				slapi_ch_array_free(entry_scopes);
b161c9
+				theConfig.entryScopeCount = 0;
b161c9
+				*returncode = LDAP_UNWILLING_TO_PERFORM;
b161c9
+				goto done;
b161c9
+			}
b161c9
+		}
b161c9
+		/* Now create our SDN array for conflict checking */
b161c9
+		include_dn = (Slapi_DN **)slapi_ch_calloc(sizeof(Slapi_DN *), num_vals+1);
b161c9
+		for (i = 0;i < num_vals; i++){
b161c9
+			include_dn[i] = slapi_sdn_new_dn_passin(entry_scopes[i]);
b161c9
+		}
b161c9
+	}
b161c9
+	/*
b161c9
+	 * Check and process the entry exclude scopes
b161c9
+	 */
b161c9
+	entry_exclude_scopes =
b161c9
+		slapi_entry_attr_get_charray_ext(e, MEMBEROF_ENTRY_SCOPE_EXCLUDE_SUBTREE, &num_vals);
b161c9
+	if(entry_exclude_scopes){
b161c9
+		int i = 0;
b161c9
+
b161c9
+		/* Validate the syntax before we create our DN array */
b161c9
+		for (i = 0;i < num_vals; i++){
b161c9
+			if(slapi_dn_syntax_check(pb, entry_exclude_scopes[i], 1)){
b161c9
+				/* invalid dn syntax */
b161c9
+				PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
b161c9
+					"%s: Invalid DN (%s) for exclude suffix.",
b161c9
+					MEMBEROF_PLUGIN_SUBSYSTEM, entry_scopes[i]);
b161c9
+				slapi_ch_array_free(entry_exclude_scopes);
b161c9
+				*returncode = LDAP_UNWILLING_TO_PERFORM;
b161c9
+				goto done;
b161c9
+			}
b161c9
+		}
b161c9
+		/* Now create our SDN array for conflict checking */
b161c9
+		exclude_dn = (Slapi_DN **)slapi_ch_calloc(sizeof(Slapi_DN *),num_vals+1);
b161c9
+		for (i = 0;i < num_vals; i++){
b161c9
+			exclude_dn[i] = slapi_sdn_new_dn_passin(entry_exclude_scopes[i]);
b161c9
+		}
b161c9
+	}
b161c9
+	/*
b161c9
+	 * Need to do conflict checking
b161c9
+	 */
b161c9
+	if(include_dn && exclude_dn){
b161c9
+		/*
b161c9
+		 * Make sure we haven't mixed the same suffix, and there are no
b161c9
+		 * conflicts between the includes and excludes
b161c9
+		 */
b161c9
+		int i = 0;
b161c9
+
b161c9
+		while(include_dn[i]){
b161c9
+			int x = 0;
b161c9
+			while(exclude_dn[x]){
b161c9
+				if(slapi_sdn_compare(include_dn[i], exclude_dn[x] ) == 0)
b161c9
+				{
b161c9
+					/* we have a conflict */
b161c9
+					PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
b161c9
+						"%s: include suffix (%s) is also listed as an exclude suffix list",
b161c9
+						MEMBEROF_PLUGIN_SUBSYSTEM, slapi_sdn_get_dn(include_dn[i]));
b161c9
+					*returncode = LDAP_UNWILLING_TO_PERFORM;
b161c9
+					goto done;
b161c9
+			   }
b161c9
+			   x++;
b161c9
+			}
b161c9
+			i++;
b161c9
+		}
b161c9
+
b161c9
+		/* Check for parent/child conflicts */
b161c9
+		i = 0;
b161c9
+		while(include_dn[i]){
b161c9
+			int x = 0;
b161c9
+			while(exclude_dn[x]){
b161c9
+				if(slapi_sdn_issuffix(include_dn[i], exclude_dn[x]))
b161c9
+				{
b161c9
+					/* we have a conflict */
b161c9
+					PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
b161c9
+						"%s: include suffix (%s) is a child of the exclude suffix(%s)",
b161c9
+						MEMBEROF_PLUGIN_SUBSYSTEM,
b161c9
+						slapi_sdn_get_dn(include_dn[i]),
b161c9
+						slapi_sdn_get_dn(exclude_dn[i]));
b161c9
+					*returncode = LDAP_UNWILLING_TO_PERFORM;
b161c9
+					goto done;
b161c9
+			   }
b161c9
+			   x++;
b161c9
+			}
b161c9
+			i++;
b161c9
+		}
b161c9
+	}
b161c9
 
b161c9
 done:
b161c9
+	memberof_free_scope(exclude_dn, &num_vals);
b161c9
+	memberof_free_scope(include_dn,	&num_vals);
b161c9
+	slapi_ch_free((void**)&entry_scopes);
b161c9
+	slapi_ch_free((void**)&entry_exclude_scopes);
b161c9
 	slapi_sdn_free(&config_sdn);
b161c9
 	slapi_ch_free_string(&config_dn);
a2f18f
 	slapi_ch_free_string(&skip_nested);
a2f18f
@@ -299,7 +421,6 @@ done:
b161c9
 	}
b161c9
 }
b161c9
 
b161c9
-
b161c9
 /*
b161c9
  * memberof_apply_config()
b161c9
  *
a2f18f
@@ -318,10 +439,11 @@ memberof_apply_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry*
b161c9
 	int num_groupattrs = 0;
b161c9
 	int groupattr_name_len = 0;
b161c9
 	char *allBackends = NULL;
b161c9
-	char *entryScope = NULL;
a2f18f
-	char *entryScopeExcludeSubtree = NULL;
b161c9
+	char **entryScopes = NULL;
b161c9
+	char **entryScopeExcludeSubtrees = NULL;
b161c9
 	char *sharedcfg = NULL;
a2f18f
 	char *skip_nested = NULL;
b161c9
+	int num_vals = 0;
b161c9
 
b161c9
 	*returncode = LDAP_SUCCESS;
b161c9
 
a2f18f
@@ -353,8 +475,6 @@ memberof_apply_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry*
b161c9
 	groupattrs = slapi_entry_attr_get_charray(e, MEMBEROF_GROUP_ATTR);
b161c9
 	memberof_attr = slapi_entry_attr_get_charptr(e, MEMBEROF_ATTR);
b161c9
 	allBackends = slapi_entry_attr_get_charptr(e, MEMBEROF_BACKEND_ATTR);
b161c9
-	entryScope = slapi_entry_attr_get_charptr(e, MEMBEROF_ENTRY_SCOPE_ATTR);
a2f18f
-	entryScopeExcludeSubtree = slapi_entry_attr_get_charptr(e, MEMBEROF_ENTRY_SCOPE_EXCLUDE_SUBTREE);
a2f18f
 	skip_nested = slapi_entry_attr_get_charptr(e, MEMBEROF_SKIP_NESTED_ATTR);
b161c9
 
b161c9
 	/*
a2f18f
@@ -480,49 +600,39 @@ memberof_apply_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry*
b161c9
 		theConfig.allBackends = 0;
b161c9
 	}
b161c9
 
b161c9
-	slapi_sdn_free(&theConfig.entryScope);
b161c9
-	if (entryScope)
b161c9
-	{
b161c9
-        	if (slapi_dn_syntax_check(NULL, entryScope, 1) == 1) {
b161c9
-            		slapi_log_error(SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
b161c9
-                		"Error: Ignoring invalid DN used as plugin entry scope: [%s]\n",
b161c9
-                		entryScope);
b161c9
-			theConfig.entryScope = NULL;
b161c9
-			slapi_ch_free_string(&entryScope);
b161c9
-		} else {
b161c9
-			theConfig.entryScope = slapi_sdn_new_dn_passin(entryScope);
b161c9
+	/*
b161c9
+	 * Check and process the entry scopes
b161c9
+	 */
b161c9
+	memberof_free_scope(theConfig.entryScopes, &theConfig.entryScopeCount);
b161c9
+	entryScopes = slapi_entry_attr_get_charray_ext(e, MEMBEROF_ENTRY_SCOPE_ATTR, &num_vals);
b161c9
+	if(entryScopes){
b161c9
+		int i = 0;
b161c9
+
b161c9
+		/* Validation has already been performed in preop, just build the DN's */
b161c9
+		theConfig.entryScopes = (Slapi_DN **)slapi_ch_calloc(sizeof(Slapi_DN *), num_vals+1);
b161c9
+		for (i = 0;i < num_vals; i++){
b161c9
+			theConfig.entryScopes[i] = slapi_sdn_new_dn_passin(entryScopes[i]);
b161c9
 		}
b161c9
-	} else {
b161c9
-		theConfig.entryScope = NULL;
b161c9
+		theConfig.entryScopeCount = num_vals; /* shortcut for config copy */
b161c9
 	}
b161c9
-        
b161c9
-        slapi_sdn_free(&theConfig.entryScopeExcludeSubtree);
b161c9
-        if (entryScopeExcludeSubtree)
b161c9
-	{
b161c9
-        	if (theConfig.entryScope == NULL) {
b161c9
-                        slapi_log_error(SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
b161c9
-                		"Error: Ignoring ExcludeSubtree (%s) because entryScope is not define\n",
b161c9
-                		entryScopeExcludeSubtree);
b161c9
-			theConfig.entryScopeExcludeSubtree = NULL;
b161c9
-			slapi_ch_free_string(&entryScopeExcludeSubtree);
b161c9
-                } else if (slapi_dn_syntax_check(NULL, entryScopeExcludeSubtree, 1) == 1) {
b161c9
-            		slapi_log_error(SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
b161c9
-                		"Error: Ignoring invalid DN used as plugin entry exclude subtree: [%s]\n",
b161c9
-                		entryScopeExcludeSubtree);
b161c9
-			theConfig.entryScopeExcludeSubtree = NULL;
b161c9
-			slapi_ch_free_string(&entryScopeExcludeSubtree);
b161c9
-		} else {
b161c9
-			theConfig.entryScopeExcludeSubtree = slapi_sdn_new_dn_passin(entryScopeExcludeSubtree);
b161c9
+	/*
b161c9
+	 * Check and process the entry exclude scopes
b161c9
+	 */
b161c9
+	memberof_free_scope(theConfig.entryScopeExcludeSubtrees,
b161c9
+	                    &theConfig.entryExcludeScopeCount);
b161c9
+	entryScopeExcludeSubtrees =
b161c9
+	    slapi_entry_attr_get_charray_ext(e, MEMBEROF_ENTRY_SCOPE_EXCLUDE_SUBTREE, &num_vals);
b161c9
+	if(entryScopeExcludeSubtrees){
b161c9
+		int i = 0;
b161c9
+
b161c9
+		/* Validation has already been performed in preop, just build the DN's */
b161c9
+		theConfig.entryScopeExcludeSubtrees =
b161c9
+				(Slapi_DN **)slapi_ch_calloc(sizeof(Slapi_DN *),num_vals+1);
b161c9
+		for (i = 0;i < num_vals; i++){
b161c9
+			theConfig.entryScopeExcludeSubtrees[i] =
b161c9
+				slapi_sdn_new_dn_passin(entryScopeExcludeSubtrees[i]);
b161c9
 		}
b161c9
-	} else {
b161c9
-		theConfig.entryScopeExcludeSubtree = NULL;
b161c9
-	}
b161c9
-        if (theConfig.entryScopeExcludeSubtree && theConfig.entryScope && !slapi_sdn_issuffix(theConfig.entryScopeExcludeSubtree, theConfig.entryScope)) {
b161c9
-                slapi_log_error(SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
b161c9
-                        "Error: Ignoring ExcludeSubtree (%s) that is out of the scope (%s)\n",
b161c9
-                        slapi_sdn_get_dn(theConfig.entryScopeExcludeSubtree),
b161c9
-                        slapi_sdn_get_dn(theConfig.entryScope));
b161c9
-                slapi_sdn_free(&theConfig.entryScopeExcludeSubtree);
b161c9
+		theConfig.entryExcludeScopeCount = num_vals; /* shortcut for config copy */
b161c9
 	}
b161c9
 
b161c9
 	/* release the lock */
a2f18f
@@ -536,6 +646,8 @@ done:
b161c9
 	slapi_ch_free_string(&memberof_attr);
b161c9
 	slapi_ch_free_string(&allBackends);
a2f18f
 	slapi_ch_free_string(&skip_nested);
b161c9
+	slapi_ch_free((void **)&entryScopes);
b161c9
+	slapi_ch_free((void **)&entryScopeExcludeSubtrees);
b161c9
 
b161c9
 	if (*returncode != LDAP_SUCCESS)
b161c9
 	{
a2f18f
@@ -616,6 +728,23 @@ memberof_copy_config(MemberOfConfig *dest, MemberOfConfig *src)
b161c9
 		{
b161c9
 			dest->allBackends = src->allBackends;
b161c9
 		}
b161c9
+
b161c9
+		if(src->entryScopes){
b161c9
+			int num_vals = 0;
b161c9
+
b161c9
+			dest->entryScopes = (Slapi_DN **)slapi_ch_calloc(sizeof(Slapi_DN *),src->entryScopeCount+1);
b161c9
+			for(num_vals = 0; src->entryScopes[num_vals]; num_vals++){
b161c9
+				dest->entryScopes[num_vals] = slapi_sdn_dup(src->entryScopes[num_vals]);
b161c9
+			}
b161c9
+		}
b161c9
+		if(src->entryScopeExcludeSubtrees){
b161c9
+			int num_vals = 0;
b161c9
+
b161c9
+			dest->entryScopeExcludeSubtrees = (Slapi_DN **)slapi_ch_calloc(sizeof(Slapi_DN *),src->entryExcludeScopeCount+1);
b161c9
+			for(num_vals = 0; src->entryScopes[num_vals]; num_vals++){
b161c9
+				dest->entryScopeExcludeSubtrees[num_vals] = slapi_sdn_dup(src->entryScopeExcludeSubtrees[num_vals]);
b161c9
+			}
b161c9
+		}
b161c9
 	}
b161c9
 }
b161c9
 
a2f18f
@@ -641,6 +770,8 @@ memberof_free_config(MemberOfConfig *config)
b161c9
 		slapi_ch_free((void **)&config->group_slapiattrs);
b161c9
 
b161c9
 		slapi_ch_free_string(&config->memberof_attr);
b161c9
+		memberof_free_scope(config->entryScopes, &config->entryScopeCount);
b161c9
+		memberof_free_scope(config->entryScopeExcludeSubtrees, &config->entryExcludeScopeCount);
b161c9
 	}
b161c9
 }
b161c9
 
a2f18f
@@ -706,30 +837,6 @@ memberof_config_get_all_backends()
b161c9
 	return all_backends;
b161c9
 }
b161c9
 
b161c9
-Slapi_DN *
b161c9
-memberof_config_get_entry_scope()
b161c9
-{
b161c9
-	Slapi_DN *entry_scope;
b161c9
-
b161c9
-	slapi_rwlock_rdlock(memberof_config_lock);
b161c9
-	entry_scope = theConfig.entryScope;
b161c9
-	slapi_rwlock_unlock(memberof_config_lock);
b161c9
-
b161c9
-	return entry_scope;
b161c9
-}
b161c9
-
b161c9
-Slapi_DN *
b161c9
-memberof_config_get_entry_scope_exclude_subtree()
b161c9
-{
b161c9
-	Slapi_DN *entry_exclude_subtree;
b161c9
-
b161c9
-	slapi_rwlock_rdlock(memberof_config_lock);
b161c9
-	entry_exclude_subtree = theConfig.entryScopeExcludeSubtree;
b161c9
-	slapi_rwlock_unlock(memberof_config_lock);
b161c9
-
b161c9
-	return entry_exclude_subtree;
b161c9
-}
b161c9
-
b161c9
 /*
b161c9
  * Check if we are modifying the config, or changing the shared config entry
b161c9
  */
b161c9
diff --git a/ldap/servers/plugins/retrocl/retrocl.c b/ldap/servers/plugins/retrocl/retrocl.c
a2f18f
index 78a0c6d..4bcbb38 100644
b161c9
--- a/ldap/servers/plugins/retrocl/retrocl.c
b161c9
+++ b/ldap/servers/plugins/retrocl/retrocl.c
a2f18f
@@ -45,6 +45,9 @@ char **retrocl_attributes = NULL;
b161c9
 char **retrocl_aliases = NULL;
b161c9
 int retrocl_log_deleted = 0;
b161c9
 
b161c9
+static Slapi_DN **retrocl_includes = NULL;
b161c9
+static Slapi_DN **retrocl_excludes = NULL;
b161c9
+
b161c9
 /* ----------------------------- Retrocl Plugin */
b161c9
 
b161c9
 static Slapi_PluginDesc retrocldesc = {"retrocl", VENDOR, DS_PACKAGE_VERSION, "Retrocl Plugin"};
a2f18f
@@ -349,6 +352,8 @@ static int retrocl_start (Slapi_PBlock *pb)
b161c9
     int rc = 0;
b161c9
     Slapi_Entry *e = NULL;
b161c9
     char **values = NULL;
b161c9
+    int num_vals = 0;
b161c9
+    int i = 0;
b161c9
 
b161c9
     retrocl_rootdse_init(pb);
b161c9
 
a2f18f
@@ -369,6 +374,87 @@ static int retrocl_start (Slapi_PBlock *pb)
b161c9
         return -1;
b161c9
     }
b161c9
 
b161c9
+    /* Get the exclude suffixes */
b161c9
+    values = slapi_entry_attr_get_charray_ext(e, CONFIG_CHANGELOG_EXCLUDE_SUFFIX, &num_vals);
b161c9
+    if(values){
b161c9
+        /* Validate the syntax before we create our DN array */
b161c9
+        for (i = 0;i < num_vals; i++){
b161c9
+            if(slapi_dn_syntax_check(pb, values[i], 1)){
b161c9
+                /* invalid dn syntax */
b161c9
+                slapi_log_error(SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
b161c9
+                        "Invalid DN (%s) for exclude suffix.\n", values[i] );
b161c9
+                slapi_ch_array_free(values);
b161c9
+                return -1;
b161c9
+            }
b161c9
+        }
b161c9
+        /* Now create our SDN array */
b161c9
+        retrocl_excludes = (Slapi_DN **)slapi_ch_calloc(sizeof(Slapi_DN *),num_vals+1);
b161c9
+        for (i = 0;i < num_vals; i++){
b161c9
+            retrocl_excludes[i] = slapi_sdn_new_dn_byval(values[i]);
b161c9
+        }
b161c9
+        slapi_ch_array_free(values);
b161c9
+    }
b161c9
+    /* Get the include suffixes */
b161c9
+    values = slapi_entry_attr_get_charray_ext(e, CONFIG_CHANGELOG_INCLUDE_SUFFIX, &num_vals);
b161c9
+    if(values){
b161c9
+        for (i = 0;i < num_vals; i++){
b161c9
+            /* Validate the syntax before we create our DN array */
b161c9
+            if(slapi_dn_syntax_check(pb, values[i], 1)){
b161c9
+                /* invalid dn syntax */
b161c9
+                slapi_log_error(SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
b161c9
+                        "Invalid DN (%s) for include suffix.\n", values[i] );
b161c9
+                slapi_ch_array_free(values);
b161c9
+                return -1;
b161c9
+            }
b161c9
+        }
b161c9
+        /* Now create our SDN array */
b161c9
+        retrocl_includes = (Slapi_DN **)slapi_ch_calloc(sizeof(Slapi_DN *),num_vals+1);
b161c9
+        for (i = 0;i < num_vals; i++){
b161c9
+            retrocl_includes[i] = slapi_sdn_new_dn_byval(values[i]);
b161c9
+        }
b161c9
+        slapi_ch_array_free(values);
b161c9
+    }
b161c9
+    if(retrocl_includes && retrocl_excludes){
b161c9
+        /*
b161c9
+         * Make sure we haven't mixed the same suffix, and there are no
b161c9
+         * conflicts between the includes and excludes
b161c9
+         */
b161c9
+        int i = 0;
b161c9
+
b161c9
+        while(retrocl_includes[i]){
b161c9
+            int x = 0;
b161c9
+            while(retrocl_excludes[x]){
b161c9
+               if(slapi_sdn_compare(retrocl_includes[i], retrocl_excludes[x] ) == 0){
b161c9
+                   /* we have a conflict */
b161c9
+                   slapi_log_error(SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
b161c9
+                           "include suffix (%s) is also listed in exclude suffix list\n",
b161c9
+		                   slapi_sdn_get_dn(retrocl_includes[i]));
b161c9
+                   return -1;
b161c9
+               }
b161c9
+               x++;
b161c9
+            }
b161c9
+            i++;
b161c9
+        }
b161c9
+
b161c9
+        /* Check for parent/child conflicts */
b161c9
+        i = 0;
b161c9
+        while(retrocl_includes[i]){
b161c9
+            int x = 0;
b161c9
+            while(retrocl_excludes[x]){
b161c9
+               if(slapi_sdn_issuffix(retrocl_includes[i], retrocl_excludes[x])){
b161c9
+                   /* we have a conflict */
b161c9
+                   slapi_log_error(SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
b161c9
+                           "include suffix (%s) is a child of the exclude suffix(%s)\n",
b161c9
+                           slapi_sdn_get_dn(retrocl_includes[i]),
b161c9
+                           slapi_sdn_get_dn(retrocl_excludes[i]));
b161c9
+                   return -1;
b161c9
+               }
b161c9
+               x++;
b161c9
+            }
b161c9
+            i++;
b161c9
+        }
b161c9
+    }
b161c9
+
b161c9
     values = slapi_entry_attr_get_charray(e, "nsslapd-attribute");
b161c9
     if (values != NULL) {
b161c9
         int n = 0;
a2f18f
@@ -434,6 +520,49 @@ static int retrocl_start (Slapi_PBlock *pb)
b161c9
 }
b161c9
 
b161c9
 /*
b161c9
+ * Check if an entry is in the configured scope.
b161c9
+ * Return 1 if entry is in the scope, or 0 otherwise.
b161c9
+ * For MODRDN the caller should check both the preop
b161c9
+ * and postop entries.  If we are moving out of, or
b161c9
+ * into scope, we should record it.
b161c9
+ */
b161c9
+int
b161c9
+retrocl_entry_in_scope(Slapi_Entry *e)
b161c9
+{
b161c9
+    Slapi_DN *sdn = slapi_entry_get_sdn(e);
b161c9
+
b161c9
+    if (e == NULL){
b161c9
+        return 1;
b161c9
+    }
b161c9
+
b161c9
+    if (retrocl_excludes){
b161c9
+        int i = 0;
b161c9
+
b161c9
+        /* check the excludes */
b161c9
+        while(retrocl_excludes[i]){
b161c9
+            if (slapi_sdn_issuffix(sdn, retrocl_excludes[i])){
b161c9
+                return 0;
b161c9
+            }
b161c9
+            i++;
b161c9
+        }
b161c9
+    }
b161c9
+    if (retrocl_includes){
b161c9
+        int i = 0;
b161c9
+
b161c9
+        /* check the excludes */
b161c9
+        while(retrocl_includes[i]){
b161c9
+            if (slapi_sdn_issuffix(sdn, retrocl_includes[i])){
b161c9
+                return 1;
b161c9
+            }
b161c9
+            i++;
b161c9
+        }
b161c9
+        return 0;
b161c9
+    }
b161c9
+
b161c9
+    return 1;
b161c9
+}
b161c9
+
b161c9
+/*
b161c9
  * Function: retrocl_stop
b161c9
  *
b161c9
  * Returns: 0
a2f18f
@@ -446,26 +575,40 @@ static int retrocl_start (Slapi_PBlock *pb)
b161c9
 
b161c9
 static int retrocl_stop (Slapi_PBlock *pb)
b161c9
 {
b161c9
-  int rc = 0;
b161c9
-
b161c9
-  slapi_ch_array_free(retrocl_attributes);
b161c9
-  retrocl_attributes = NULL;
b161c9
-  slapi_ch_array_free(retrocl_aliases);
b161c9
-  retrocl_aliases = NULL;
b161c9
-
b161c9
-  retrocl_stop_trimming();  
b161c9
-  retrocl_be_changelog = NULL;
b161c9
-  retrocl_forget_changenumbers();
b161c9
-  PR_DestroyLock(retrocl_internal_lock);
b161c9
-  retrocl_internal_lock = NULL;
b161c9
-  slapi_destroy_rwlock(retrocl_cn_lock);
b161c9
-  retrocl_cn_lock = NULL;
b161c9
-  legacy_initialised = 0;
b161c9
-
b161c9
-  slapi_config_remove_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, "",
b161c9
-          LDAP_SCOPE_BASE,"(objectclass=*)", retrocl_rootdse_search);
b161c9
-
b161c9
-  return rc;
b161c9
+    int rc = 0;
b161c9
+    int i = 0;
b161c9
+
b161c9
+    slapi_ch_array_free(retrocl_attributes);
b161c9
+    retrocl_attributes = NULL;
b161c9
+    slapi_ch_array_free(retrocl_aliases);
b161c9
+    retrocl_aliases = NULL;
b161c9
+
b161c9
+    while(retrocl_excludes && retrocl_excludes[i]){
b161c9
+        slapi_sdn_free(&retrocl_excludes[i]);
b161c9
+        i++;
b161c9
+    }
b161c9
+    slapi_ch_free((void**)&retrocl_excludes);
b161c9
+    i = 0;
b161c9
+
b161c9
+    while(retrocl_includes && retrocl_includes[i]){
b161c9
+        slapi_sdn_free(&retrocl_includes[i]);
b161c9
+        i++;
b161c9
+    }
b161c9
+    slapi_ch_free((void**)&retrocl_includes);
b161c9
+
b161c9
+    retrocl_stop_trimming();
b161c9
+    retrocl_be_changelog = NULL;
b161c9
+    retrocl_forget_changenumbers();
b161c9
+    PR_DestroyLock(retrocl_internal_lock);
b161c9
+    retrocl_internal_lock = NULL;
b161c9
+    slapi_destroy_rwlock(retrocl_cn_lock);
b161c9
+    retrocl_cn_lock = NULL;
b161c9
+    legacy_initialised = 0;
b161c9
+
b161c9
+    slapi_config_remove_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, "",
b161c9
+            LDAP_SCOPE_BASE,"(objectclass=*)", retrocl_rootdse_search);
b161c9
+
b161c9
+    return rc;
b161c9
 }
b161c9
 
b161c9
 /*
b161c9
diff --git a/ldap/servers/plugins/retrocl/retrocl.h b/ldap/servers/plugins/retrocl/retrocl.h
a2f18f
index ae0139c..7edd62f 100644
b161c9
--- a/ldap/servers/plugins/retrocl/retrocl.h
b161c9
+++ b/ldap/servers/plugins/retrocl/retrocl.h
a2f18f
@@ -67,6 +67,8 @@ typedef struct _cnumRet {
b161c9
 /* was originally changelogmaximumage */
b161c9
 #define CONFIG_CHANGELOG_MAXAGE_ATTRIBUTE     "nsslapd-changelogmaxage"
b161c9
 #define CONFIG_CHANGELOG_DIRECTORY_ATTRIBUTE  "nsslapd-changelogdir"
b161c9
+#define CONFIG_CHANGELOG_INCLUDE_SUFFIX       "nsslapd-include-suffix"
b161c9
+#define CONFIG_CHANGELOG_EXCLUDE_SUFFIX       "nsslapd-exclude-suffix"
b161c9
 
b161c9
 #define RETROCL_CHANGELOG_DN "cn=changelog"
b161c9
 #define RETROCL_MAPPINGTREE_DN "cn=\"cn=changelog\",cn=mapping tree,cn=config"
a2f18f
@@ -140,4 +142,6 @@ extern void retrocl_init_trimming(void);
b161c9
 extern void retrocl_stop_trimming(void);
b161c9
 extern char *retrocl_get_config_str(const char *attrt);
b161c9
 
b161c9
+int retrocl_entry_in_scope(Slapi_Entry *e);
b161c9
+
b161c9
 #endif /* _H_RETROCL */
b161c9
diff --git a/ldap/servers/plugins/retrocl/retrocl_po.c b/ldap/servers/plugins/retrocl/retrocl_po.c
a2f18f
index 7083d0a..f689373 100644
b161c9
--- a/ldap/servers/plugins/retrocl/retrocl_po.c
b161c9
+++ b/ldap/servers/plugins/retrocl/retrocl_po.c
a2f18f
@@ -140,6 +140,7 @@ write_replog_db(
b161c9
     int			flag,
b161c9
     time_t		curtime,
b161c9
     Slapi_Entry         *log_e,
b161c9
+	Slapi_Entry         *post_entry,
b161c9
     const char          *newrdn,
b161c9
     LDAPMod		**modrdn_mods,
b161c9
     const char          *newsuperior
a2f18f
@@ -156,11 +157,26 @@ write_replog_db(
b161c9
     int	err = 0;
b161c9
     int ret = LDAP_SUCCESS;
b161c9
     int	i;
b161c9
+    int mark = 0;
b161c9
 
b161c9
     if (!dn) {
b161c9
         slapi_log_error( SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME, "write_replog_db: NULL dn\n");
b161c9
         return ret;
b161c9
     }
b161c9
+    mark = (post_entry && retrocl_entry_in_scope(post_entry));
b161c9
+    slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "post in scope (%d)\n",mark);
b161c9
+
b161c9
+    if (post_entry){
b161c9
+        if(!retrocl_entry_in_scope(log_e) && !retrocl_entry_in_scope(post_entry)){
b161c9
+            /* modrdn: entry not in scope, just return... */
b161c9
+            return ret;
b161c9
+        }
b161c9
+    } else {
b161c9
+        if(!retrocl_entry_in_scope(log_e)){
b161c9
+            /* entry not in scope, just return... */
b161c9
+            return ret;
b161c9
+        }
b161c9
+    }
b161c9
 
b161c9
     PR_Lock(retrocl_internal_lock);
b161c9
     changenum = retrocl_assign_changenumber();
a2f18f
@@ -319,7 +335,7 @@ write_replog_db(
b161c9
         break;
b161c9
 
b161c9
     case OP_DELETE:
b161c9
-        if (log_e) {
b161c9
+        if (retrocl_log_deleted) {
b161c9
             /* we have to log the full entry */
b161c9
             if ( entry2reple( e, log_e, OP_DELETE ) != 0 ) {
b161c9
                 err = SLAPI_PLUGIN_FAILURE;
a2f18f
@@ -559,7 +575,8 @@ int retrocl_postob (Slapi_PBlock *pb, int optype)
b161c9
     char		*dn;
b161c9
     LDAPMod		**log_m = NULL;
b161c9
     int			flag = 0;
b161c9
-    Slapi_Entry		*te = NULL;
b161c9
+    Slapi_Entry		*entry = NULL;
b161c9
+    Slapi_Entry		*post_entry = NULL;
b161c9
     Slapi_Operation     *op = NULL;
b161c9
     LDAPMod		**modrdn_mods = NULL;
b161c9
     char *newrdn = NULL;
a2f18f
@@ -624,7 +641,12 @@ int retrocl_postob (Slapi_PBlock *pb, int optype)
b161c9
         LDAPDebug0Args(LDAP_DEBUG_TRACE,"not applying change for nsTombstone entries\n");
b161c9
         return SLAPI_PLUGIN_SUCCESS;
b161c9
     }
b161c9
-	
b161c9
+    /*
b161c9
+     * Start by grabbing the preop entry, ADD will replace it as needed.  Getting the entry
b161c9
+     * allows up to perform scoping in write_replog_db() for all op types.
b161c9
+     */
b161c9
+    (void)slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &entry);
b161c9
+
b161c9
     switch ( optype ) {
b161c9
     case OP_MODIFY:
b161c9
         (void)slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &log_m );
a2f18f
@@ -634,14 +656,14 @@ int retrocl_postob (Slapi_PBlock *pb, int optype)
b161c9
          * For adds, we want the unnormalized dn, so we can preserve
b161c9
          * spacing, case, when replicating it.
b161c9
          */
b161c9
-        (void)slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &te );
b161c9
-        if ( NULL != te ) {
b161c9
-            dn = slapi_entry_get_dn( te );
b161c9
+        (void)slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &entry );
b161c9
+        if ( NULL != entry ) {
b161c9
+            dn = slapi_entry_get_dn( entry );
b161c9
         }
b161c9
         break;
b161c9
     case OP_DELETE:
b161c9
         if (retrocl_log_deleted)
b161c9
-            (void)slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &te);
b161c9
+            (void)slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &entry);
b161c9
         break;
b161c9
     case OP_MODRDN:
b161c9
         /* newrdn is used just for logging; no need to be normalized */
a2f18f
@@ -649,13 +671,14 @@ int retrocl_postob (Slapi_PBlock *pb, int optype)
b161c9
         (void)slapi_pblock_get( pb, SLAPI_MODRDN_DELOLDRDN, &flag );
b161c9
         (void)slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &modrdn_mods );
b161c9
         (void)slapi_pblock_get( pb, SLAPI_MODRDN_NEWSUPERIOR_SDN, &newsuperior );
b161c9
+        (void)slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &post_entry);
b161c9
         break;
b161c9
     }
b161c9
 
b161c9
     /* check if we should log change to retro changelog, and
b161c9
      * if so, do it here */
b161c9
-    if((rc = write_replog_db( pb, optype, dn, log_m, flag, curtime, te,
b161c9
-        newrdn, modrdn_mods, slapi_sdn_get_dn(newsuperior) )))
b161c9
+    if((rc = write_replog_db( pb, optype, dn, log_m, flag, curtime, entry,
b161c9
+        post_entry, newrdn, modrdn_mods, slapi_sdn_get_dn(newsuperior) )))
b161c9
     {
b161c9
         slapi_log_error(SLAPI_LOG_FATAL, "retrocl-plugin",
b161c9
                         "retrocl_postob: operation failure [%d]\n", rc);
b161c9
-- 
b161c9
1.9.3
b161c9