]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/modify.c
Modify ad_cmp() macro to support use as an ordering function.
[openldap] / servers / slapd / back-bdb / modify.c
1 /* modify.c - bdb backend modify routine */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 #include "portable.h"
9
10 #include <stdio.h>
11 #include <ac/string.h>
12 #include <ac/time.h>
13
14 #include "back-bdb.h"
15 #include "external.h"
16
17 static int add_values( Entry *e, Modification *mod, char *dn );
18 static int delete_values( Entry *e, Modification *mod, char *dn );
19 static int replace_values( Entry *e, Modification *mod, char *dn );
20
21 int bdb_modify_internal(
22         BackendDB *be,
23         Connection *conn,
24         Operation *op,
25         DB_TXN *tid,
26         Modifications *modlist,
27         Entry *e,
28         const char **text,
29         char *textbuf,
30         size_t textlen )
31 {
32         int rc, err;
33         Modification    *mod;
34         Modifications   *ml;
35         Attribute       *save_attrs;
36         Attribute       *ap;
37
38         Debug( LDAP_DEBUG_TRACE, "bdb_modify_internal: 0x%08lx: %s\n",
39                 e->e_id, e->e_dn, 0);
40
41         if ( !acl_check_modlist( be, conn, op, e, modlist )) {
42                 return LDAP_INSUFFICIENT_ACCESS;
43         }
44
45         save_attrs = e->e_attrs;
46         e->e_attrs = attrs_dup( e->e_attrs );
47
48         for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
49                 mod = &ml->sml_mod;
50
51                 switch ( mod->sm_op ) {
52                 case LDAP_MOD_ADD:
53                         Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: add\n", 0, 0, 0);
54                         err = add_values( e, mod, op->o_ndn.bv_val );
55
56                         if( err != LDAP_SUCCESS ) {
57                                 *text = "modify: add values failed";
58                                 Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
59                                         err, *text, 0);
60                         }
61                         break;
62
63                 case LDAP_MOD_DELETE:
64                         Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: delete\n", 0, 0, 0);
65                         err = delete_values( e, mod, op->o_ndn.bv_val );
66                         assert( err != LDAP_TYPE_OR_VALUE_EXISTS );
67                         if( err != LDAP_SUCCESS ) {
68                                 *text = "modify: delete values failed";
69                                 Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
70                                         err, *text, 0);
71                         }
72                         break;
73
74                 case LDAP_MOD_REPLACE:
75                         Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: replace\n", 0, 0, 0);
76                         err = replace_values( e, mod, op->o_ndn.bv_val );
77                         assert( err != LDAP_TYPE_OR_VALUE_EXISTS );
78                         if( err != LDAP_SUCCESS ) {
79                                 *text = "modify: replace values failed";
80                                 Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
81                                         err, *text, 0);
82                         }
83                         break;
84
85                 case SLAP_MOD_SOFTADD:
86                         Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: softadd\n", 0, 0, 0);
87                         /* Avoid problems in index_add_mods()
88                          * We need to add index if necessary.
89                          */
90                         mod->sm_op = LDAP_MOD_ADD;
91                         err = add_values( e, mod, op->o_ndn.bv_val );
92
93                         if ( err == LDAP_TYPE_OR_VALUE_EXISTS ) {
94                                 err = LDAP_SUCCESS;
95                         }
96
97                         if( err != LDAP_SUCCESS ) {
98                                 *text = "modify: (soft)add values failed";
99                                 Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
100                                         err, *text, 0);
101                         }
102                         break;
103
104                 default:
105                         Debug(LDAP_DEBUG_ANY, "bdb_modify_internal: invalid op %d\n",
106                                 mod->sm_op, 0, 0);
107                         *text = "Invalid modify operation";
108                         err = LDAP_OTHER;
109                         Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
110                                 err, *text, 0);
111                 }
112
113                 if ( err != LDAP_SUCCESS ) {
114                         attrs_free( e->e_attrs );
115                         e->e_attrs = save_attrs;
116                         /* unlock entry, delete from cache */
117                         return err; 
118                 }
119
120                 /* If objectClass was modified, reset the flags */
121                 if ( mod->sm_desc == slap_schema.si_ad_objectClass ) {
122                         e->e_ocflags = 0;
123                 }
124
125                 /* check if modified attribute was indexed */
126                 err = bdb_index_is_indexed( be, mod->sm_desc );
127                 if ( err == LDAP_SUCCESS ) {
128                         ap = attr_find( save_attrs, mod->sm_desc );
129                         if ( ap ) ap->a_flags |= SLAP_ATTR_IXDEL;
130
131                         ap = attr_find( e->e_attrs, mod->sm_desc );
132                         if ( ap ) ap->a_flags |= SLAP_ATTR_IXADD;
133                 }
134         }
135
136         /* check that the entry still obeys the schema */
137         rc = entry_schema_check( be, e, save_attrs, text, textbuf, textlen );
138         if ( rc != LDAP_SUCCESS ) {
139                 attrs_free( e->e_attrs );
140                 e->e_attrs = save_attrs;
141                 Debug( LDAP_DEBUG_ANY, "entry failed schema check: %s\n",
142                         *text, 0, 0 );
143                 return rc;
144         }
145
146         /* update the indices of the modified attributes */
147
148         /* start with deleting the old index entries */
149         for ( ap = save_attrs; ap != NULL; ap = ap->a_next ) {
150                 if ( ap->a_flags & SLAP_ATTR_IXDEL ) {
151                         rc = bdb_index_values( be, tid, ap->a_desc, ap->a_vals,
152                                                e->e_id, SLAP_INDEX_DELETE_OP );
153                         if ( rc != LDAP_SUCCESS ) {
154                                 attrs_free( e->e_attrs );
155                                 e->e_attrs = save_attrs;
156                                 Debug( LDAP_DEBUG_ANY,
157                                        "Attribute index delete failure",
158                                        0, 0, 0 );
159                                 return rc;
160                         }
161                         ap->a_flags &= ~SLAP_ATTR_IXDEL;
162                 }
163         }
164
165         /* add the new index entries */
166         for ( ap = e->e_attrs; ap != NULL; ap = ap->a_next ) {
167                 if (ap->a_flags & SLAP_ATTR_IXADD) {
168                         rc = bdb_index_values( be, tid, ap->a_desc, ap->a_vals,
169                                                e->e_id, SLAP_INDEX_ADD_OP );
170                         if ( rc != LDAP_SUCCESS ) {
171                                 attrs_free( e->e_attrs );
172                                 e->e_attrs = save_attrs;
173                                 Debug( LDAP_DEBUG_ANY,
174                                        "Attribute index add failure",
175                                        0, 0, 0 );
176                                 return rc;
177                         }
178                         ap->a_flags &= ~SLAP_ATTR_IXADD;
179                 }
180         }
181
182         return rc;
183 }
184
185
186 int
187 bdb_modify(
188         BackendDB       *be,
189         Connection      *conn,
190         Operation       *op,
191         struct berval   *dn,
192         struct berval   *ndn,
193         Modifications   *modlist )
194 {
195         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
196         int rc;
197         Entry           *matched;
198         Entry           *e;
199         int             manageDSAit = get_manageDSAit( op );
200         const char *text = NULL;
201         char textbuf[SLAP_TEXT_BUFLEN];
202         size_t textlen = sizeof textbuf;
203         DB_TXN  *ltid = NULL;
204         struct bdb_op_info opinfo;
205
206         Debug( LDAP_DEBUG_ARGS, "bdb_modify: %s\n", dn->bv_val, 0, 0 );
207
208         if( 0 ) {
209 retry:  /* transaction retry */
210                 Debug(LDAP_DEBUG_TRACE,
211                         "bdb_modify: retrying...\n", 0, 0, 0);
212                 rc = txn_abort( ltid );
213                 ltid = NULL;
214                 op->o_private = NULL;
215                 if( rc != 0 ) {
216                         rc = LDAP_OTHER;
217                         text = "internal error";
218                         goto return_results;
219                 }
220                 ldap_pvt_thread_yield();
221         }
222
223         if( bdb->bi_txn ) {
224                 /* begin transaction */
225                 rc = txn_begin( bdb->bi_dbenv, NULL, &ltid, 
226                         bdb->bi_db_opflags );
227                 text = NULL;
228                 if( rc != 0 ) {
229                         Debug( LDAP_DEBUG_TRACE,
230                                 "bdb_modify: txn_begin failed: %s (%d)\n",
231                                 db_strerror(rc), rc, 0 );
232                         rc = LDAP_OTHER;
233                         text = "internal error";
234                         goto return_results;
235                 }
236         }
237
238         opinfo.boi_bdb = be;
239         opinfo.boi_txn = ltid;
240         opinfo.boi_err = 0;
241         op->o_private = &opinfo;
242
243         /* get entry */
244         rc = bdb_dn2entry( be, ltid, ndn, &e, &matched, 0 );
245
246         if ( rc != 0 ) {
247                 Debug( LDAP_DEBUG_TRACE,
248                         "bdb_modify: dn2entry failed (%d)\n",
249                         rc, 0, 0 );
250                 switch( rc ) {
251                 case DB_LOCK_DEADLOCK:
252                 case DB_LOCK_NOTGRANTED:
253                         goto retry;
254                 case DB_NOTFOUND:
255                         break;
256                 default:
257                         rc = LDAP_OTHER;
258                 }
259                 text = "internal error";
260                 goto return_results;
261         }
262
263         /* acquire and lock entry */
264         if ( e == NULL ) {
265                 char* matched_dn = NULL;
266                 BerVarray refs;
267
268                 if ( matched != NULL ) {
269                         matched_dn = ch_strdup( matched->e_dn );
270                         refs = is_entry_referral( matched )
271                                 ? get_entry_referrals( be, conn, op, matched )
272                                 : NULL;
273                         bdb_entry_return( be, matched );
274                         matched = NULL;
275
276                 } else {
277                         refs = referral_rewrite( default_referral,
278                                 NULL, dn, LDAP_SCOPE_DEFAULT );
279                 }
280
281                 send_ldap_result( conn, op, rc = LDAP_REFERRAL,
282                         matched_dn, NULL, refs, NULL );
283
284                 ber_bvarray_free( refs );
285                 free( matched_dn );
286
287                 return rc;
288         }
289
290         if ( !manageDSAit && is_entry_referral( e ) ) {
291                 /* parent is a referral, don't allow add */
292                 /* parent is an alias, don't allow add */
293                 BerVarray refs = get_entry_referrals( be,
294                         conn, op, e );
295
296                 Debug( LDAP_DEBUG_TRACE,
297                         "bdb_modify: entry is referral\n",
298                         0, 0, 0 );
299
300                 send_ldap_result( conn, op, rc = LDAP_REFERRAL,
301                         e->e_dn, NULL, refs, NULL );
302
303                 ber_bvarray_free( refs );
304                 goto done;
305         }
306         
307         /* Modify the entry */
308         rc = bdb_modify_internal( be, conn, op, ltid, modlist, e,
309                 &text, textbuf, textlen );
310
311         if( rc != LDAP_SUCCESS ) {
312                 Debug( LDAP_DEBUG_TRACE,
313                         "bdb_modify: modify failed (%d)\n",
314                         rc, 0, 0 );
315                 switch( rc ) {
316                 case DB_LOCK_DEADLOCK:
317                 case DB_LOCK_NOTGRANTED:
318                         goto retry;
319                 }
320                 goto return_results;
321         }
322
323         /* change the entry itself */
324         rc = bdb_id2entry_update( be, ltid, e );
325         if ( rc != 0 ) {
326                 Debug( LDAP_DEBUG_TRACE,
327                         "bdb_modify: id2entry update failed (%d)\n",
328                         rc, 0, 0 );
329                 switch( rc ) {
330                 case DB_LOCK_DEADLOCK:
331                 case DB_LOCK_NOTGRANTED:
332                         goto retry;
333                 }
334                 text = "entry update failed";
335                 goto return_results;
336         }
337
338         if( bdb->bi_txn ) {
339                 rc = txn_commit( ltid, 0 );
340         }
341         ltid = NULL;
342         op->o_private = NULL;
343
344         if( rc != 0 ) {
345                 Debug( LDAP_DEBUG_TRACE,
346                         "bdb_modify: txn_commit failed: %s (%d)\n",
347                         db_strerror(rc), rc, 0 );
348                 rc = LDAP_OTHER;
349                 text = "commit failed";
350
351         } else {
352                 Debug( LDAP_DEBUG_TRACE,
353                         "bdb_modify: updated id=%08lx dn=\"%s\"\n",
354                         e->e_id, e->e_dn, 0 );
355                 rc = LDAP_SUCCESS;
356                 text = NULL;
357         }
358
359 return_results:
360         send_ldap_result( conn, op, rc,
361                 NULL, text, NULL, NULL );
362
363         if( rc == LDAP_SUCCESS && bdb->bi_txn_cp ) {
364                 ldap_pvt_thread_yield();
365                 TXN_CHECKPOINT( bdb->bi_dbenv,
366                         bdb->bi_txn_cp_kbyte, bdb->bi_txn_cp_min, 0 );
367         }
368
369 done:
370         if( ltid != NULL ) {
371                 txn_abort( ltid );
372                 op->o_private = NULL;
373         }
374
375         if( e != NULL ) {
376                 bdb_entry_return( be, e );
377         }
378         return rc;
379 }
380
381 static int
382 add_values(
383         Entry   *e,
384         Modification    *mod,
385         char    *dn
386 )
387 {
388         int             i, j;
389         Attribute       *a;
390
391         /* char *desc = mod->sm_desc->ad_cname.bv_val; */
392         MatchingRule *mr = mod->sm_desc->ad_type->sat_equality;
393
394         a = attr_find( e->e_attrs, mod->sm_desc );
395
396         /* check if the values we're adding already exist */
397         if( mr == NULL || !mr->smr_match ) {
398                 if ( a != NULL ) {
399                         /* do not allow add of additional attribute
400                                 if no equality rule exists */
401                         return LDAP_INAPPROPRIATE_MATCHING;
402                 }
403
404                 for ( i = 0; mod->sm_bvalues[i].bv_val != NULL; i++ ) {
405                         /* test asserted values against existing values */
406                         if( a ) {
407                                 for( j = 0; a->a_vals[j].bv_val != NULL; j++ ) {
408                                         int rc = ber_bvcmp( &mod->sm_bvalues[i],
409                                                 &a->a_vals[j] );
410
411                                         if( rc == 0 ) {
412                                                 /* value exists already */
413                                                 return LDAP_TYPE_OR_VALUE_EXISTS;
414                                         }
415                                 }
416                         }
417
418                         /* test asserted values against themselves */
419                         for( j = 0; j < i; j++ ) {
420                                 int rc = ber_bvcmp( &mod->sm_bvalues[i],
421                                         &mod->sm_bvalues[j] );
422
423                                 if( rc == 0 ) {
424                                         /* value exists already */
425                                         return LDAP_TYPE_OR_VALUE_EXISTS;
426                                 }
427                         }
428                 }
429
430         } else {
431                 for ( i = 0; mod->sm_bvalues[i].bv_val != NULL; i++ ) {
432                         int rc, match;
433                         const char *text = NULL;
434                         struct berval asserted;
435
436                         rc = value_normalize( mod->sm_desc,
437                                 SLAP_MR_EQUALITY,
438                                 &mod->sm_bvalues[i],
439                                 &asserted,
440                                 &text );
441
442                         if( rc != LDAP_SUCCESS ) return rc;
443
444                         if( a ) {
445                                 for ( j = 0; a->a_vals[j].bv_val != NULL; j++ ) {
446                                         int rc = value_match( &match, mod->sm_desc, mr,
447                                                 SLAP_MR_VALUE_SYNTAX_MATCH,
448                                                 &a->a_vals[j], &asserted, &text );
449
450                                         if( rc == LDAP_SUCCESS && match == 0 ) {
451                                                 free( asserted.bv_val );
452                                                 return LDAP_TYPE_OR_VALUE_EXISTS;
453                                         }
454                                 }
455                         }
456
457                         for ( j = 0; j < i; j++ ) {
458                                 int rc = value_match( &match, mod->sm_desc, mr,
459                                         SLAP_MR_VALUE_SYNTAX_MATCH,
460                                         &mod->sm_bvalues[j], &asserted, &text );
461
462                                 if( rc == LDAP_SUCCESS && match == 0 ) {
463                                         free( asserted.bv_val );
464                                         return LDAP_TYPE_OR_VALUE_EXISTS;
465                                 }
466                         }
467
468                         free( asserted.bv_val );
469                 }
470         }
471
472         /* no - add them */
473         if( attr_merge( e, mod->sm_desc, mod->sm_bvalues ) != 0 ) {
474                 /* this should return result of attr_merge */
475                 return LDAP_OTHER;
476         }
477
478         return LDAP_SUCCESS;
479 }
480
481 static int
482 delete_values(
483         Entry   *e,
484         Modification    *mod,
485         char    *dn
486 )
487 {
488         int             i, j, k, found;
489         Attribute       *a;
490         char *desc = mod->sm_desc->ad_cname.bv_val;
491         MatchingRule *mr = mod->sm_desc->ad_type->sat_equality;
492
493         /* delete the entire attribute */
494         if ( mod->sm_bvalues == NULL ) {
495                 Debug( LDAP_DEBUG_TRACE,
496                         "bdb_modify_delete: removing entire attribute %s\n",
497                         desc, 0, 0 );
498                 return attr_delete( &e->e_attrs, mod->sm_desc )
499                         ? LDAP_NO_SUCH_ATTRIBUTE : LDAP_SUCCESS;
500         }
501
502         if( mr == NULL || !mr->smr_match ) {
503                 /* disallow specific attributes from being deleted if
504                         no equality rule */
505                 return LDAP_INAPPROPRIATE_MATCHING;
506         }
507
508         /* delete specific values - find the attribute first */
509         if ( (a = attr_find( e->e_attrs, mod->sm_desc )) == NULL ) {
510                 Debug( LDAP_DEBUG_TRACE,
511                         "bdb_modify_delete: could not find attribute %s\n",
512                         desc, 0, 0 );
513                 return LDAP_NO_SUCH_ATTRIBUTE;
514         }
515
516         /* find each value to delete */
517         for ( i = 0; mod->sm_bvalues[i].bv_val != NULL; i++ ) {
518                 int rc;
519                 const char *text = NULL;
520
521                 struct berval asserted;
522
523                 rc = value_normalize( mod->sm_desc,
524                         SLAP_MR_EQUALITY,
525                         &mod->sm_bvalues[i],
526                         &asserted,
527                         &text );
528
529                 if( rc != LDAP_SUCCESS ) return rc;
530
531                 found = 0;
532                 for ( j = 0; a->a_vals[j].bv_val != NULL; j++ ) {
533                         int match;
534                         int rc = value_match( &match, mod->sm_desc, mr,
535                                 SLAP_MR_VALUE_SYNTAX_MATCH,
536                                 &a->a_vals[j], &asserted, &text );
537
538                         if( rc == LDAP_SUCCESS && match != 0 ) {
539                                 continue;
540                         }
541
542                         /* found a matching value */
543                         found = 1;
544
545                         /* delete it */
546                         free( a->a_vals[j].bv_val );
547                         for ( k = j + 1; a->a_vals[k].bv_val != NULL; k++ ) {
548                                 a->a_vals[k - 1] = a->a_vals[k];
549                         }
550                         a->a_vals[k - 1].bv_val = NULL;
551                         a->a_vals[k - 1].bv_len = 0;
552
553                         break;
554                 }
555
556                 free( asserted.bv_val );
557
558                 /* looked through them all w/o finding it */
559                 if ( ! found ) {
560                         Debug( LDAP_DEBUG_TRACE,
561                                 "bdb_modify_delete: could not find value for attr %s\n",
562                                 desc, 0, 0 );
563                         return LDAP_NO_SUCH_ATTRIBUTE;
564                 }
565         }
566
567         /* if no values remain, delete the entire attribute */
568         if ( a->a_vals[0].bv_val == NULL ) {
569                 Debug( LDAP_DEBUG_TRACE,
570                         "bdb_modify_delete: removing entire attribute %s\n",
571                         desc, 0, 0 );
572                 if ( attr_delete( &e->e_attrs, mod->sm_desc ) ) {
573                         return LDAP_NO_SUCH_ATTRIBUTE;
574                 }
575         }
576
577         return LDAP_SUCCESS;
578 }
579
580 static int
581 replace_values(
582         Entry   *e,
583         Modification    *mod,
584         char    *dn
585 )
586 {
587         (void) attr_delete( &e->e_attrs, mod->sm_desc );
588
589         if ( mod->sm_bvalues ) {
590                 return add_values( e, mod, dn );
591         }
592
593         return LDAP_SUCCESS;
594 }