]> git.sur5r.net Git - openldap/commitdiff
ITS#5809 - fixed again, based on 1.423 (reverted 1.424-1.425)
authorHoward Chu <hyc@openldap.org>
Sat, 29 Nov 2008 03:41:33 +0000 (03:41 +0000)
committerHoward Chu <hyc@openldap.org>
Sat, 29 Nov 2008 03:41:33 +0000 (03:41 +0000)
servers/slapd/syncrepl.c

index 3ed2c4059ee642ea012fa93b3e11ce5c31961453..a0de80d99ca48a34b138ca172d6ff18803d3cb3d 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright 2003-2008 The OpenLDAP Foundation.
  * Portions Copyright 2003 by IBM Corporation.
- * Portions Copyright 2003 by Howard Chu, Symas Corporation.
+ * Portions Copyright 2003-2008 by Howard Chu, Symas Corporation.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -1932,9 +1932,14 @@ typedef struct dninfo {
        Entry *new_entry;
        struct berval dn;
        struct berval ndn;
+       struct berval nnewSup;
        int renamed;    /* Was an existing entry renamed? */
+       int delOldRDN;  /* Was old RDN deleted? */
        Modifications **modlist;        /* the modlist we received */
        Modifications *mods;    /* the modlist we compared */
+       Attribute *oldNattr;    /* old naming attr */
+       AttributeDescription *oldDesc;  /* for renames */
+       AttributeDescription *newDesc;  /* for renames */
 } dninfo;
 
 /* return 1 if inserted, 0 otherwise */
