ldap/servers/plugins/replication/windows_protocol_util.c | 24 +++++++++++++--
ldap/servers/slapd/entry.c | 8 +++++
ldap/servers/slapd/slapi-plugin.h | 15 ++++++++-
3 files changed, 43 insertions(+), 4 deletions(-)
New commits:
commit 2be27d35da9fdf6d5f35ff4fb94906f4fe193133
Author: Nathan Kinder <nkinder(a)redhat.com>
Date: Thu Apr 21 15:42:28 2011 -0700
Bug 695779 - windows sync can lose old values when a new value is added
When a single modify operation with multiple mods is played back to
AD, it is possible for some old values to be lost. We check if a
mod is necessary to send to AD to detect loops, and skip sending
operations or values that are not necessary. This doesn't take
prior mods in the same modify operation into account though, which
can make the results incorrect.
This patch makes the sync code keep a running copy of the AD entry
that the mods are applied to. We use this copy to check which mods
can be skipped and which we must send.
diff --git a/ldap/servers/plugins/replication/windows_protocol_util.c
b/ldap/servers/plugins/replication/windows_protocol_util.c
index 716f4be..abcdbc3 100644
--- a/ldap/servers/plugins/replication/windows_protocol_util.c
+++ b/ldap/servers/plugins/replication/windows_protocol_util.c
@@ -2063,12 +2063,11 @@ windows_delete_local_entry(Slapi_DN *sdn){
error message to that effect.
*/
static int
-mod_already_made(Private_Repl_Protocol *prp, Slapi_Mod *smod)
+mod_already_made(Private_Repl_Protocol *prp, Slapi_Mod *smod, Slapi_Entry *ad_entry)
{
int retval = 0;
int op = 0;
const char *type = NULL;
- const Slapi_Entry *ad_entry = windows_private_get_raw_entry(prp->agmt);
if (!slapi_mod_isvalid(smod)) { /* bogus */
slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
@@ -2155,6 +2154,15 @@ mod_already_made(Private_Repl_Protocol *prp, Slapi_Mod *smod)
agmt_get_long_name(prp->agmt), op);
}
+ /* If the mod shouldn't be skipped, we should
+ * apply it to the entry that was passed in. This
+ * allows a single modify operation with multiple
+ * mods to take prior mods into account when
+ * determining what can be skipped. */
+ if (retval == 0) {
+ slapi_entry_apply_mod(ad_entry, slapi_mod_get_ldapmod_byref(smod));
+ }
+
return retval;
}
@@ -2438,10 +2446,18 @@ windows_map_mods_for_replay(Private_Repl_Protocol *prp,LDAPMod
**original_mods,
LDAPMod *mod = NULL;
int is_nt4 = windows_private_get_isnt4(prp->agmt);
Slapi_Mod *mysmod = NULL;
+ const Slapi_Entry *ad_entry = NULL;
+ Slapi_Entry *ad_entry_copy = NULL;
LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_map_mods_for_replay\n", 0, 0, 0
);
/* Iterate through the original mods, looking each attribute type up in the maps for
either user or group */
+
+ /* Make a copy of the AD entry. */
+ ad_entry = windows_private_get_raw_entry(prp->agmt);
+ if (ad_entry) {
+ ad_entry_copy = slapi_entry_dup(ad_entry);
+ }
slapi_mods_init_byref(&smods, original_mods);
slapi_mods_init(&mapped_smods,10);
@@ -2603,7 +2619,7 @@ windows_map_mods_for_replay(Private_Repl_Protocol *prp,LDAPMod
**original_mods,
}
}
/* Otherwise we do not copy this mod at all */
- if (mysmod && !mod_already_made(prp, mysmod)) { /* make sure this mod is still
valid to send */
+ if (mysmod && !mod_already_made(prp, mysmod, ad_entry_copy)) { /* make sure
this mod is still valid to send */
slapi_mods_add_ldapmod(&mapped_smods, slapi_mod_get_ldapmod_passout(mysmod));
}
if (mysmod) {
@@ -2612,6 +2628,8 @@ windows_map_mods_for_replay(Private_Repl_Protocol *prp,LDAPMod
**original_mods,
mod = slapi_mods_get_next_mod(&smods);
}
+
+ slapi_entry_free(ad_entry_copy);
slapi_mods_done (&smods);
/* Extract the mods for the caller */
*returned_mods = slapi_mods_get_ldapmods_passout(&mapped_smods);
diff --git a/ldap/servers/slapd/entry.c b/ldap/servers/slapd/entry.c
index 9e0b57f..a945485 100644
--- a/ldap/servers/slapd/entry.c
+++ b/ldap/servers/slapd/entry.c
@@ -3176,6 +3176,14 @@ slapi_entry_apply_mods( Slapi_Entry *e, LDAPMod **mods )
return entry_apply_mods(e, mods);
}
+/*
+ * Apply a single mod to an entry
+ */
+int slapi_entry_apply_mod( Slapi_Entry *e, LDAPMod *mod )
+{
+ return entry_apply_mod(e, mod);
+}
+
int
entry_apply_mods( Slapi_Entry *e, LDAPMod **mods )
{
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
index 8531fbe..898e0f8 100644
--- a/ldap/servers/slapd/slapi-plugin.h
+++ b/ldap/servers/slapd/slapi-plugin.h
@@ -1978,7 +1978,7 @@ int slapi_entry_delete_string(Slapi_Entry *e, const char *type,
const char *valu
void slapi_entry_diff(Slapi_Mods *smods, Slapi_Entry *e1, Slapi_Entry *e2, int
diff_ctrl);
/**
- * Applies an array of \c LDAPMod modifications a Slapi_Entry.
+ * Applies an array of \c LDAPMod modifications to a Slapi_Entry.
*
* \param e Entry to which you want to apply the modifications.
* \param mods \c NULL terminated array of \c LDAPMod modifications that you
@@ -1991,6 +1991,19 @@ void slapi_entry_diff(Slapi_Mods *smods, Slapi_Entry *e1,
Slapi_Entry *e2, int d
int slapi_entry_apply_mods(Slapi_Entry *e, LDAPMod **mods);
/**
+ * Applies a single \c LDAPMod modification to a Slapi_Entry.
+ *
+ * \param e Entry to which you want to apply the modification.
+ * \param mod A pointer to the \c LDAPMod modification that you
+ * want to apply to the specified entry.
+ * \return \c LDAP_SUCCESS if the mod applied to the entry cleanly, otherwise
+ * an LDAP error is returned.
+ * \warning It is up to the caller to free the \c LDAPMod after the mod has
+ * been applied.
+ */
+int slapi_entry_apply_mod(Slapi_Entry *e, LDAPMod *mod);
+
+/**
* Renames a Slapi_Entry.
*
* This function will rename an existing \c Slapi_Entry, similar to what