]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/modify.c
17acfbd73668bb8477c04d23f9588dc5a94278b1
[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                 /* check if modified attribute was indexed */
121                 err = bdb_index_is_indexed( be, mod->sm_desc );
122                 if ( err == LDAP_SUCCESS ) {
123                         ap = attr_find( save_attrs, mod->sm_desc );
124                         if ( ap ) ap->a_flags |= SLAP_ATTR_IXDEL;
125
126                         ap = attr_find( e->e_attrs, mod->sm_desc );
127                         if ( ap ) ap->a_flags |= SLAP_ATTR_IXADD;
128                 }
129         }
130
131         /* check that the entry still obeys the schema */
132         rc = entry_schema_check( be, e, save_attrs, text, textbuf, textlen );
133         if ( rc != LDAP_SUCCESS ) {
134                 attrs_free( e->e_attrs );
135                 e->e_attrs = save_attrs;
136                 Debug( LDAP_DEBUG_ANY, "entry failed schema check: %s\n",
137                         *text, 0, 0 );
138                 return rc;
139         }
140
141         /* update the indices of the modified attributes */
142
143         /* start with deleting the old index entries */
144         for ( ap = save_attrs; ap != NULL; ap = ap->a_next ) {
145                 if ( ap->a_flags & SLAP_ATTR_IXDEL ) {
146                         rc = bdb_index_values( be, tid, ap->a_desc, ap->a_vals,
147                                                e->e_id, SLAP_INDEX_DELETE_OP );
148                         if ( rc != LDAP_SUCCESS ) {
149                                 attrs_free( e->e_attrs );
150                                 e->e_attrs = save_attrs;
151                                 Debug( LDAP_DEBUG_ANY,
152                                        "Attribute index delete failure",
153                                        0, 0, 0 );
154                                 return rc;
155                         }
156                         ap->a_flags &= ~SLAP_ATTR_IXDEL;
157                 }
158         }
159
160         /* add the new index entries */
161         for ( ap = e->e_attrs; ap != NULL; ap = ap->a_next ) {
162                 if (ap->a_flags & SLAP_ATTR_IXADD) {
163                         rc = bdb_index_values( be, tid, ap->a_desc, ap->a_vals,
164                                                e->e_id, SLAP_INDEX_ADD_OP );
165                         if ( rc != LDAP_SUCCESS ) {
166                                 attrs_free( e->e_attrs );
167                                 e->e_attrs = save_attrs;
168                                 Debug( LDAP_DEBUG_ANY,
169                                        "Attribute index add failure",
170                                        0, 0, 0 );
171                                 return rc;
172                         }
173                         ap->a_flags &= ~SLAP_ATTR_IXADD;
174                 }
175         }
176
177         return rc;
178 }
179
180
181 int
182 bdb_modify(
183         BackendDB       *be,
184         Connection      *conn,
185         Operation       *op,
186         struct berval   *dn,
187         struct berval   *ndn,
188         Modifications   *modlist )
189 {
190         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
191         int rc;
192         Entry           *matched;
193         Entry           *e;
194         int             manageDSAit = get_manageDSAit( op );
195         const char *text = NULL;
196         char textbuf[SLAP_TEXT_BUFLEN];
197         size_t textlen = sizeof textbuf;
198         DB_TXN  *ltid = NULL;
199         struct bdb_op_info opinfo;
200
201         Debug( LDAP_DEBUG_ARGS, "bdb_modify: %s\n", dn->bv_val, 0, 0 );
202
203         if( 0 ) {
204 retry:  /* transaction retry */
205                 Debug(LDAP_DEBUG_TRACE,
206                         "bdb_modify: retrying...\n", 0, 0, 0);
207                 rc = txn_abort( ltid );
208                 ltid = NULL;
209                 op->o_private = NULL;
210                 if( rc != 0 ) {
211                         rc = LDAP_OTHER;
212                         text = "internal error";
213                         goto return_results;
214                 }
215                 ldap_pvt_thread_yield();
216         }
217
218         if( bdb->bi_txn ) {
219                 /* begin transaction */
220                 rc = txn_begin( bdb->bi_dbenv, NULL, &ltid, 
221                         bdb->bi_db_opflags );
222                 text = NULL;
223                 if( rc != 0 ) {
224                         Debug( LDAP_DEBUG_TRACE,
225                                 "bdb_modify: txn_begin failed: %s (%d)\n",
226                                 db_strerror(rc), rc, 0 );
227                         rc = LDAP_OTHER;
228                         text = "internal error";
229                         goto return_results;
230                 }
231         }
232
233         opinfo.boi_bdb = be;
234         opinfo.boi_txn = ltid;
235         opinfo.boi_err = 0;
236         op->o_private = &opinfo;
237
238         /* get entry */
239         rc = bdb_dn2entry( be, ltid, ndn, &e, &matched, 0 );
240
241         if ( rc != 0 ) {
242                 Debug( LDAP_DEBUG_TRACE,
243                         "bdb_modify: dn2entry failed (%d)\n",
244                         rc, 0, 0 );
245                 switch( rc ) {
246                 case DB_LOCK_DEADLOCK:
247                 case DB_LOCK_NOTGRANTED:
248                         goto retry;
249                 case DB_NOTFOUND:
250                         break;
251                 default:
252                         rc = LDAP_OTHER;
253                 }
254                 text = "internal error";
255                 goto return_results;
256         }
257
258         /* acquire and lock entry */
259         if ( e == NULL ) {
260                 char* matched_dn = NULL;
261                 BerVarray refs;
262
263                 if ( matched != NULL ) {
264                         matched_dn = ch_strdup( matched->e_dn );
265                         refs = is_entry_referral( matched )
266                                 ? get_entry_referrals( be, conn, op, matched )
267                                 : NULL;
268                         bdb_entry_return( be, matched );
269                         matched = NULL;
270
271                 } else {
272                         refs = referral_rewrite( default_referral,
273                                 NULL, dn, LDAP_SCOPE_DEFAULT );
274                 }
275
276                 send_ldap_result( conn, op, rc = LDAP_REFERRAL,
277                         matched_dn, NULL, refs, NULL );
278
279                 ber_bvarray_free( refs );
280                 free( matched_dn );
281
282                 return rc;
283         }
284
285         if ( !manageDSAit && is_entry_referral( e ) ) {
286                 /* parent is a referral, don't allow add */
287                 /* parent is an alias, don't allow add */
288                 BerVarray refs = get_entry_referrals( be,
289                         conn, op, e );
290
291                 Debug( LDAP_DEBUG_TRACE,
292                         "bdb_modify: entry is referral\n",
293                         0, 0, 0 );
294
295                 send_ldap_result( conn, op, rc = LDAP_REFERRAL,
296                         e->e_dn, NULL, refs, NULL );
297
298                 ber_bvarray_free( refs );
299                 goto done;
300         }
301         
302         /* Modify the entry */
303         rc = bdb_modify_internal( be, conn, op, ltid, modlist, e,
304                 &text, textbuf, textlen );
305
306         if( rc != LDAP_SUCCESS ) {
307                 Debug( LDAP_DEBUG_TRACE,
308                         "bdb_modify: modify failed (%d)\n",
309                         rc, 0, 0 );
310                 switch( rc ) {
311                 case DB_LOCK_DEADLOCK:
312                 case DB_LOCK_NOTGRANTED:
313                         goto retry;
314                 }
315                 goto return_results;
316         }
317
318         /* change the entry itself */
319         rc = bdb_id2entry_update( be, ltid, e );
320         if ( rc != 0 ) {
321                 Debug( LDAP_DEBUG_TRACE,
322                         "bdb_modify: id2entry update failed (%d)\n",
323                         rc, 0, 0 );
324                 switch( rc ) {
325                 case DB_LOCK_DEADLOCK:
326                 case DB_LOCK_NOTGRANTED:
327                         goto retry;
328                 }
329                 text = "entry update failed";
330                 goto return_results;
331         }
332
333         if( bdb->bi_txn ) {
334                 rc = txn_commit( ltid, 0 );
335         }
336         ltid = NULL;
337         op->o_private = NULL;
338
339         if( rc != 0 ) {
340                 Debug( LDAP_DEBUG_TRACE,
341                         "bdb_modify: txn_commit failed: %s (%d)\n",
342                         db_strerror(rc), rc, 0 );
343                 rc = LDAP_OTHER;
344                 text = "commit failed";
345
346         } else {
347                 Debug( LDAP_DEBUG_TRACE,
348                         "bdb_modify: updated id=%08lx dn=\"%s\"\n",
349                         e->e_id, e->e_dn, 0 );
350                 rc = LDAP_SUCCESS;
351                 text = NULL;
352         }
353
354 return_results:
355         send_ldap_result( conn, op, rc,
356                 NULL, text, NULL, NULL );
357
358         if( rc == LDAP_SUCCESS && bdb->bi_txn_cp ) {
359                 ldap_pvt_thread_yield();
360                 TXN_CHECKPOINT( bdb->bi_dbenv,
361                         bdb->bi_txn_cp_kbyte, bdb->bi_txn_cp_min, 0 );
362         }
363
364 done:
365         if( ltid != NULL ) {
366                 txn_abort( ltid );
367                 op->o_private = NULL;
368         }
369
370         if( e != NULL ) {
371                 bdb_entry_return( be, e );
372         }
373         return rc;
374 }
375
376 static int
377 add_values(
378         Entry   *e,
379         Modification    *mod,
380         char    *dn
381 )
382 {
383         int             i, j;
384         Attribute       *a;
385
386         /* char *desc = mod->sm_desc->ad_cname.bv_val; */
387         MatchingRule *mr = mod->sm_desc->ad_type->sat_equality;
388
389         a = attr_find( e->e_attrs, mod->sm_desc );
390
391         /* check if the values we're adding already exist */
392         if( mr == NULL || !mr->smr_match ) {
393                 if ( a != NULL ) {
394                         /* do not allow add of additional attribute
395                                 if no equality rule exists */
396                         return LDAP_INAPPROPRIATE_MATCHING;
397                 }
398
399                 for ( i = 0; mod->sm_bvalues[i].bv_val != NULL; i++ ) {
400                         /* test asserted values against existing values */
401                         if( a ) {
402                                 for( j = 0; a->a_vals[j].bv_val != NULL; j++ ) {
403                                         int rc = ber_bvcmp( &mod->sm_bvalues[i],
404                                                 &a->a_vals[j] );
405
406                                         if( rc == 0 ) {
407                                                 /* value exists already */
408                                                 return LDAP_TYPE_OR_VALUE_EXISTS;
409                                         }
410                                 }
411                         }
412
413                         /* test asserted values against themselves */
414                         for( j = 0; j < i; j++ ) {
415                                 int rc = ber_bvcmp( &mod->sm_bvalues[i],
416                                         &mod->sm_bvalues[j] );
417
418                                 if( rc == 0 ) {
419                                         /* value exists already */
420                                         return LDAP_TYPE_OR_VALUE_EXISTS;
421                                 }
422                         }
423                 }
424
425         } else {
426                 for ( i = 0; mod->sm_bvalues[i].bv_val != NULL; i++ ) {
427                         int rc, match;
428                         const char *text = NULL;
429                         struct berval asserted;
430
431                         rc = value_normalize( mod->sm_desc,
432                                 SLAP_MR_EQUALITY,
433                                 &mod->sm_bvalues[i],
434                                 &asserted,
435                                 &text );
436
437                         if( rc != LDAP_SUCCESS ) return rc;
438
439                         if( a ) {
440                                 for ( j = 0; a->a_vals[j].bv_val != NULL; j++ ) {
441                                         int rc = value_match( &match, mod->sm_desc, mr,
442                                                 SLAP_MR_VALUE_SYNTAX_MATCH,
443                                                 &a->a_vals[j], &asserted, &text );
444
445                                         if( rc == LDAP_SUCCESS && match == 0 ) {
446                                                 free( asserted.bv_val );
447                                                 return LDAP_TYPE_OR_VALUE_EXISTS;
448                                         }
449                                 }
450                         }
451
452                         for ( j = 0; j < i; j++ ) {
453                                 int rc = value_match( &match, mod->sm_desc, mr,
454                                         SLAP_MR_VALUE_SYNTAX_MATCH,
455                                         &mod->sm_bvalues[j], &asserted, &text );
456
457                                 if( rc == LDAP_SUCCESS && match == 0 ) {
458                                         free( asserted.bv_val );
459                                         return LDAP_TYPE_OR_VALUE_EXISTS;
460                                 }
461                         }
462
463                         free( asserted.bv_val );
464                 }
465         }
466
467         /* no - add them */
468         if( attr_merge( e, mod->sm_desc, mod->sm_bvalues ) != 0 ) {
469                 /* this should return result of attr_merge */
470                 return LDAP_OTHER;
471         }
472
473         return LDAP_SUCCESS;
474 }
475
476 static int
477 delete_values(
478         Entry   *e,
479         Modification    *mod,
480         char    *dn
481 )
482 {
483         int             i, j, k, found;
484         Attribute       *a;
485         char *desc = mod->sm_desc->ad_cname.bv_val;
486         MatchingRule *mr = mod->sm_desc->ad_type->sat_equality;
487
488         /* delete the entire attribute */
489         if ( mod->sm_bvalues == NULL ) {
490                 Debug( LDAP_DEBUG_TRACE,
491                         "bdb_modify_delete: removing entire attribute %s\n",
492                         desc, 0, 0 );
493                 return attr_delete( &e->e_attrs, mod->sm_desc )
494                         ? LDAP_NO_SUCH_ATTRIBUTE : LDAP_SUCCESS;
495         }
496
497         if( mr == NULL || !mr->smr_match ) {
498                 /* disallow specific attributes from being deleted if
499                         no equality rule */
500                 return LDAP_INAPPROPRIATE_MATCHING;
501         }
502
503         /* delete specific values - find the attribute first */
504         if ( (a = attr_find( e->e_attrs, mod->sm_desc )) == NULL ) {
505                 Debug( LDAP_DEBUG_TRACE,
506                         "bdb_modify_delete: could not find attribute %s\n",
507                         desc, 0, 0 );
508                 return LDAP_NO_SUCH_ATTRIBUTE;
509         }
510
511         /* find each value to delete */
512         for ( i = 0; mod->sm_bvalues[i].bv_val != NULL; i++ ) {
513                 int rc;
514                 const char *text = NULL;
515
516                 struct berval asserted;
517
518                 rc = value_normalize( mod->sm_desc,
519                         SLAP_MR_EQUALITY,
520                         &mod->sm_bvalues[i],
521                         &asserted,
522                         &text );
523
524                 if( rc != LDAP_SUCCESS ) return rc;
525
526                 found = 0;
527                 for ( j = 0; a->a_vals[j].bv_val != NULL; j++ ) {
528                         int match;
529                         int rc = value_match( &match, mod->sm_desc, mr,
530                                 SLAP_MR_VALUE_SYNTAX_MATCH,
531                                 &a->a_vals[j], &asserted, &text );
532
533                         if( rc == LDAP_SUCCESS && match != 0 ) {
534                                 continue;
535                         }
536
537                         /* found a matching value */
538                         found = 1;
539
540                         /* delete it */
541                         free( a->a_vals[j].bv_val );
542                         for ( k = j + 1; a->a_vals[k].bv_val != NULL; k++ ) {
543                                 a->a_vals[k - 1] = a->a_vals[k];
544                         }
545                         a->a_vals[k - 1].bv_val = NULL;
546                         a->a_vals[k - 1].bv_len = 0;
547
548                         break;
549                 }
550
551                 free( asserted.bv_val );
552
553                 /* looked through them all w/o finding it */
554                 if ( ! found ) {
555                         Debug( LDAP_DEBUG_TRACE,
556                                 "bdb_modify_delete: could not find value for attr %s\n",
557                                 desc, 0, 0 );
558                         return LDAP_NO_SUCH_ATTRIBUTE;
559                 }
560         }
561
562         /* if no values remain, delete the entire attribute */
563         if ( a->a_vals[0].bv_val == NULL ) {
564                 Debug( LDAP_DEBUG_TRACE,
565                         "bdb_modify_delete: removing entire attribute %s\n",
566                         desc, 0, 0 );
567                 if ( attr_delete( &e->e_attrs, mod->sm_desc ) ) {
568                         return LDAP_NO_SUCH_ATTRIBUTE;
569                 }
570         }
571
572         return LDAP_SUCCESS;
573 }
574
575 static int
576 replace_values(
577         Entry   *e,
578         Modification    *mod,
579         char    *dn
580 )
581 {
582         int rc = attr_delete( &e->e_attrs, mod->sm_desc );
583
584         if( rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_ATTRIBUTE ) {
585                 return rc;
586         }
587         rc = LDAP_SUCCESS;
588
589         if ( mod->sm_bvalues ) {
590                 rc = add_values( e, mod, dn );
591         }
592
593         return LDAP_SUCCESS;
594 }