]> git.sur5r.net Git - openldap/blob - servers/slapd/mods.c
37392a41b94cafcf0434367b489c49029a2d2b53
[openldap] / servers / slapd / mods.c
1 /*
2  * Copyright 1998-2003 The OpenLDAP Foundation, All Rights Reserved.
3  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
4  */
5 /*
6  * Copyright (c) 1995 Regents of the University of Michigan.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms are permitted
10  * provided that this notice is preserved and that due credit is given
11  * to the University of Michigan at Ann Arbor. The name of the University
12  * may not be used to endorse or promote products derived from this
13  * software without specific prior written permission. This software
14  * is provided ``as is'' without express or implied warranty.
15  */
16
17 #include "portable.h"
18
19 #include "slap.h"
20
21 int
22 modify_check_duplicates(
23         AttributeDescription    *ad,
24         MatchingRule            *mr,
25         BerVarray               vals,
26         BerVarray               mods,
27         int                     permissive,
28         const char      **text,
29         char *textbuf, size_t textlen )
30 {
31         int             i, j, numvals = 0, nummods,
32                         rc = LDAP_SUCCESS, matched;
33         BerVarray       nvals = NULL, nmods = NULL;
34
35         /*
36          * FIXME: better do the following
37          * 
38          *   - count the existing values
39          *   - count the new values
40          *   
41          *   - if the existing values are less than the new ones {
42          *       - normalize all the existing values
43          *       - for each new value {
44          *           - normalize
45          *           - check with existing
46          *           - cross-check with already normalized new vals
47          *       }
48          *   } else {
49          *       - for each new value {
50          *           - normalize
51          *           - cross-check with already normalized new vals
52          *       }
53          *       - for each existing value {
54          *           - normalize
55          *           - check with already normalized new values
56          *       }
57          *   }
58          *
59          * The first case is good when adding a lot of new values,
60          * and significantly at first import of values (e.g. adding
61          * a new group); the latter case seems to be quite important
62          * as well, because it is likely to be the most frequently
63          * used when administering the entry.  The current 
64          * implementation will always normalize all the existing
65          * values before checking.  If there's no duplicate, the
66          * performances should not change; they will in case of error.
67          */
68
69         for ( nummods = 0; mods[ nummods ].bv_val != NULL; nummods++ )
70                 /* count new values */ ;
71
72         if ( vals ) {
73                 for ( numvals = 0; vals[ numvals ].bv_val != NULL; numvals++ )
74                         /* count existing values */ ;
75
76                 if ( numvals < nummods ) {
77                         nvals = SLAP_CALLOC( numvals + 1, sizeof( struct berval ) );
78                         if( nvals == NULL ) {
79 #ifdef NEW_LOGGING
80                                 LDAP_LOG( OPERATION, ERR,
81                                         "modify_check_duplicates: SLAP_CALLOC failed", 0, 0, 0 );
82 #else
83                                 Debug( LDAP_DEBUG_ANY, 
84                                         "modify_check_duplicates: SLAP_CALLOC failed", 0, 0, 0 );
85 #endif
86                                 goto return_results;
87                         }
88
89                         /* normalize the existing values first */
90                         for ( j = 0; vals[ j ].bv_val != NULL; j++ ) {
91                                 rc = value_normalize( ad, SLAP_MR_EQUALITY,
92                                         &vals[ j ], &nvals[ j ], text );
93
94                                 /* existing attribute values must normalize */
95                                 assert( rc == LDAP_SUCCESS );
96
97                                 if ( rc != LDAP_SUCCESS ) {
98                                         nvals[ j ].bv_val = NULL;
99                                         goto return_results;
100                                 }
101                         }
102                         nvals[ j ].bv_val = NULL;
103                 }
104         }
105
106         /*
107          * If the existing values are less than the new values,
108          * it is more convenient to normalize all the existing
109          * values and test each new value against them first,
110          * then to other already normalized values
111          */
112         nmods = SLAP_CALLOC( nummods + 1, sizeof( struct berval ) );
113         if ( nmods == NULL ) {
114 #ifdef NEW_LOGGING
115                 LDAP_LOG( OPERATION, ERR,
116                         "modify_check_duplicates: SLAP_CALLOC failed", 0, 0, 0 );
117 #else
118                 Debug( LDAP_DEBUG_ANY, 
119                         "modify_check_duplicates: SLAP_CALLOC failed", 0, 0, 0 );
120 #endif
121                 goto return_results;
122         }
123
124         for ( i = 0; mods[ i ].bv_val != NULL; i++ ) {
125                 rc = value_normalize( ad, SLAP_MR_EQUALITY,
126                         &mods[ i ], &nmods[ i ], text );
127
128                 if ( rc != LDAP_SUCCESS ) {
129                         nmods[ i ].bv_val = NULL;
130                         goto return_results;
131                 }
132
133                 if ( numvals > 0 && numvals < nummods ) {
134                         for ( matched = 0, j = 0; nvals[ j ].bv_val; j++ ) {
135                                 int match;
136
137                                 rc = (*mr->smr_match)( &match,
138                                         SLAP_MR_ATTRIBUTE_SYNTAX_MATCH,
139                                         ad->ad_type->sat_syntax,
140                                         mr, &nmods[ i ], &nvals[ j ] );
141                                 if ( rc != LDAP_SUCCESS ) {
142                                         nmods[ i + 1 ].bv_val = NULL;
143                                         *text = textbuf;
144                                         snprintf( textbuf, textlen,
145                                                 "%s: matching rule failed",
146                                                 ad->ad_cname.bv_val );
147                                         goto return_results;
148                                 }
149
150                                 if ( match == 0 ) {
151                                         if ( permissive ) {
152                                                 matched++;
153                                                 continue;
154                                         }
155                                         *text = textbuf;
156                                         snprintf( textbuf, textlen,
157                                                 "%s: value #%d provided more than once",
158                                                 ad->ad_cname.bv_val, i );
159                                         rc = LDAP_TYPE_OR_VALUE_EXISTS;
160                                         nmods[ i + 1 ].bv_val = NULL;
161                                         goto return_results;
162                                 }
163                         }
164
165                         if ( permissive && matched == j ) {
166                                 nmods[ i + 1 ].bv_val = NULL;
167                                 rc = LDAP_TYPE_OR_VALUE_EXISTS;
168                                 goto return_results;
169                         }
170                 }
171         
172                 for ( matched = 0, j = 0; j < i; j++ ) {
173                         int match;
174
175                         rc = (*mr->smr_match)( &match,
176                                 SLAP_MR_ATTRIBUTE_SYNTAX_MATCH,
177                                 ad->ad_type->sat_syntax,
178                                 mr, &nmods[ i ], &nmods[ j ] );
179                         if ( rc != LDAP_SUCCESS ) {
180                                 nmods[ i + 1 ].bv_val = NULL;
181                                 *text = textbuf;
182                                 snprintf( textbuf, textlen,
183                                         "%s: matching rule failed",
184                                         ad->ad_cname.bv_val );
185                                 goto return_results;
186                         }
187
188                         if ( match == 0 ) {
189                                 if ( permissive ) {
190                                         matched++;
191                                         continue;
192                                 }
193                                 *text = textbuf;
194                                 snprintf( textbuf, textlen,
195                                         "%s: value #%d provided more than once",
196                                         ad->ad_cname.bv_val, j );
197                                 rc = LDAP_TYPE_OR_VALUE_EXISTS;
198                                 nmods[ i + 1 ].bv_val = NULL;
199                                 goto return_results;
200                         }
201                 }
202
203                 if ( permissive && matched == j ) {
204                         nmods[ i + 1 ].bv_val = NULL;
205                         rc = LDAP_TYPE_OR_VALUE_EXISTS;
206                         goto return_results;
207                 }
208         }
209         nmods[ i ].bv_val = NULL;
210
211         /*
212          * if new values are more than existing values, it is more
213          * convenient to normalize and check all new values first,
214          * then check each new value against existing values, which 
215          * can be normalized in place
216          */
217
218         if ( numvals >= nummods ) {
219                 for ( j = 0; vals[ j ].bv_val; j++ ) {
220                         struct berval   asserted;
221
222                         rc = value_normalize( ad, SLAP_MR_EQUALITY,
223                                 &vals[ j ], &asserted, text );
224
225                         if ( rc != LDAP_SUCCESS ) {
226                                 goto return_results;
227                         }
228
229                         for ( matched = 0, i = 0; nmods[ i ].bv_val; i++ ) {
230                                 int match;
231
232                                 rc = (*mr->smr_match)( &match,
233                                         SLAP_MR_ATTRIBUTE_SYNTAX_MATCH,
234                                         ad->ad_type->sat_syntax,
235                                         mr, &nmods[ i ], &asserted );
236                                 if ( rc != LDAP_SUCCESS ) {
237                                         *text = textbuf;
238                                         snprintf( textbuf, textlen,
239                                                 "%s: matching rule failed",
240                                                 ad->ad_cname.bv_val );
241                                         goto return_results;
242                                 }
243
244                                 if ( match == 0 ) {
245                                         if ( permissive ) {
246                                                 matched++;
247                                                 continue;
248                                         }
249                                         *text = textbuf;
250                                         snprintf( textbuf, textlen,
251                                                 "%s: value #%d provided more than once",
252                                                 ad->ad_cname.bv_val, j );
253                                         rc = LDAP_TYPE_OR_VALUE_EXISTS;
254                                         goto return_results;
255                                 }
256                         }
257
258                         if ( permissive && matched == i ) {
259                                 rc = LDAP_TYPE_OR_VALUE_EXISTS;
260                                 goto return_results;
261                         }
262                 }
263         }
264
265 return_results:;
266         if ( nvals ) {
267                 ber_bvarray_free( nvals );
268         }
269         if ( nmods ) {
270                 ber_bvarray_free( nmods );
271         }
272
273         return rc;
274 }
275
276 int
277 modify_add_values(
278         Entry   *e,
279         Modification    *mod,
280         int     permissive,
281         const char      **text,
282         char *textbuf, size_t textlen
283 )
284 {
285         int             i, j;
286         int             matched;
287         Attribute       *a;
288         MatchingRule *mr = mod->sm_desc->ad_type->sat_equality;
289         const char *op;
290
291         switch( mod->sm_op ) {
292         case LDAP_MOD_ADD:
293                 op = "add";
294                 break;
295         case LDAP_MOD_REPLACE:
296                 op = "replace";
297                 break;
298         default:
299                 op = "?";
300                 assert( 0 );
301         }
302
303         a = attr_find( e->e_attrs, mod->sm_desc );
304
305         /*
306          * With permissive set, as long as the attribute being added
307          * has the same value(s?) as the existing attribute, then the
308          * modify will succeed.
309          */
310
311         /* check if the values we're adding already exist */
312         if( mr == NULL || !mr->smr_match ) {
313                 if ( a != NULL ) {
314                         /* do not allow add of additional attribute
315                                 if no equality rule exists */
316                         *text = textbuf;
317                         snprintf( textbuf, textlen,
318                                 "modify/%s: %s: no equality matching rule",
319                                 op, mod->sm_desc->ad_cname.bv_val );
320                         return LDAP_INAPPROPRIATE_MATCHING;
321                 }
322
323                 for ( i = 0; mod->sm_bvalues[i].bv_val != NULL; i++ ) {
324                         /* test asserted values against existing values */
325                         if( a ) {
326                                 for( matched = 0, j = 0; a->a_vals[j].bv_val != NULL; j++ ) {
327                                         if ( bvmatch( &mod->sm_bvalues[i],
328                                                 &a->a_vals[j] ) ) {
329                                                 if ( permissive ) {
330                                                         matched++;
331                                                         continue;
332                                                 }
333                                                 /* value exists already */
334                                                 *text = textbuf;
335                                                 snprintf( textbuf, textlen,
336                                                         "modify/%s: %s: value #%i already exists",
337                                                         op, mod->sm_desc->ad_cname.bv_val, j );
338                                                 return LDAP_TYPE_OR_VALUE_EXISTS;
339                                         }
340                                 }
341                                 if ( permissive && matched == j ) {
342                                         /* values already exist; do nothing */
343                                         return LDAP_SUCCESS;
344                                 }
345                         }
346
347                         /* test asserted values against themselves */
348                         for( j = 0; j < i; j++ ) {
349                                 if ( bvmatch( &mod->sm_bvalues[i],
350                                         &mod->sm_bvalues[j] ) ) {
351
352                                         /* value exists already */
353                                         *text = textbuf;
354                                         snprintf( textbuf, textlen,
355                                                 "modify/%s: %s: value #%i already exists",
356                                                 op, mod->sm_desc->ad_cname.bv_val, j );
357                                         return LDAP_TYPE_OR_VALUE_EXISTS;
358                                 }
359                         }
360                 }
361
362         } else {
363                 /*
364                  * The original code performs ( n ) normalizations 
365                  * and ( n * ( n - 1 ) / 2 ) matches, which hide
366                  * the same number of normalizations.  The new code
367                  * performs the same number of normalizations ( n )
368                  * and ( n * ( n - 1 ) / 2 ) mem compares, far less
369                  * expensive than an entire match, if a match is
370                  * equivalent to a normalization and a mem compare ...
371                  * 
372                  * This is far more memory expensive than the previous,
373                  * but it can heavily improve performances when big
374                  * chunks of data are added (typical example is a group
375                  * with thousands of DN-syntax members; on my system:
376                  * for members of 5-RDN DNs,
377
378                 members         orig            bvmatch (dirty) new
379                 1000            0m38.456s       0m0.553s        0m0.608s
380                 2000            2m33.341s       0m0.851s        0m1.003s
381
382                  * Moreover, 100 groups with 10000 members each were
383                  * added in 37m27.933s (an analogous LDIF file was
384                  * loaded into Active Directory in 38m28.682s, BTW).
385                  * 
386                  * Maybe we could switch to the new algorithm when
387                  * the number of values overcomes a given threshold?
388                  */
389
390                 int             rc;
391
392                 if ( mod->sm_bvalues[ 1 ].bv_val == 0 ) {
393                         if ( a != NULL ) {
394                                 struct berval   asserted;
395                                 int             i;
396
397                                 rc = value_normalize( mod->sm_desc, SLAP_MR_EQUALITY,
398                                         &mod->sm_bvalues[ 0 ], &asserted, text );
399
400                                 if ( rc != LDAP_SUCCESS ) {
401                                         return rc;
402                                 }
403
404                                 for ( matched = 0, i = 0; a->a_vals[ i ].bv_val; i++ ) {
405                                         int     match;
406
407                                         rc = value_match( &match, mod->sm_desc, mr,
408                                                 SLAP_MR_ATTRIBUTE_SYNTAX_MATCH,
409                                                 &a->a_vals[ i ], &asserted, text );
410
411                                         if( rc == LDAP_SUCCESS && match == 0 ) {
412                                                 if ( permissive ) {
413                                                         matched++;
414                                                         continue;
415                                                 }
416                                                 free( asserted.bv_val );
417                                                 *text = textbuf;
418                                                 snprintf( textbuf, textlen,
419                                                         "modify/%s: %s: value #0 already exists",
420                                                         op, mod->sm_desc->ad_cname.bv_val, 0 );
421                                                 return LDAP_TYPE_OR_VALUE_EXISTS;
422                                         }
423                                 }
424                                 if ( permissive && matched == i ) {
425                                         /* values already exist; do nothing */
426                                         return LDAP_SUCCESS;
427                                 }
428                         }
429
430                 } else {
431                         rc = modify_check_duplicates( mod->sm_desc, mr,
432                                         a ? a->a_vals : NULL, mod->sm_bvalues,
433                                         permissive,
434                                         text, textbuf, textlen );
435
436                         if ( permissive && rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
437                                 return LDAP_SUCCESS;
438                         }
439
440                         if ( rc != LDAP_SUCCESS ) {
441                                 return rc;
442                         }
443                 }
444         }
445
446         /* no - add them */
447 #ifdef SLAP_NVALUES
448         if( attr_merge( e, mod->sm_desc, mod->sm_values, mod->sm_nvalues ) != 0 )
449 #else
450         if( attr_merge( e, mod->sm_desc, mod->sm_bvalues ) != 0 )
451 #endif
452         {
453                 /* this should return result of attr_merge */
454                 *text = textbuf;
455                 snprintf( textbuf, textlen,
456                         "modify/%s: %s: merge error",
457                         op, mod->sm_desc->ad_cname.bv_val );
458                 return LDAP_OTHER;
459         }
460
461         return LDAP_SUCCESS;
462 }
463
464 int
465 modify_delete_values(
466         Entry   *e,
467         Modification    *mod,
468         int     permissive,
469         const char      **text,
470         char *textbuf, size_t textlen
471 )
472 {
473         int             i, j, k, rc = LDAP_SUCCESS;
474         Attribute       *a;
475         MatchingRule    *mr = mod->sm_desc->ad_type->sat_equality;
476         BerVarray       nvals = NULL;
477         char            dummy = '\0';
478
479         /*
480          * If permissive is set, then the non-existence of an 
481          * attribute is not treated as an error.
482          */
483
484         /* delete the entire attribute */
485         if ( mod->sm_bvalues == NULL ) {
486                 rc = attr_delete( &e->e_attrs, mod->sm_desc );
487
488                 if( permissive ) {
489                         rc = LDAP_SUCCESS;
490                 } else if( rc != LDAP_SUCCESS ) {
491                         *text = textbuf;
492                         snprintf( textbuf, textlen,
493                                 "modify/delete: %s: no such attribute",
494                                 mod->sm_desc->ad_cname.bv_val );
495                         rc = LDAP_NO_SUCH_ATTRIBUTE;
496                 }
497                 return rc;
498         }
499
500         if( mr == NULL || !mr->smr_match ) {
501                 /* disallow specific attributes from being deleted if
502                         no equality rule */
503                 *text = textbuf;
504                 snprintf( textbuf, textlen,
505                         "modify/delete: %s: no equality matching rule",
506                         mod->sm_desc->ad_cname.bv_val );
507                 return LDAP_INAPPROPRIATE_MATCHING;
508         }
509
510         /* delete specific values - find the attribute first */
511         if ( (a = attr_find( e->e_attrs, mod->sm_desc )) == NULL ) {
512                 if( permissive ) {
513                         return LDAP_SUCCESS;
514                 }
515                 *text = textbuf;
516                 snprintf( textbuf, textlen,
517                         "modify/delete: %s: no such attribute",
518                         mod->sm_desc->ad_cname.bv_val );
519                 return LDAP_NO_SUCH_ATTRIBUTE;
520         }
521
522         /* find each value to delete */
523         for ( j = 0; a->a_vals[ j ].bv_val != NULL; j++ )
524                 /* count existing values */ ;
525
526         nvals = (BerVarray)SLAP_CALLOC( j + 1, sizeof ( struct berval ) );
527         if( nvals == NULL ) {
528 #ifdef NEW_LOGGING
529                 LDAP_LOG( OPERATION, ERR,
530                         "modify_delete_values: SLAP_CALLOC failed", 0, 0, 0 );
531 #else
532                 Debug( LDAP_DEBUG_ANY, 
533                         "modify_delete_values: SLAP_CALLOC failed", 0, 0, 0 );
534 #endif
535                                 goto return_results;
536         }
537
538         /* normalize existing values */
539         for ( j = 0; a->a_vals[ j ].bv_val != NULL; j++ ) {
540                 rc = value_normalize( a->a_desc, SLAP_MR_EQUALITY,
541                         &a->a_vals[ j ], &nvals[ j ], text );
542
543                 if ( rc != LDAP_SUCCESS ) {
544                         nvals[ j ].bv_val = NULL;
545                         goto return_results;
546                 }
547         }
548
549         for ( i = 0; mod->sm_bvalues[ i ].bv_val != NULL; i++ ) {
550                 struct  berval asserted;
551                 int     found = 0;
552
553                 /* normalize the value to be deleted */
554                 rc = value_normalize( mod->sm_desc, SLAP_MR_EQUALITY,
555                         &mod->sm_bvalues[ i ], &asserted, text );
556
557                 if( rc != LDAP_SUCCESS ) {
558                         goto return_results;
559                 }
560
561                 /* search it */
562                 for ( j = 0; nvals[ j ].bv_val != NULL; j++ ) {
563                         int match;
564
565                         if ( nvals[ j ].bv_val == &dummy ) {
566                                 continue;
567                         }
568
569                         rc = (*mr->smr_match)( &match,
570                                 SLAP_MR_ATTRIBUTE_SYNTAX_MATCH,
571                                 a->a_desc->ad_type->sat_syntax,
572                                 mr, &nvals[ j ], &asserted );
573
574                         if ( rc != LDAP_SUCCESS ) {
575                                 free( asserted.bv_val );
576                                 *text = textbuf;
577                                 snprintf( textbuf, textlen,
578                                         "%s: matching rule failed",
579                                         mod->sm_desc->ad_cname.bv_val );
580                                 goto return_results;
581                         }
582
583                         if ( match != 0 ) {
584                                 continue;
585                         }
586
587                         found = 1;
588
589                         /* delete value and mark it as dummy */
590                         free( nvals[ j ].bv_val );
591                         nvals[ j ].bv_val = &dummy;
592
593                         break;
594                 }
595
596                 free( asserted.bv_val );
597
598                 if ( found == 0 ) {
599                         *text = textbuf;
600                         snprintf( textbuf, textlen,
601                                 "modify/delete: %s: no such value",
602                                 mod->sm_desc->ad_cname.bv_val );
603                         rc = LDAP_NO_SUCH_ATTRIBUTE;
604                         goto return_results;
605                 }
606         }
607
608         /* compact array skipping dummies */
609         for ( k = 0, j = 0; nvals[ k ].bv_val != NULL; j++, k++ ) {
610
611                 /* delete and skip dummies */ ;
612                 for ( ; nvals[ k ].bv_val == &dummy; k++ ) {
613                         free( a->a_vals[ k ].bv_val );
614                 }
615
616                 if ( j != k ) {
617                         a->a_vals[ j ] = a->a_vals[ k ];
618                 }
619
620                 if ( a->a_vals[ k ].bv_val == NULL ) {
621                         break;
622                 }
623         }
624         a->a_vals[ j ].bv_val = NULL;
625
626         assert( i == k - j );
627
628         /* if no values remain, delete the entire attribute */
629         if ( a->a_vals[0].bv_val == NULL ) {
630                 if ( attr_delete( &e->e_attrs, mod->sm_desc ) ) {
631                         *text = textbuf;
632                         snprintf( textbuf, textlen,
633                                 "modify/delete: %s: no such attribute",
634                                 mod->sm_desc->ad_cname.bv_val );
635                         rc = LDAP_NO_SUCH_ATTRIBUTE;
636                 }
637         }
638
639 return_results:;
640         if ( nvals ) {
641                 /* delete the remaining normalized values */
642                 for ( j = 0; nvals[ j ].bv_val != NULL; j++ ) {
643                         if ( nvals[ j ].bv_val != &dummy ) {
644                                 ber_memfree( nvals[ j ].bv_val );
645                         }
646                 }
647                 ber_memfree( nvals );
648         }
649
650         return rc;
651 }
652
653 int
654 modify_replace_values(
655         Entry   *e,
656         Modification    *mod,
657         int             permissive,
658         const char      **text,
659         char *textbuf, size_t textlen
660 )
661 {
662         (void) attr_delete( &e->e_attrs, mod->sm_desc );
663
664         if ( mod->sm_bvalues ) {
665                 return modify_add_values( e, mod, permissive, text, textbuf, textlen );
666         }
667
668         return LDAP_SUCCESS;
669 }
670
671 void
672 slap_mod_free(
673         Modification    *mod,
674         int                             freeit
675 )
676 {
677         if ( mod->sm_values != NULL ) ber_bvarray_free( mod->sm_values );
678
679 #ifdef SLAP_NVALUES
680         if ( mod->sm_nvalues != NULL ) ber_bvarray_free( mod->sm_nvalues );
681 #endif
682
683         if( freeit )
684                 free( mod );
685 }
686
687 void
688 slap_mods_free(
689     Modifications       *ml
690 )
691 {
692         Modifications *next;
693
694         for ( ; ml != NULL; ml = next ) {
695                 next = ml->sml_next;
696
697                 slap_mod_free( &ml->sml_mod, 0 );
698                 free( ml );
699         }
700 }
701