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