@@ -2201,38 +2206,187 @@ retry_add:;
                op->o_req_dn = dni.dn;
                op->o_req_ndn = dni.ndn;
                if ( dni.renamed ) {
-                       struct berval noldp, newp, nnewp;
+                       struct berval noldp, newp;
+                       Modifications *mod, **modtail, **ml, *m2;
+                       int i, got_replace = 0;
 
                        op->o_tag = LDAP_REQ_MODRDN;
                        dnRdn( &entry->e_name, &op->orr_newrdn );
                        dnRdn( &entry->e_nname, &op->orr_nnewrdn );
 
-                       dnParent( &dni.ndn, &noldp );
-                       dnParent( &entry->e_nname, &nnewp );
-                       if ( !dn_match( &noldp, &nnewp ) ) {
+                       if ( !BER_BVISNULL( &dni.nnewSup )) {
                                dnParent( &entry->e_name, &newp );
                                op->orr_newSup = &newp;
-                               op->orr_nnewSup = &nnewp;
+                               op->orr_nnewSup = &dni.nnewSup;
                        } else {
                                op->orr_newSup = NULL;
                                op->orr_nnewSup = NULL;
                        }
-                       /* Let the modify handler take care of deleting old RDNs */
-                       op->orr_deleteoldrdn = 0;
+                       op->orr_deleteoldrdn = dni.delOldRDN;
                        op->orr_modlist = NULL;
                        if ( ( rc = slap_modrdn2mods( op, &rs_modify ) ) ) {
                                goto done;
                        }
 
+                       /* Drop the RDN mods from this op:
+                        * If delOldRDN is TRUE then we should see a delete modop
+                        * for oldDesc. The valid cases are:
+                        *  oldNattr had only one value, newDesc == oldDesc:
+                        *    we'll see a replace modop for oldDesc
+                        *  oldNattr had only one value, newDesc != oldDesc:
+                        *    we'll see a delete modop for oldDesc with no values
+                        *  oldNattr had multiple values:
+                        *    we'll see a delete modop for oldDesc / old RDN value
+                        *
+                        * If the modop has only one value, just drop it; it's already
+                        * present in orr_modlist. Otherwise, if we see a delete for
+                        * oldDesc with more values, we must remove the old RDN value
+                        * from that modop since it's already in orr_modlist.
+                        *
+                        * Likewise if we see a replace on oldDesc with multiple values,
+                        * we must drop the new RDN value and turn it into an add.
+                        *
+                        * We should also see an add modop for newDesc. (But not if
+                        * we got a replace modop due to delOldRDN.) If it has
+                        * multiple values, we'll have to drop the new RDN value.
+                        */
+                       modtail = &op->orr_modlist;
+                       if ( dni.delOldRDN ) {
+                               for ( ml = &dni.mods; *ml; ml = &(*ml)->sml_next ) {
+                                       if ( (*ml)->sml_desc == dni.oldDesc ) {
+                                               short sm_op;
+                                               mod = *ml;
+                                               if ( mod->sml_op == LDAP_MOD_REPLACE &&
+                                                       dni.oldDesc != dni.newDesc ) {
+                                                       /* This Replace is due to other Mods.
+                                                        * Just let it ride.
+                                                        */
+                                                       continue;
+                                               }
+                                               if ( mod->sml_numvals <= 1 &&
+                                                       dni.oldNattr->a_numvals == 1 &&
+                                                       ( mod->sml_op == LDAP_MOD_DELETE ||
+                                                         mod->sml_op == LDAP_MOD_REPLACE )) {
+                                                       /* Drop this op */
+                                                       *ml = mod->sml_next;
+                                                       mod->sml_next = NULL;
+                                                       slap_mods_free( mod, 1 );
+                                                       if ( mod->sml_op == LDAP_MOD_REPLACE )
+                                                               got_replace = 1;
+                                                       break;
+                                               }
+                                               if ( mod->sml_op == LDAP_MOD_ADD )
+                                                       continue;
+                                               if ( mod->sml_op == LDAP_MOD_DELETE && mod->sml_numvals == 0 )
+                                                       continue;
+                                               if ( mod->sml_op == LDAP_MOD_REPLACE ) {
+                                                       got_replace = 1;
+                                                       sm_op = SLAP_MOD_SOFTADD;
+                                               } else {
+                                                       sm_op = LDAP_MOD_DELETE;
+                                               }
+                                               for ( m2 = op->orr_modlist; m2; m2=m2->sml_next ) {
+                                                       if ( m2->sml_desc == dni.oldDesc &&
+                                                               m2->sml_op == sm_op ) break;
+                                               }
+                                               for ( i=0; i<mod->sml_numvals; i++ ) {
+                                                       if ( bvmatch( &mod->sml_values[i], &m2->sml_values[0] )) {
+                                                               mod->sml_numvals--;
+                                                               ch_free( mod->sml_values[i].bv_val );
+                                                               mod->sml_values[i] = mod->sml_values[mod->sml_numvals];
+                                                               BER_BVZERO( &mod->sml_values[mod->sml_numvals] );
+                                                               if ( mod->sml_nvalues ) {
+                                                                       ch_free( mod->sml_nvalues[i].bv_val );
+                                                                       mod->sml_nvalues[i] = mod->sml_nvalues[mod->sml_numvals];
+                                                                       BER_BVZERO( &mod->sml_nvalues[mod->sml_numvals] );
+                                                               }
+                                                               break;
+                                                       }
+                                               }
+                                               break;
+                                       }
+                               }
+                       }
+                       if ( !got_replace ) {
+                               for ( ml = &dni.mods; *ml; ml = &(*ml)->sml_next ) {
+                                       if ( (*ml)->sml_desc == dni.newDesc ) {
+                                               mod = *ml;
+                                               if ( mod->sml_op != LDAP_MOD_ADD )
+                                                       continue;
+                                               if ( mod->sml_numvals == 1 ) {
+                                                       /* Drop this op */
+                                                       *ml = mod->sml_next;
+                                                       mod->sml_next = NULL;
+                                                       slap_mods_free( mod, 1 );
+                                                       break;
+                                               }
+                                               for ( m2 = op->orr_modlist; m2; m2=m2->sml_next ) {
+                                                       if ( m2->sml_desc == dni.oldDesc &&
+                                                               m2->sml_op == SLAP_MOD_SOFTADD ) break;
+                                               }
+                                               for ( i=0; i<mod->sml_numvals; i++ ) {
+                                                       if ( bvmatch( &mod->sml_values[i], &m2->sml_values[0] )) {
+                                                               mod->sml_numvals--;
+                                                               ch_free( mod->sml_values[i].bv_val );
+                                                               mod->sml_values[i] = mod->sml_values[mod->sml_numvals];
+                                                               BER_BVZERO( &mod->sml_values[mod->sml_numvals] );
+                                                               if ( mod->sml_nvalues ) {
+                                                                       ch_free( mod->sml_nvalues[i].bv_val );
+                                                                       mod->sml_nvalues[i] = mod->sml_nvalues[mod->sml_numvals];
+                                                                       BER_BVZERO( &mod->sml_nvalues[mod->sml_numvals] );
+                                                               }
+                                                               break;
+                                                       }
+                                               }
+                                               break;
+                                       }
+                               }
+                       }
+                                       
                        /* RDNs must be NUL-terminated for back-ldap */
                        noldp = op->orr_newrdn;
                        ber_dupbv_x( &op->orr_newrdn, &noldp, op->o_tmpmemctx );
                        noldp = op->orr_nnewrdn;
                        ber_dupbv_x( &op->orr_nnewrdn, &noldp, op->o_tmpmemctx );
 
-                       /* Remove the CSN for now, only propagate the Modify */
-                       if ( syncCSN ) {
-                               slap_graduate_commit_csn( op );
+                       /* Setup opattrs too */
+                       {
+                               static AttributeDescription *nullattr = NULL;
+                               static AttributeDescription **const opattrs[] = {
+                                       &slap_schema.si_ad_entryCSN,
+                                       &slap_schema.si_ad_modifiersName,
+                                       &slap_schema.si_ad_modifyTimestamp,
+                                       &nullattr
+                               };
+                               AttributeDescription *opattr;
+                               int i;
+
+                               modtail = &m2;
+                               /* pull mod off incoming modlist */
+                               for ( i = 0; (opattr = *opattrs[i]) != NULL; i++ ) {
+                                       for ( ml = &dni.mods; *ml; ml = &(*ml)->sml_next )
+                                       {
+                                               if ( (*ml)->sml_desc == opattr ) {
+                                                       mod = *ml;
+                                                       *ml = mod->sml_next;
+                                                       mod->sml_next = NULL;
+                                                       *modtail = mod;
+                                                       modtail = &mod->sml_next;
+                                                       break;
+                                               }
+                                       }
+                               }
+                               /* If there are still Modifications left, put the opattrs
+                                * back, and let be_modify run. Otherwise, append the opattrs
+                                * to the orr_modlist.
+                                */
+                               if ( dni.mods ) {
+                                       mod = dni.mods;
+                               } else {
+                                       mod = op->orr_modlist;
+                               }
+                               for ( ; mod->sml_next; mod=mod->sml_next );
+                               mod->sml_next = m2;
                        }
                        op->o_bd = si->si_wbe;
                        rc = op->o_bd->be_modrdn( op, &rs_modify );
@@ -2244,12 +2398,9 @@ retry_add:;
                                        "syncrepl_entry: %s be_modrdn (%d)\n", 
                                        si->si_ridtxt, rc, 0 );
                        op->o_bd = be;
-                       /* Renamed entries still have other mods so just fallthru */
+                       /* Renamed entries may still have other mods so just fallthru */
                        op->o_req_dn = entry->e_name;
                        op->o_req_ndn = entry->e_nname;
-                       if ( syncCSN ) {
-                               slap_queue_csn( op, syncCSN );
-                       }
                }
                if ( dni.mods ) {
                        op->o_tag = LDAP_REQ_MODIFY;
@@ -2269,7 +2420,7 @@ retry_add:;
                                        si->si_ridtxt, rs_modify.sr_err, 0 );
                        }
                        op->o_bd = be;
