diff --git a/servers/slapd/add.c b/servers/slapd/add.c index 1525b4e3e2..9c6ca2e6ee 100644 --- a/servers/slapd/add.c +++ b/servers/slapd/add.c @@ -490,6 +490,9 @@ slap_mods2entry( } else { attr->a_nvals = attr->a_vals; } + /* slap_mods_check() gives us sorted results */ + if ( attr->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL ) + attr->a_flags |= SLAP_ATTR_SORTED_VALS; *tail = attr; tail = &attr->a_next; diff --git a/servers/slapd/attr.c b/servers/slapd/attr.c index f4d76dc21c..7112358c1e 100644 --- a/servers/slapd/attr.c +++ b/servers/slapd/attr.c @@ -281,6 +281,74 @@ attrs_dup( Attribute *a ) return anew; } +int +attr_valfind( + Attribute *a, + unsigned flags, + struct berval *val, + unsigned *slot, + void *ctx ) +{ + struct berval nval = BER_BVNULL, *cval; + MatchingRule *mr = a->a_desc->ad_type->sat_equality; + const char *text; + int match = -1, rc; + unsigned i; + + if( !SLAP_IS_MR_ASSERTED_VALUE_NORMALIZED_MATCH( flags ) && + mr->smr_normalize ) + { + rc = (mr->smr_normalize)( + flags & (SLAP_MR_TYPE_MASK|SLAP_MR_SUBTYPE_MASK|SLAP_MR_VALUE_OF_SYNTAX), + a->a_desc->ad_type->sat_syntax, + mr, val, &nval, ctx ); + + if( rc != LDAP_SUCCESS ) { + return LDAP_INVALID_SYNTAX; + } + cval = &nval; + } else { + cval = val; + } + + if ( a->a_flags & SLAP_ATTR_SORTED_VALS ) { + /* Binary search */ + unsigned base = 0, n = a->a_numvals; + int val = 0; + + while ( 0 < n ) { + unsigned pivot = n >> 1; + i = base + pivot; + rc = value_match( &match, a->a_desc, mr, flags, + &a->a_nvals[i], cval, &text ); + if ( rc == LDAP_SUCCESS && match == 0 ) + break; + n = pivot; + if ( match > 0 ) + base = i+1; + } + if ( match > 0 ) + i++; + } else { + /* Linear search */ + for ( i = 0; i < a->a_numvals; i++ ) { + const char *text; + + rc = ordered_value_match( &match, a->a_desc, mr, flags, + &a->a_nvals[i], cval, &text ); + if ( rc == LDAP_SUCCESS && match == 0 ) + break; + } + } + *slot = i; + if ( match ) + rc = LDAP_NO_SUCH_ATTRIBUTE; + if ( nval.bv_val ) + slap_sl_free( nval.bv_val, ctx ); + + return rc; +} + int attr_valadd( Attribute *a, @@ -312,22 +380,52 @@ attr_valadd( a->a_nvals = a->a_vals; } - v2 = &a->a_vals[a->a_numvals]; - for ( i = 0 ; i < nn; i++ ) { - ber_dupbv( &v2[i], &vals[i] ); - if ( BER_BVISNULL( &v2[i] ) ) break; - } - BER_BVZERO( &v2[i] ); - - if ( nvals ) { - v2 = &a->a_nvals[a->a_numvals]; + /* If sorted and old vals exist, must insert */ + if (( a->a_flags & SLAP_ATTR_SORTED_VALS ) && a->a_numvals ) { + unsigned slot; + int j, rc; + v2 = nvals ? nvals : vals; + for ( i = 0; i < nn; i++ ) { + rc = attr_valfind( a, SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX | + SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH, + &v2[i], &slot, NULL ); + if ( rc != LDAP_NO_SUCH_ATTRIBUTE ) { + /* should never happen */ + if ( rc == LDAP_SUCCESS ) + rc = LDAP_TYPE_OR_VALUE_EXISTS; + return rc; + } + for ( j = a->a_numvals; j > slot; j-- ) { + a->a_vals[j+1] = a->a_vals[j]; + if ( nvals ) + a->a_nvals[j+1] = a->a_nvals[j]; + } + ber_dupbv( &a->a_nvals[j], &v2[i] ); + if ( nvals ) + ber_dupbv( &a->a_vals[j], &vals[i] ); + a->a_numvals++; + } + BER_BVZERO( &a->a_vals[a->a_numvals] ); + if ( a->a_vals != a->a_nvals ) + BER_BVZERO( &a->a_nvals[a->a_numvals] ); + } else { + v2 = &a->a_vals[a->a_numvals]; for ( i = 0 ; i < nn; i++ ) { - ber_dupbv( &v2[i], &nvals[i] ); + ber_dupbv( &v2[i], &vals[i] ); if ( BER_BVISNULL( &v2[i] ) ) break; } BER_BVZERO( &v2[i] ); + + if ( nvals ) { + v2 = &a->a_nvals[a->a_numvals]; + for ( i = 0 ; i < nn; i++ ) { + ber_dupbv( &v2[i], &nvals[i] ); + if ( BER_BVISNULL( &v2[i] ) ) break; + } + BER_BVZERO( &v2[i] ); + } + a->a_numvals += i; } - a->a_numvals += i; return 0; } diff --git a/servers/slapd/back-bdb/monitor.c b/servers/slapd/back-bdb/monitor.c index 96ad309967..2271940de1 100644 --- a/servers/slapd/back-bdb/monitor.c +++ b/servers/slapd/back-bdb/monitor.c @@ -210,6 +210,7 @@ bdb_monitor_free( mod.sm_op = LDAP_MOD_DELETE; mod.sm_desc = slap_schema.si_ad_objectClass; mod.sm_values = values; + mod.sm_numvals = 1; values[ 0 ] = oc_olmBDBDatabase->soc_cname; BER_BVZERO( &values[ 1 ] ); @@ -218,9 +219,10 @@ bdb_monitor_free( /* don't care too much about return code... */ /* remove attrs */ + mod.sm_values = NULL; + mod.sm_numvals = 0; for ( i = 0; s_at[ i ].desc != NULL; i++ ) { mod.sm_desc = *s_at[ i ].ad; - mod.sm_values = NULL; rc = modify_delete_values( e, &mod, 1, &text, textbuf, sizeof( textbuf ) ); /* don't care too much about return code... */ diff --git a/servers/slapd/bconfig.c b/servers/slapd/bconfig.c index 6a6e56d61f..d83ac37520 100644 --- a/servers/slapd/bconfig.c +++ b/servers/slapd/bconfig.c @@ -177,6 +177,7 @@ enum { CFG_HIDDEN, CFG_MONITORING, CFG_SERVERID, + CFG_SORTVALS, CFG_LAST }; @@ -552,6 +553,11 @@ static ConfigTable config_back_cf_table[] = { { "sockbuf_max_incoming_auth", "max", 2, 2, 0, ARG_BER_LEN_T, &sockbuf_max_incoming_auth, "( OLcfgGlAt:62 NAME 'olcSockbufMaxIncomingAuth' " "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL }, + { "sortvals", "attr", 2, 0, 0, ARG_MAGIC|CFG_SORTVALS, + &config_generic, "( OLcfgGlAt:83 NAME 'olcSortVals' " + "DESC 'Attributes whose values will always be sorted' " + "EQUALITY caseIgnoreMatch " + "SYNTAX OMsDirectoryString )", NULL, NULL }, { "subordinate", "[advertise]", 1, 2, 0, ARG_DB|ARG_MAGIC, &config_subordinate, "( OLcfgDbAt:0.15 NAME 'olcSubordinate' " "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL }, @@ -765,7 +771,7 @@ static ConfigOCs cf_ocs[] = { "NAME 'olcFrontendConfig' " "DESC 'OpenLDAP frontend configuration' " "AUXILIARY " - "MAY ( olcDefaultSearchBase $ olcPasswordHash ) )", + "MAY ( olcDefaultSearchBase $ olcPasswordHash $ olcSortVals ) )", Cft_Database, NULL, NULL }, #ifdef SLAPD_MODULES { "( OLcfgGlOc:8 " @@ -786,6 +792,13 @@ typedef struct ServerID { static ServerID *sid_list; +typedef struct ADlist { + struct ADlist *al_next; + AttributeDescription *al_desc; +} ADlist; + +static ADlist *sortVals; + static int config_generic(ConfigArgs *c) { int i; @@ -1000,6 +1013,14 @@ config_generic(ConfigArgs *c) { case CFG_SSTR_IF_MIN: c->value_int = index_substr_if_minlen; break; + case CFG_SORTVALS: { + ADlist *sv; + rc = 1; + for ( sv = sortVals; sv; sv = sv->al_next ) { + value_add_one( &c->rvalue_vals, &sv->al_desc->ad_cname ); + rc = 0; + } + } break; #ifdef SLAPD_MODULES case CFG_MODLOAD: { ModPaths *mp = c->private; @@ -1224,6 +1245,27 @@ config_generic(ConfigArgs *c) { } } break; + case CFG_SORTVALS: + if ( c->valx < 0 ) { + ADlist *sv; + for ( sv = sortVals; sv; sv = sortVals ) { + sortVals = sv->al_next; + sv->al_desc->ad_type->sat_flags &= ~SLAP_AT_SORTED_VAL; + ch_free( sv ); + } + } else { + ADlist *sv, **prev; + int i = 0; + + for ( prev = &sortVals, sv = sortVals; i < c->valx; i++ ) { + prev = &sv->al_next; + sv = sv->al_next; + } + sv->al_desc->ad_type->sat_flags &= ~SLAP_AT_SORTED_VAL; + *prev = sv->al_next; + ch_free( sv ); + } + break; case CFG_LIMITS: /* FIXME: there is no limits_free function */ @@ -1447,6 +1489,52 @@ config_generic(ConfigArgs *c) { return(1); break; + case CFG_SORTVALS: { + ADlist *svnew = NULL, *svtail, *sv; + + for ( i = 1; i < c->argc; i++ ) { + AttributeDescription *ad = NULL; + const char *text; + int rc; + + rc = slap_str2ad( c->argv[i], &ad, &text ); + if ( rc ) { + snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unknown attribute type #%d", + c->argv[0], i ); +sortval_reject: + Debug(LDAP_DEBUG_ANY, "%s: %s %s\n", + c->log, c->cr_msg, c->argv[i] ); + for ( sv = svnew; sv; sv = svnew ) { + svnew = sv->al_next; + ch_free( sv ); + } + return 1; + } + if (( ad->ad_type->sat_flags & SLAP_AT_ORDERED ) || + ad->ad_type->sat_single_value ) { + snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> inappropriate attribute type #%d", + c->argv[0], i ); + goto sortval_reject; + } + sv = ch_malloc( sizeof( ADlist )); + sv->al_desc = ad; + if ( !svnew ) { + svnew = sv; + } else { + svtail->al_next = sv; + } + svtail = sv; + } + for ( sv = svnew; sv; sv = sv->al_next ) + sv->al_desc->ad_type->sat_flags |= SLAP_AT_SORTED_VAL; + for ( sv = sortVals; sv && sv->al_next; sv = sv->al_next ); + if ( sv ) + sv->al_next = svnew; + else + sortVals = svnew; + } + break; + case CFG_ACL: /* Don't append to the global ACL if we're on a specific DB */ i = c->valx; @@ -4544,7 +4632,7 @@ config_modify_internal( CfEntryInfo *ce, Operation *op, SlapReply *rs, */ if ( ct && ml->sml_values ) { delrec *d; - for (i=0; ml->sml_values[i].bv_val; i++); + i = ml->sml_numvals; d = ch_malloc( sizeof(delrec) + (i - 1)* sizeof(int)); d->nidx = i; d->next = NULL; @@ -4577,8 +4665,7 @@ config_modify_internal( CfEntryInfo *ce, Operation *op, SlapReply *rs, if ( ct->arg_type & ARG_NO_INSERT ) { Attribute *a = attr_find( e->e_attrs, ml->sml_desc ); if ( a ) { - for (i = 0; a->a_vals[i].bv_val; i++ ); - navals = i; + navals = a->a_numvals; } } for ( i=0; !BER_BVISNULL( &ml->sml_values[i] ); i++ ) { diff --git a/servers/slapd/entry.c b/servers/slapd/entry.c index 3e868cc811..ff03e8203f 100644 --- a/servers/slapd/entry.c +++ b/servers/slapd/entry.c @@ -277,6 +277,18 @@ str2entry2( char *s, int checkvals ) if (( ad_prev && ad != ad_prev ) || ( i == lines )) { int j, k; + /* FIXME: we only need this when migrating from an unsorted DB */ + if ( atail != &ahead && atail->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL ) { + rc = slap_sort_vals( (Modifications *)atail, &text, &j, NULL ); + if ( rc == LDAP_SUCCESS ) { + atail->a_flags |= SLAP_ATTR_SORTED_VALS; + } else if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) { + Debug( LDAP_DEBUG_ANY, + "str2entry: attributeType %s value #%d provided more than once\n", + atail->a_desc->ad_cname.bv_val, j, 0 ); + goto fail; + } + } atail->a_next = attr_alloc( NULL ); atail = atail->a_next; atail->a_flags = 0; @@ -892,6 +904,19 @@ int entry_decode(EntryHeader *eh, Entry **e) } else { a->a_nvals = a->a_vals; } + /* FIXME: This is redundant once a sorted entry is saved into the DB */ + if ( a->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL ) { + rc = slap_sort_vals( (Modifications *)a, &text, &j, NULL ); + if ( rc == LDAP_SUCCESS ) { + a->a_flags |= SLAP_ATTR_SORTED_VALS; + } else if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) { + /* should never happen */ + Debug( LDAP_DEBUG_ANY, + "entry_decode: attributeType %s value #%d provided more than once\n", + a->a_desc->ad_cname.bv_val, j, 0 ); + return rc; + } + } a = a->a_next; nattrs--; if ( !nattrs ) diff --git a/servers/slapd/modify.c b/servers/slapd/modify.c index f3948d4e33..d0282b16e6 100644 --- a/servers/slapd/modify.c +++ b/servers/slapd/modify.c @@ -640,200 +640,200 @@ int slap_mods_check( /* check for duplicates, but ignore Deletes. */ if( nvals > 1 && ml->sml_op != LDAP_MOD_DELETE ) { -#define SLAP_MODS_CHECK_QUICKSORT -#ifndef SLAP_MODS_CHECK_QUICKSORT - int i, j, rc, match; - MatchingRule *mr = ad->ad_type->sat_equality; - - for ( i = 1; i < nvals ; i++ ) { - /* test asserted values against themselves */ - for( j = 0; j < i; j++ ) { - rc = ordered_value_match( &match, ml->sml_desc, mr, - SLAP_MR_EQUALITY - | SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX - | SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH - | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH, - ml->sml_nvalues - ? &ml->sml_nvalues[i] - : &ml->sml_values[i], - ml->sml_nvalues - ? &ml->sml_nvalues[j] - : &ml->sml_values[j], - text ); - if ( rc == LDAP_SUCCESS && match == 0 ) { - /* value exists already */ - snprintf( textbuf, textlen, - "%s: value #%d provided more than once", - ml->sml_desc->ad_cname.bv_val, j ); - *text = textbuf; - return LDAP_TYPE_OR_VALUE_EXISTS; - - } else if ( rc != LDAP_SUCCESS ) { - return rc; - } - } + int i; + rc = slap_sort_vals( ml, text, &i, ctx ); + if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) { + /* value exists already */ + snprintf( textbuf, textlen, + "%s: value #%d provided more than once", + ml->sml_desc->ad_cname.bv_val, i ); + *text = textbuf; } -#else /* SLAP_MODS_CHECK_QUICKSORT */ + if ( rc ) + return rc; + } + } else { + ml->sml_numvals = 0; + } + } -/* Quicksort + Insertion sort for small arrays */ + return LDAP_SUCCESS; +} + +/* Sort a set of values. An (Attribute *) may be used interchangeably here + * instead of a (Modifications *) structure. + * + * Uses Quicksort + Insertion sort for small arrays + */ + +int +slap_sort_vals( + Modifications *ml, + const char **text, + int *dup, + void *ctx ) +{ + AttributeDescription *ad; + MatchingRule *mr; + int istack[sizeof(int)*16]; + int i, j, k, l, ir, jstack, match, *ix, itmp, nvals, rc; + int is_norm; + struct berval a, *cv; #define SMALL 8 #define SWAP(a,b,tmp) tmp=(a);(a)=(b);(b)=tmp #define COMP(a,b) match=0; rc = ordered_value_match( &match, \ - ml->sml_desc, mr, SLAP_MR_EQUALITY \ + ad, mr, SLAP_MR_EQUALITY \ | SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX \ | SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH \ | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH, \ &(a), &(b), text ); - MatchingRule *mr = ad->ad_type->sat_equality; - int istack[sizeof(int)*16]; - int i, j, k, l, ir, jstack, match, *ix, itmp; - struct berval a, *cv; - -/* If PRESERVE_ORDER is defined only the index array is sorted; the - * actual values are left in their incoming order. Otherwise, the - * only reason to keep the index array is to identify the offending - * value when duplicates are found. - */ -#define PRESERVE_ORDER -#ifndef PRESERVE_ORDER - struct berval va, *v, *nv, bvtmp; - -#define IX(x) x -#define EXCH(x,y) SWAP(ix[x],ix[y],itmp); SWAP(cv[x],cv[y],bvtmp); \ - if (nv) {SWAP(v[x],v[y],bvtmp);} -#define SETA(x) itmp = ix[x]; a = cv[x]; if (nv) va=v[x] -#define GETA(x) ix[x] = itmp; cv[x] = a; if (nv) v[x]=va -#define SET(x,y) ix[x] = ix[y]; cv[x] = cv[y]; if (nv) v[x]=v[y] - - v = ml->sml_values; - nv = ml->sml_nvalues; - -#else /* PRESERVE_ORDER */ - #define IX(x) ix[x] #define EXCH(x,y) SWAP(ix[x],ix[y],itmp) #define SETA(x) itmp = ix[x]; a = cv[itmp] #define GETA(x) ix[x] = itmp; #define SET(x,y) ix[x] = ix[y] -#endif /* PRESERVE_ORDER */ + ad = ml->sml_desc; + nvals = ml->sml_numvals; - cv = ml->sml_nvalues ? ml->sml_nvalues : ml->sml_values; - if ( ad == slap_schema.si_ad_objectClass ) - mr = NULL; /* shortcut matching */ + /* For Modifications, sml_nvalues is NULL if normalization wasn't needed. + * For Attributes, sml_nvalues == sml_values when normalization isn't needed. + */ + if ( ml->sml_nvalues && ml->sml_nvalues != ml->sml_values ) { + cv = ml->sml_nvalues; + is_norm = 1; + } else { + cv = ml->sml_values; + is_norm = 0; + } - /* record indices to preserve input ordering */ - ix = slap_sl_malloc( nvals * sizeof(int), ctx ); - for (i=0; iad_type->sat_equality; - ir = nvals-1; - l = 0; - jstack = 0; + /* record indices to preserve input ordering */ + ix = slap_sl_malloc( nvals * sizeof(int), ctx ); + for (i=0; i=0;i--) { - COMP(cv[IX(i)], a); - if ( match <= 0 ) - break; - SET(i+1,i); - } - GETA(i+1); - if ( match == 0 ) goto done; - } - if ( jstack == 0 ) break; - if ( match == 0 ) break; - ir = istack[jstack--]; - l = istack[jstack--]; - } else { - k = (l + ir) >> 1; /* Choose median of left, center, right */ - EXCH(k, l+1); - COMP( cv[IX(l)], cv[IX(ir)] ); - if ( match > 0 ) { - EXCH(l, ir); - } else if ( match == 0 ) { - i = ir; - break; - } - COMP( cv[IX(l+1)], cv[IX(ir)] ); - if ( match > 0 ) { - EXCH(l+1, ir); - } else if ( match == 0 ) { - i = ir; - break; - } - COMP( cv[IX(l)], cv[IX(l+1)] ); - if ( match > 0 ) { - EXCH(l, l+1); - } else if ( match == 0 ) { - i = l; - break; - } - i = l+1; - j = ir; - a = cv[IX(i)]; - for(;;) { - do { - i++; - COMP( cv[IX(i)], a ); - } while( match < 0 ); - while( match > 0 ) { - j--; - COMP( cv[IX(j)], a ); - } - if (j < i) { - match = 1; - break; - } - if ( match == 0 ) { - i = l+1; - break; - } - EXCH(i,j); - } - if ( match == 0 ) - break; - EXCH(l+1,j); - jstack += 2; - if (ir-i+1 >= j) { - istack[jstack] = ir; - istack[jstack-1] = i; - ir = j; - } else { - istack[jstack] = j; - istack[jstack-1] = l; - l = i; - } - } + ir = nvals-1; + l = 0; + jstack = 0; + + for(;;) { + if (ir - l < SMALL) { /* Insertion sort */ + match=1; + for (j=l+1;j<=ir;j++) { + SETA(j); + for (i=j-1;i>=0;i--) { + COMP(cv[IX(i)], a); + if ( match <= 0 ) + break; + SET(i+1,i); } -done: - if ( i >= 0 ) - j = ix[i]; - - slap_sl_free( ix, ctx ); - - if ( rc != LDAP_SUCCESS ) { - return rc; - } else if ( match == 0 ) { - /* value exists already */ - assert( i >= 0 ); - assert( i < nvals ); - snprintf( textbuf, textlen, - "%s: value #%d provided more than once", - ml->sml_desc->ad_cname.bv_val, j ); - *text = textbuf; - return LDAP_TYPE_OR_VALUE_EXISTS; + GETA(i+1); + if ( match == 0 ) goto done; + } + if ( jstack == 0 ) break; + if ( match == 0 ) break; + ir = istack[jstack--]; + l = istack[jstack--]; + } else { + k = (l + ir) >> 1; /* Choose median of left, center, right */ + EXCH(k, l+1); + COMP( cv[IX(l)], cv[IX(ir)] ); + if ( match > 0 ) { + EXCH(l, ir); + } else if ( match == 0 ) { + i = ir; + break; + } + COMP( cv[IX(l+1)], cv[IX(ir)] ); + if ( match > 0 ) { + EXCH(l+1, ir); + } else if ( match == 0 ) { + i = ir; + break; + } + COMP( cv[IX(l)], cv[IX(l+1)] ); + if ( match > 0 ) { + EXCH(l, l+1); + } else if ( match == 0 ) { + i = l; + break; + } + i = l+1; + j = ir; + a = cv[IX(i)]; + for(;;) { + do { + i++; + COMP( cv[IX(i)], a ); + } while( match < 0 ); + while( match > 0 ) { + j--; + COMP( cv[IX(j)], a ); } -#endif /* SLAP_MODS_CHECK_QUICKSORT */ + if (j < i) { + match = 1; + break; + } + if ( match == 0 ) { + i = l+1; + break; + } + EXCH(i,j); + } + if ( match == 0 ) + break; + EXCH(l+1,j); + jstack += 2; + if (ir-i+1 >= j) { + istack[jstack] = ir; + istack[jstack-1] = i; + ir = j; + } else { + istack[jstack] = j; + istack[jstack-1] = l; + l = i; } } } + done: + if ( i >= 0 ) + *dup = ix[i]; + /* For sorted attributes, put the values in index order */ + if ( rc == LDAP_SUCCESS && match && + ( ad->ad_type->sat_flags & SLAP_AT_SORTED_VAL )) { + BerVarray tmpv = slap_sl_malloc( sizeof( struct berval ) * nvals, ctx ); + for ( i = 0; isml_values; + for ( i = 0; i= 0 ); + assert( i < nvals ); + return LDAP_TYPE_OR_VALUE_EXISTS; + } return LDAP_SUCCESS; } diff --git a/servers/slapd/mods.c b/servers/slapd/mods.c index bfc0cd76e6..cbb1d70300 100644 --- a/servers/slapd/mods.c +++ b/servers/slapd/mods.c @@ -56,11 +56,21 @@ modify_add_values( assert( 0 ); } + /* FIXME: Catch old code that doesn't set sm_numvals. + */ + if ( !BER_BVISNULL( &mod->sm_values[mod->sm_numvals] )) { + int i; + for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ ); + assert( mod->sm_numvals == i ); + } + /* check if values to add exist in attribute */ a = attr_find( e->e_attrs, mod->sm_desc ); if ( a != NULL ) { - int rc, i, j, p; MatchingRule *mr; + struct berval *cvals; + int rc, i, j, p; + unsigned flags; mr = mod->sm_desc->ad_type->sat_equality; if( mr == NULL || !mr->smr_match ) { @@ -74,10 +84,7 @@ modify_add_values( } if ( permissive ) { - for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ ) { - /* EMPTY -- just counting 'em */; - } - + i = mod->sm_numvals; pmod.sm_values = (BerVarray)ch_malloc( (i + 1) * sizeof( struct berval )); if ( pmod.sm_nvalues != NULL ) { @@ -92,39 +99,32 @@ modify_add_values( * server (whether from LDAP or from the underlying * database). */ - for ( p = i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ ) { - int match; + flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX; + if ( mod->sm_nvalues ) { + flags |= SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH | + SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH; + cvals = mod->sm_nvalues; + } else { + cvals = mod->sm_values; + } + for ( p = i = 0; i < mod->sm_numvals; i++ ) { + unsigned slot; - assert( a->a_vals[0].bv_val != NULL ); - for ( j = 0; !BER_BVISNULL( &a->a_vals[j] ); j++ ) { - if ( mod->sm_nvalues ) { - rc = ordered_value_match( &match, mod->sm_desc, mr, - SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX - | SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH - | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH, - &a->a_nvals[j], &mod->sm_nvalues[i], text ); - } else { - rc = ordered_value_match( &match, mod->sm_desc, mr, - SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX, - &a->a_vals[j], &mod->sm_values[i], text ); - } - - if ( rc == LDAP_SUCCESS && match == 0 ) { + rc = attr_valfind( a, flags, &cvals[i], &slot, NULL ); + if ( rc == LDAP_SUCCESS ) { + if ( !permissive ) { /* value already exists */ - if ( permissive ) break; - *text = textbuf; snprintf( textbuf, textlen, "modify/%s: %s: value #%d already exists", op, mod->sm_desc->ad_cname.bv_val, i ); return LDAP_TYPE_OR_VALUE_EXISTS; - - } else if ( rc != LDAP_SUCCESS ) { - return rc; } + } else if ( rc != LDAP_NO_SUCH_ATTRIBUTE ) { + return rc; } - if ( permissive && match != 0 ) { + if ( permissive && rc ) { if ( pmod.sm_nvalues ) { pmod.sm_nvalues[p] = mod->sm_nvalues[i]; } @@ -164,8 +164,8 @@ modify_add_values( /* this should return result of attr_merge */ *text = textbuf; snprintf( textbuf, textlen, - "modify/%s: %s: merge error", - op, mod->sm_desc->ad_cname.bv_val ); + "modify/%s: %s: merge error (%d)", + op, mod->sm_desc->ad_cname.bv_val, rc ); return LDAP_OTHER; } @@ -192,11 +192,13 @@ modify_delete_vindex( char *textbuf, size_t textlen, int *idx ) { - int i, j, k, rc = LDAP_SUCCESS; Attribute *a; MatchingRule *mr = mod->sm_desc->ad_type->sat_equality; + struct berval *cvals; + int *id2 = NULL; + int i, j, rc; + unsigned flags; char dummy = '\0'; - int match = 0; /* * If permissive is set, then the non-existence of an @@ -219,6 +221,17 @@ modify_delete_vindex( return rc; } + /* FIXME: Catch old code that doesn't set sm_numvals. + */ + if ( !BER_BVISNULL( &mod->sm_values[mod->sm_numvals] )) { + for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ ); + assert( mod->sm_numvals == i ); + } + if ( !idx ) { + id2 = ch_malloc( mod->sm_numvals * sizeof( int )); + idx = id2; + } + if( mr == NULL || !mr->smr_match ) { /* disallow specific attributes from being deleted if no equality rule */ @@ -226,102 +239,90 @@ modify_delete_vindex( snprintf( textbuf, textlen, "modify/delete: %s: no equality matching rule", mod->sm_desc->ad_cname.bv_val ); - return LDAP_INAPPROPRIATE_MATCHING; + rc = LDAP_INAPPROPRIATE_MATCHING; + goto return_result; } /* delete specific values - find the attribute first */ if ( (a = attr_find( e->e_attrs, mod->sm_desc )) == NULL ) { if( permissive ) { - return LDAP_SUCCESS; + rc = LDAP_SUCCESS; + goto return_result; } *text = textbuf; snprintf( textbuf, textlen, "modify/delete: %s: no such attribute", mod->sm_desc->ad_cname.bv_val ); - return LDAP_NO_SUCH_ATTRIBUTE; + rc = LDAP_NO_SUCH_ATTRIBUTE; + goto return_result; } + if ( mod->sm_nvalues ) { + flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX + | SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH + | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH; + cvals = mod->sm_nvalues; + } else { + flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX; + cvals = mod->sm_values; + } + + /* Locate values to delete */ for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ ) { - int found = 0; - for ( j = 0; !BER_BVISNULL( &a->a_vals[j] ); j++ ) { - /* skip already deleted values */ - if ( a->a_vals[j].bv_val == &dummy ) { + unsigned sort; + rc = attr_valfind( a, flags, &cvals[i], &sort, NULL ); + if ( rc == LDAP_SUCCESS ) { + idx[i] = sort; + } else if ( rc == LDAP_NO_SUCH_ATTRIBUTE ) { + if ( permissive ) { + idx[i] = -1; continue; } - - if( mod->sm_nvalues ) { - assert( a->a_nvals != NULL ); - rc = ordered_value_match( &match, a->a_desc, mr, - SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX - | SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH - | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH, - &a->a_nvals[j], &mod->sm_nvalues[i], text ); - } else { -#if 0 - assert( a->a_nvals == NULL ); -#endif - rc = ordered_value_match( &match, a->a_desc, mr, - SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX, - &a->a_vals[j], &mod->sm_values[i], text ); - } - - if ( rc != LDAP_SUCCESS ) { - *text = textbuf; - snprintf( textbuf, textlen, - "%s: matching rule failed", - mod->sm_desc->ad_cname.bv_val ); - break; - } - - if ( match != 0 ) { - continue; - } - - found = 1; - - if ( idx ) - idx[i] = j; - - /* delete value and mark it as dummy */ - free( a->a_vals[j].bv_val ); - a->a_vals[j].bv_val = &dummy; - if( a->a_nvals != a->a_vals ) { - free( a->a_nvals[j].bv_val ); - a->a_nvals[j].bv_val = &dummy; - } - a->a_numvals--; - - break; - } - - if ( found == 0 ) { *text = textbuf; snprintf( textbuf, textlen, "modify/delete: %s: no such value", mod->sm_desc->ad_cname.bv_val ); - rc = LDAP_NO_SUCH_ATTRIBUTE; - if ( i > 0 ) { - break; - } else { - goto return_results; - } + goto return_result; + } else { + *text = textbuf; + snprintf( textbuf, textlen, + "modify/delete: %s: matching rule failed", + mod->sm_desc->ad_cname.bv_val ); + goto return_result; } } + /* Delete the values */ + for ( i = 0; i < mod->sm_numvals; i++ ) { + /* Skip permissive values that weren't found */ + if ( idx[i] < 0 ) + continue; + /* Skip duplicate delete specs */ + if ( a->a_vals[idx[i]].bv_val == &dummy ) + continue; + /* delete value and mark it as gone */ + free( a->a_vals[idx[i]].bv_val ); + a->a_vals[idx[i]].bv_val = &dummy; + if( a->a_nvals != a->a_vals ) { + free( a->a_nvals[idx[i]].bv_val ); + a->a_nvals[idx[i]].bv_val = &dummy; + } + a->a_numvals--; + } + /* compact array skipping dummies */ - for ( k = 0, j = 0; !BER_BVISNULL( &a->a_vals[k] ); k++ ) { + for ( i = 0, j = 0; !BER_BVISNULL( &a->a_vals[i] ); i++ ) { /* skip dummies */ - if( a->a_vals[k].bv_val == &dummy ) { - assert( a->a_nvals[k].bv_val == &dummy ); + if( a->a_vals[i].bv_val == &dummy ) { + assert( a->a_nvals[i].bv_val == &dummy ); continue; } - if ( j != k ) { - a->a_vals[ j ] = a->a_vals[ k ]; + if ( j != i ) { + a->a_vals[ j ] = a->a_vals[ i ]; if (a->a_nvals != a->a_vals) { - a->a_nvals[ j ] = a->a_nvals[ k ]; + a->a_nvals[ j ] = a->a_nvals[ i ]; } } - j++; } @@ -331,8 +332,9 @@ modify_delete_vindex( } /* if no values remain, delete the entire attribute */ - if ( BER_BVISNULL( &a->a_vals[0] ) ) { + if ( !a->a_numvals ) { if ( attr_delete( &e->e_attrs, mod->sm_desc ) ) { + /* Can never happen */ *text = textbuf; snprintf( textbuf, textlen, "modify/delete: %s: no such attribute", @@ -343,9 +345,9 @@ modify_delete_vindex( /* For an ordered attribute, renumber the value indices */ ordered_value_sort( a, 1 ); } - -return_results:; - +return_result: + if ( id2 ) + ch_free( id2 ); return rc; } diff --git a/servers/slapd/overlays/dynlist.c b/servers/slapd/overlays/dynlist.c index 7e8a50ca3b..1b58fe4bef 100644 --- a/servers/slapd/overlays/dynlist.c +++ b/servers/slapd/overlays/dynlist.c @@ -189,6 +189,7 @@ dynlist_sc_update( Operation *op, SlapReply *rs ) mod.sm_type = dlc->dlc_dli->dli_member_ad->ad_cname; mod.sm_values = vals; mod.sm_nvalues = nvals; + mod.sm_numvals = 1; (void)modify_add_values( e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) ); @@ -292,6 +293,7 @@ dynlist_sc_update( Operation *op, SlapReply *rs ) mod.sm_type = a->a_desc->ad_cname; mod.sm_values = vals; mod.sm_nvalues = nvals; + mod.sm_numvals = j; (void)modify_add_values( e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) ); diff --git a/servers/slapd/overlays/memberof.c b/servers/slapd/overlays/memberof.c index 7d56d9789e..40cf08b930 100644 --- a/servers/slapd/overlays/memberof.c +++ b/servers/slapd/overlays/memberof.c @@ -416,6 +416,7 @@ memberof_value_modify( op2.o_ndn = op->o_bd->be_rootndn; ml = &mod[ 0 ]; + ml->sml_numvals = 1; ml->sml_values = &values[ 0 ]; ml->sml_values[ 0 ] = mo->mo_dn; BER_BVZERO( &ml->sml_values[ 1 ] ); @@ -430,6 +431,7 @@ memberof_value_modify( op2.orm_modlist = ml; ml = &mod[ 1 ]; + ml->sml_numvals = 1; ml->sml_values = &values[ 2 ]; BER_BVZERO( &ml->sml_values[ 1 ] ); ml->sml_nvalues = &nvalues[ 2 ]; @@ -812,6 +814,7 @@ memberof_op_modify( Operation *op, SlapReply *rs ) BER_BVZERO( &ml->sml_values[ i ] ); ber_memfree( ml->sml_nvalues[ i ].bv_val ); BER_BVZERO( &ml->sml_nvalues[ i ] ); + ml->sml_numvals--; if ( j - i == 1 ) { break; } @@ -909,6 +912,7 @@ memberof_op_modify( Operation *op, SlapReply *rs ) ber_memfree( ml->sml_nvalues[ i ].bv_val ); BER_BVZERO( &ml->sml_nvalues[ i ] ); } + ml->sml_numvals--; if ( j - i == 1 ) { break; } @@ -1019,6 +1023,7 @@ memberof_op_modify( Operation *op, SlapReply *rs ) ber_memfree( ml->sml_nvalues[ i ].bv_val ); BER_BVZERO( &ml->sml_nvalues[ i ] ); } + ml->sml_numvals--; if ( j - i == 1 ) { break; } diff --git a/servers/slapd/overlays/syncprov.c b/servers/slapd/overlays/syncprov.c index 72d2b330cc..3cdcbf0c78 100644 --- a/servers/slapd/overlays/syncprov.c +++ b/servers/slapd/overlays/syncprov.c @@ -1287,6 +1287,7 @@ syncprov_checkpoint( Operation *op, SlapReply *rs, slap_overinst *on ) SlapReply rsm = { 0 }; slap_callback cb = {0}; + mod.sml_numvals = si->si_numcsns; mod.sml_values = si->si_ctxcsn; mod.sml_nvalues = NULL; mod.sml_desc = slap_schema.si_ad_contextCSN; diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h index 0dfce791c8..fbbc9ae675 100644 --- a/servers/slapd/proto-slap.h +++ b/servers/slapd/proto-slap.h @@ -268,6 +268,11 @@ LDAP_SLAPD_F (void) comp_tree_free LDAP_P(( Attribute *a )); LDAP_SLAPD_F (Attribute *) attr_alloc LDAP_P(( AttributeDescription *ad )); LDAP_SLAPD_F (Attribute *) attrs_alloc LDAP_P(( int num )); LDAP_SLAPD_F (int) attr_prealloc LDAP_P(( int num )); +LDAP_SLAPD_F (int) attr_valfind LDAP_P(( Attribute *a, + unsigned flags, + struct berval *val, + unsigned *slot, + void *ctx )); LDAP_SLAPD_F (int) attr_valadd LDAP_P(( Attribute *a, BerVarray vals, BerVarray nvals, @@ -1166,6 +1171,12 @@ LDAP_SLAPD_F( int ) slap_mods_check( const char **text, char *textbuf, size_t textlen, void *ctx ); +LDAP_SLAPD_F( int ) slap_sort_vals( + Modifications *ml, + const char **text, + int *dup, + void *ctx ); + LDAP_SLAPD_F( void ) slap_timestamp( time_t *tm, struct berval *bv ); diff --git a/servers/slapd/slap.h b/servers/slapd/slap.h index 7707748146..bbcc3437b8 100644 --- a/servers/slapd/slap.h +++ b/servers/slapd/slap.h @@ -690,10 +690,19 @@ struct AttributeType { #define SLAP_AT_MANAGEABLE 0x0800U /* no-user-mod can be by-passed */ +/* Note: ORDERED values have an ordering specifically set by the + * user, denoted by the {x} ordering prefix on the values. + * + * SORTED values are simply sorted by memcmp. SORTED values can + * be efficiently located by binary search. ORDERED values have no + * such advantage. An attribute cannot have both properties. + */ #define SLAP_AT_ORDERED_VAL 0x0001U /* values are ordered */ #define SLAP_AT_ORDERED_SIB 0x0002U /* siblings are ordered */ #define SLAP_AT_ORDERED 0x0003U /* value has order index */ +#define SLAP_AT_SORTED_VAL 0x0010U /* values should be sorted */ + #define SLAP_AT_HARDCODE 0x10000U /* hardcoded schema */ #define SLAP_AT_DELETED 0x20000U @@ -1109,22 +1118,23 @@ struct ValuesReturnFilter { /* * represents an attribute (description + values) + * desc, vals, nvals, numvals fields must align with Modification */ struct Attribute { AttributeDescription *a_desc; BerVarray a_vals; /* preserved values */ BerVarray a_nvals; /* normalized values */ -#ifdef LDAP_COMP_MATCH - ComponentData *a_comp_data; /* component values */ -#endif - Attribute *a_next; unsigned a_numvals; /* number of vals */ unsigned a_flags; #define SLAP_ATTR_IXADD 0x1U #define SLAP_ATTR_IXDEL 0x2U #define SLAP_ATTR_DONT_FREE_DATA 0x4U #define SLAP_ATTR_DONT_FREE_VALS 0x8U -#define SLAP_ATTR_SORTED_VALS 0x10U +#define SLAP_ATTR_SORTED_VALS 0x10U /* values are sorted */ + Attribute *a_next; +#ifdef LDAP_COMP_MATCH + ComponentData *a_comp_data; /* component values */ +#endif }; @@ -1171,8 +1181,13 @@ struct Entry { /* * A list of LDAPMods + * desc, values, nvalues, numvals must align with Attribute */ struct Modification { + AttributeDescription *sm_desc; + BerVarray sm_values; + BerVarray sm_nvalues; + unsigned sm_numvals; short sm_op; short sm_flags; /* Set for internal mods, will bypass ACL checks. Only needed when @@ -1180,12 +1195,7 @@ struct Modification { */ #define SLAP_MOD_INTERNAL 0x01 #define SLAP_MOD_MANAGING 0x02 - - AttributeDescription *sm_desc; struct berval sm_type; - BerVarray sm_values; - BerVarray sm_nvalues; - unsigned sm_numvals; }; struct Modifications { diff --git a/servers/slapd/slapi/slapi_utils.c b/servers/slapd/slapi/slapi_utils.c index f676e5402d..446fd80f4c 100644 --- a/servers/slapd/slapi/slapi_utils.c +++ b/servers/slapd/slapi/slapi_utils.c @@ -72,7 +72,8 @@ static int checkBVString(const struct berval *bv) int bvptr2obj( struct berval **bvptr, - BerVarray *bvobj ) + BerVarray *bvobj, + unsigned *num ) { int rc = LDAP_SUCCESS; int i; @@ -85,6 +86,8 @@ bvptr2obj( for ( i = 0; bvptr != NULL && bvptr[i] != NULL; i++ ) { ; /* EMPTY */ } + if ( num ) + *num = i; tmpberval = (BerVarray)slapi_ch_malloc( (i + 1)*sizeof(struct berval)); if ( tmpberval == NULL ) { @@ -231,12 +234,12 @@ slapi_entry_attr_merge( return -1; } - rc = bvptr2obj( vals, &bv ); + rc = bvptr2obj( vals, &bv, NULL ); if ( rc != LDAP_SUCCESS ) { return -1; } - rc = attr_merge_normalize_one( e, ad, bv, NULL ); + rc = attr_merge_normalize( e, ad, bv, NULL ); ch_free( bv ); return rc; @@ -545,9 +548,10 @@ slapi_entry_add_values( Slapi_Entry *e, const char *type, struct berval **vals ) * FIXME: sm_values = NULL ? */ mod.sm_values = (BerVarray)ch_malloc( sizeof(struct berval) ); mod.sm_values->bv_val = NULL; + mod.sm_numvals = 0; } else { - rc = bvptr2obj( vals, &mod.sm_values ); + rc = bvptr2obj( vals, &mod.sm_values, &mod.sm_numvals ); if ( rc != LDAP_SUCCESS ) { return LDAP_CONSTRAINT_VIOLATION; } @@ -611,7 +615,7 @@ slapi_entry_delete_values( Slapi_Entry *e, const char *type, struct berval **val return attr_delete( &e->e_attrs, mod.sm_desc ) ? LDAP_OTHER : LDAP_SUCCESS; } - rc = bvptr2obj( vals, &mod.sm_values ); + rc = bvptr2obj( vals, &mod.sm_values, &mod.sm_numvals ); if ( rc != LDAP_SUCCESS ) { return LDAP_CONSTRAINT_VIOLATION; } @@ -730,7 +734,7 @@ slapi_entry_attr_replace_sv( Slapi_Entry *e, const char *type, Slapi_Value **val attr_delete( &e->e_attrs, ad ); - rc = bvptr2obj( vals, &bv ); + rc = bvptr2obj( vals, &bv, NULL ); if ( rc != LDAP_SUCCESS ) { return -1; } @@ -1350,7 +1354,7 @@ slapi_send_ldap_result( if ( pb->pb_op->o_tag == LDAP_REQ_SEARCH ) rs->sr_nentries = nentries; if ( urls != NULL ) - bvptr2obj( urls, &rs->sr_ref ); + bvptr2obj( urls, &rs->sr_ref, NULL ); send_ldap_result( pb->pb_op, rs ); @@ -1429,7 +1433,7 @@ slapi_send_ldap_search_reference( rs.sr_matched = NULL; rs.sr_text = NULL; - rc = bvptr2obj( references, &rs.sr_ref ); + rc = bvptr2obj( references, &rs.sr_ref, NULL ); if ( rc != LDAP_SUCCESS ) { return rc; } @@ -1440,7 +1444,7 @@ slapi_send_ldap_search_reference( rs.sr_entry = e; if ( v2refs != NULL ) { - rc = bvptr2obj( v2refs, &rs.sr_v2ref ); + rc = bvptr2obj( v2refs, &rs.sr_v2ref, NULL ); if ( rc != LDAP_SUCCESS ) { slapi_ch_free( (void **)&rs.sr_ref ); return rc; diff --git a/servers/slapd/syncrepl.c b/servers/slapd/syncrepl.c index 2ab959c858..ea94e4e279 100644 --- a/servers/slapd/syncrepl.c +++ b/servers/slapd/syncrepl.c @@ -2567,6 +2567,7 @@ syncrepl_updateCookie( mod[0].sml_type = mod[0].sml_desc->ad_cname; mod[0].sml_values = NULL; mod[0].sml_nvalues = NULL; + mod[0].sml_numvals = 0; mod[0].sml_next = &mod[1]; mod[1].sml_op = LDAP_MOD_ADD; @@ -2574,6 +2575,7 @@ syncrepl_updateCookie( mod[1].sml_type = mod[0].sml_desc->ad_cname; mod[1].sml_values = NULL; mod[1].sml_nvalues = NULL; + mod[1].sml_numvals = 0; mod[1].sml_next = NULL; ldap_pvt_thread_mutex_lock( &si->si_cookieState->cs_mutex ); @@ -2589,8 +2591,10 @@ syncrepl_updateCookie( si->si_cookieState->cs_vals[j].bv_val, len ) > 0 ) { ber_bvarray_add_x( &mod[0].sml_values, &si->si_cookieState->cs_vals[j], op->o_tmpmemctx ); + mod[0].sml_numvals++; ber_bvarray_add_x( &mod[1].sml_values, &syncCookie->ctxcsn[i], op->o_tmpmemctx ); + mod[1].sml_numvals++; if ( BER_BVISNULL( &first )) first = syncCookie->ctxcsn[i]; } @@ -2600,6 +2604,7 @@ syncrepl_updateCookie( if ( j == si->si_cookieState->cs_num ) { ber_bvarray_add_x( &mod[1].sml_values, &syncCookie->ctxcsn[i], op->o_tmpmemctx ); + mod[1].sml_numvals++; if ( BER_BVISNULL( &first )) first = syncCookie->ctxcsn[i]; }