-               } else {
+               } else if ( !dni.renamed ) {
                        Debug( LDAP_DEBUG_SYNC,
                                        "syncrepl_entry: %s entry unchanged, ignored (%s)\n", 
                                        si->si_ridtxt, op->o_req_dn.bv_val, 0 );
@@ -2999,34 +3150,7 @@ dn_callback(
                                Attribute *old, *new;
                                struct berval old_rdn, new_rdn;
                                struct berval old_p, new_p;
-                               int is_ctx;
-
-                               is_ctx = dn_match( &rs->sr_entry->e_nname,
-                                       &op->o_bd->be_nsuffix[0] );
-
-                               /* Did the DN change?
-                                * case changes in the parent are ignored,
-                                * we only want to know if the RDN was
-                                * actually changed.
-                                */
-                               dnRdn( &rs->sr_entry->e_name, &old_rdn );
-                               dnRdn( &dni->new_entry->e_name, &new_rdn );
-                               dnParent( &rs->sr_entry->e_nname, &old_p );
-                               dnParent( &dni->new_entry->e_nname, &new_p );
-
-                               if ( !dn_match( &old_rdn, &new_rdn ) ||
-                                       ber_bvstrcasecmp( &old_p, &new_p ))
-                               {
-                                       dni->renamed = 1;
-
-                                       /* A ModDN has happened, but other changes may have
-                                        * occurred before we picked it up. So fallthru to
-                                        * regular Modify processing.
-                                        */
-                               }
-
-                               modtail = &dni->mods;
-                               ml = dni->modlist;
+                               int is_ctx, new_sup = 0;
 
                                /* Make sure new entry is actually newer than old entry */
                                old = attr_find( rs->sr_entry->e_attrs,
@@ -3058,6 +3182,69 @@ dn_callback(
                                        }
                                }
 
+                               is_ctx = dn_match( &rs->sr_entry->e_nname,
+                                       &op->o_bd->be_nsuffix[0] );
+
+                               /* Did the DN change?
+                                * case changes in the parent are ignored,
+                                * we only want to know if the RDN was
+                                * actually changed.
+                                */
+                               dnRdn( &rs->sr_entry->e_name, &old_rdn );
+                               dnRdn( &dni->new_entry->e_name, &new_rdn );
+                               dnParent( &rs->sr_entry->e_nname, &old_p );
+                               dnParent( &dni->new_entry->e_nname, &new_p );
+
+                               new_sup = !dn_match( &old_p, &new_p );
+                               if ( !dn_match( &old_rdn, &new_rdn ) || new_sup )
+                               {
+                                       struct berval oldRDN, oldVal;
+                                       AttributeDescription *ad = NULL;
+                                       int oldpos, newpos;
+                                       Attribute *a;
+
+                                       dni->renamed = 1;
+                                       if ( new_sup )
+                                               dni->nnewSup = new_p;
+
+                                       /* See if the oldRDN was deleted */
+                                       dnRdn( &rs->sr_entry->e_nname, &oldRDN );
+                                       oldVal.bv_val = strchr(oldRDN.bv_val, '=') + 1;
+                                       oldVal.bv_len = oldRDN.bv_len - ( oldVal.bv_val -
+                                               oldRDN.bv_val );
+                                       oldRDN.bv_len -= oldVal.bv_len + 1;
+                                       slap_bv2ad( &oldRDN, &ad, &rs->sr_text );
+                                       dni->oldDesc = ad;
+                                       for ( oldpos=0, a=rs->sr_entry->e_attrs;
+                                               a && a->a_desc != ad; oldpos++, a=a->a_next );
+                                       dni->oldNattr = a;
+                                       for ( newpos=0, a=dni->new_entry->e_attrs;
+                                               a && a->a_desc != ad; newpos++, a=a->a_next );
+                                       if ( !a || oldpos != newpos || attr_valfind( a,
+                                               SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH |
+                                               SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+                                               SLAP_MR_VALUE_OF_SYNTAX,
+                                               &oldVal, NULL, op->o_tmpmemctx ) != LDAP_SUCCESS )
+                                       {
+                                               dni->delOldRDN = 1;
+                                       }
+                                       /* Get the newRDN's desc */
+                                       dnRdn( &dni->new_entry->e_nname, &oldRDN );
+                                       oldVal.bv_val = strchr(oldRDN.bv_val, '=');
+                                       oldRDN.bv_len = oldVal.bv_val - oldRDN.bv_val;
+                                       ad = NULL;
+                                       slap_bv2ad( &oldRDN, &ad, &rs->sr_text );
+                                       dni->newDesc = ad;
+
+                                       /* A ModDN has happened, but in Refresh mode other
+                                        * changes may have occurred before we picked it up.
+                                        * So fallthru to regular Modify processing.
+                                        */
+                               }
+
+                               modtail = &dni->mods;
+                               ml = dni->modlist;
+
                                /* We assume that attributes are saved in the same order
                                 * in the remote and local databases. So if we walk through
                                 * the attributeDescriptions one by one they should match in